Search code examples
databaseflutterdartmobilehive

Flutter Hive not work right, I'm using the Hive to store data on the device, but when I close and restart the app I lose everything


I'm working on a flutter app, I'll start by saying that I'm a beginner and I'm at the beginning of mobile development, I'm using the Hive to store data on the device, while debugging I saw that when I add data, the hive is updated correctly, but when I close and restart the app I lose everything, below I will put the code of the main class that opens the box and of another class that uses the hive to store the data, the other classes of the project do not touch the hive , can someone help me?

main class:

import 'package:flutter/material.dart';
import 'package:todo/pages/home_page.dart';
import 'package:hive_flutter/hive_flutter.dart';

void main() async {
  //init the hive
  await Hive.initFlutter();

  //open a box
  var box = await Hive.openBox('myBox');

  //run the app
  runApp(const MyApp());
}

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomePage(),
      theme: ThemeData(primarySwatch: Colors.purple),
    );
  }
}

HomePage class:

import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:todo/pages/topic_page.dart';
import 'package:todo/util/dialog_box.dart';
import 'package:todo/util/topic_tile.dart';

class Topic {
  String topicName;
  List todo = [
    ["Add your first To-Do", false]
  ];

  Topic(this.topicName);

  void addTodo(List todoList) {
    todo.add(todoList);
  }

  void removeTodo(int index) {
    todo.removeAt(index);
  }

  String getName() {
    return topicName;
  }

  List getTodo() {
    return todo;
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  //reference the box
  final _myBox = Hive.box('mybox');

  //text controller
  final _controller = TextEditingController();

  //Database
  List topic = [];

  @override
  void initState() {
    if (_myBox.get("TOPIC") != null) {
      topic = _myBox.get("TOPIC");
    }
    super.initState();
  }

  //update the topic
  void updateTopic() {
    _myBox.put("TOPIC", topic);
  }

  //save new task
  void saveNewTopic() {
    setState(() {
      topic.add(Topic(_controller.text));
      _controller.clear();
    });
    Navigator.of(context).pop();
  }

  //new topic function
  void createNewTopic() {
    showDialog(
        context: context,
        builder: (context) {
          return DialogBox(
            text: "Add a new topic",
            controller: _controller,
            onSave: saveNewTopic,
            onCancel: () => Navigator.of(context).pop(),
          );
        });
    updateTopic();
  }

  //delete task
  void deleteTopic(int index) {
    setState(() {
      topic.removeAt(index);
    });
    updateTopic();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Color.fromARGB(255, 255, 190, 93),
        appBar: AppBar(
            title: const Align(
                alignment: Alignment.center,
                child: Text(
                  "Topic",
                  style: TextStyle(fontSize: 32, color: Colors.white),
                ))),
        floatingActionButton: FloatingActionButton(
          onPressed: createNewTopic,
          child: Icon(Icons.add),
        ),
        body: ListView.builder(
            itemCount: topic.length,
            itemBuilder: (context, index) {
              return GestureDetector(
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => TopicPage(topic: topic[index]),
                    ),
                  );
                },
                child: TopicTile(
                  topicName: topic[index].getName(),
                  deleteFunction: (context) => deleteTopic(index),
                ),
              );
            }));
  }
}

Solution

  • As I was running an example project with this part of your code, to recreate the bug, when I tapped on the floating action button to add the second item, I got this error:

    E/flutter ( 1691): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: HiveError: Cannot write, unknown type: Topic. Did you forget to register an adapter?
    E/flutter ( 1691): #0      BinaryWriterImpl.write (package:hive/src/binary/binary_writer_impl.dart:338:9)
    E/flutter ( 1691): #1      BinaryWriterImpl.writeList (package:hive/src/binary/binary_writer_impl.dart:223:7)
    E/flutter ( 1691): #2      BinaryWriterImpl._writeList (package:hive/src/binary/binary_writer_impl.dart:390:7)
    E/flutter ( 1691): #3      BinaryWriterImpl.write (package:hive/src/binary/binary_writer_impl.dart:329:7)
    E/flutter ( 1691): #4      BinaryWriterImpl.writeFrame (package:hive/src/binary/binary_writer_impl.dart:282:9)
    E/flutter ( 1691): #5      StorageBackendVm.writeFrames.<anonymous closure> (package:hive/src/backend/vm/storage_backend_vm.dart:128:31)
    E/flutter ( 1691): #6      ReadWriteSync.syncWrite.<anonymous closure> (package:hive/src/backend/vm/read_write_sync.dart:26:41)
    E/flutter ( 1691): <asynchronous suspension>
    E/flutter ( 1691): #7      BoxImpl._writeFrames (package:hive/src/box/box_impl.dart:88:7)
    E/flutter ( 1691): <asynchronous suspension>
    E/flutter ( 1691): 
    

    Looks like you have forgotten to register the adapter for your custom object. Because hive needs you to register an adapter for any custom object that is put into it. An example can be found here: Hive Objects. We can move the Topic class to a new file and update the code to:

    import 'package:hive_flutter/hive_flutter.dart';
    
    part 'topic.g.dart';
    
    @HiveType(typeId: 1)  //          <-- this is a hive object
    class Topic extends HiveObject {
      @HiveField(0)       //          <-- hive field
      String topicName;
    
      @HiveField(1)       //          <-- hive field
      List todo = [
        ["Add your first To-Do", false]
      ];
    
      Topic(this.topicName);
    
      void addTodo(List todoList) {
        todo.add(todoList);
      }
    
      void removeTodo(int index) {
        todo.removeAt(index);
      }
    
      String getName() {
        return topicName;
      }
    
      List getTodo() {
        return todo;
      }
    }
    

    Then we will get an error on this line:

    part 'topic.g.dart';
    

    Because we should generate the this file (which contains the adapter). so we execute this command in our project folder:

    dart run build_runner build
    

    Now we can see the error is gone. After generating the adapter, we add it in the main.dart file like so:

    void main() async {
      //init the hive
      await Hive.initFlutter();
    
      // register the adapter for the Topic hive object
      Hive.registerAdapter(TopicAdapter());          // <-- this line right here
    
      //open a box
      await Hive.openBox('myBox');
    
      runApp(const MyApp());
    }
    

    Finally we can re-run the app and see that it works as expected.

    Full tutorial for storing custom objects can be found here.