flutterflutter-listviewflutter-hive

How can I manage the state of ListView using Hive after kill task?


I am trying to develop a bookmark app which you can add and delete items. I am using Hive to manage state of the items on ListView, but it doesn't work as I expected. My expectation is that items which are added on ListView are still on the view after kill task. Also, another expectation is that if I delete the default items (Google and Apple) and kill task, the items are deleted. How can I implement these using Hive?

late Box box;
const bookmarkBox = 'bookmark_box';

Future<void> main() async {

  await Hive.initFlutter();

  box = await Hive.openBox(bookmarkBox);
  runApp(const BookmarkApp());
}

class BookmarkApp extends StatelessWidget {
  const BookmarkApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Bookmark App',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.white),
        useMaterial3: false,
      ),
      home:  BookmarkListPage(text: '', url: '',),
    );
  }
}

class BookmarkListPage extends StatefulWidget {
  BookmarkListPage({super.key, title, required this.text, required this.url});

  String text;
  String url;

  final String title = 'Bookmark';

  @override
  State<BookmarkListPage> createState() => _BookmarkListPageState();
}

class _BookmarkListPageState extends State<BookmarkListPage> {

// these are default value but you are able to delete them
  List<String> bookmarkList = ['Google', 'Apple'];
  List<String> bookmarkUrl = [
    'https://www.google.com/',
    'https://www.apple.com/jp/',
  ];

  _launchUrl(int index) async {
    var url = Uri.parse(bookmarkUrl[index]);
    if (await canLaunchUrl(url)) {
      await launchUrl(url);
    } else {
      throw "Could not open $url";
    }
  }

  @override
  Widget build(BuildContext context) {

    var box = Hive.box(bookmarkBox);

    box.get('BookmarkTitle', defaultValue: 'Not Title');
    box.get('BookmarkUrl', defaultValue: 'No URL');

    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        backgroundColor: Colors.white,
        title: Text(
          widget.title,
          style: TextStyle(fontWeight: FontWeight.bold, color: Colors.black),
        ),
        bottom: PreferredSize(
          child: Container(
            height: 1,
            color: Colors.black,
          ),
          preferredSize: Size.fromHeight(1),
        ),
      ),
      body: ListView.builder(
        itemCount: bookmarkList.length,
        itemBuilder: (context, index) {
          // you can swipe to delete items
          return Dismissible(
            background: Container(
              color: Colors.red,
              child: Icon(
                Icons.delete,
                color: Colors.white,
              ),
            ),
            onDismissed: (direction) {
              setState(() {
                bookmarkList.removeAt(index);
              });
            },
            key: UniqueKey(),
            child: Card(
              child: ListTile(
                title: Text(
                  bookmarkList[index],
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 20,
                  ),
                ),
                subtitle: Text(bookmarkUrl[index]),
                onTap:() => _launchUrl(index),
              ),
            ),
          );
        },
      ),

     
      floatingActionButton: FloatingActionButton(
        shape: const CircleBorder(),
        backgroundColor: Colors.blue,
        onPressed: () async {
          final (newBookmarkTitle, newBookmarkUrl) = await showDialog(
              context: context,
              builder: (_) {
                return BookmarkDialog();
              });

          setState(() {
            bookmarkList.add(newBookmarkTitle);
            bookmarkUrl.add(newBookmarkUrl);
            box.put('BookmarkTitle', bookmarkList);
            box.put('BookmarkUrl', bookmarkUrl);
          });

        },
        child: const Icon(
          Icons.add,
          color: Colors.white,
        ),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

Solution

  • from what I'm seeing your not using the box correctly

    bookmarkList and bookmarkUrl should be always be the same as the contents of the box. meaning each something happens to those lists it should reflect on the box

    so:

    1. when instantiating them:

      List<dynamic> bookmarkList = box.get('BookmarkTitle', defaultValue: ['Google', 'Apple']);
      List<dynamic> bookmarkUrl = box.get('BookmarkUrl', defaultValue: ['https://www.google.com/', 'https://www.apple.com/jp/']);
      
    2. when deleting an item:

          onDismissed: (direction) {
             setState(() {
               bookmarkList.removeAt(index);
               bookmarkUrl.removeAt(index);
               box.put('BookmarkTitle', bookmarkList);
               box.put('BookmarkUrl', bookmarkUrl);
             });
           },
      
    3. when adding a new item, which you did correctly:

         setState(() {
           bookmarkList.add(newBookmarkTitle);
           bookmarkUrl.add(newBookmarkUrl);
           box.put('BookmarkTitle', bookmarkList);
           box.put('BookmarkUrl', bookmarkUrl);
         });