HiveError: The box "user" is already open and of type Box<User>

4,066

Solution 1

  1. You can open your user Box in the main method of your app:
Future<void> main() async {
  ...

  final appDocumentDirectory = await path_provider.getApplicationDocumentsDirectory();
  Hive.init(appDocumentDirectory.path);
  Hive.registerAdapter(UserAdapter());
  // open the user box
  await Hive.openBox('user');

  _setUpLogging();

  runApp(MultiProvider(providers: providers, child: StartupApplication()));
}
  1. Access the previously opened box like below:
class MyHomePage extends StatefulWidget {
  const MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // user box
  Box userBox;

  @override
  void initState() {
    super.initState();
    // get the previously opened user box
    userBox = Hive.box('user');
  }

  @override
  Widget build(BuildContext context) {
    // check for your conditions 
    return (userBox.values.isNotEmpty && userBox.get(0).active == 1)
        ? HomeView()
        : Intro();
  }
}

Solution 2

I've given Hive the first try and observing this.

If you open the box like this:

await Hive.openBox("MyModelBox");

It'll throw the exception:

Box not found. Did you forget to call Hive.openBox()?

Let's take a look in Box:

BoxBase<E> _getBoxInternal<E>(String name, [bool? lazy]) {
    var lowerCaseName = name.toLowerCase();
    var box = _boxes[lowerCaseName];
    if (box != null) {
      if ((lazy == null || box.lazy == lazy) && box.valueType == E) {
        return box as BoxBase<E>;
      } else {
        var typeName = box is LazyBox
            ? 'LazyBox<${box.valueType}>'
            : 'Box<${box.valueType}>';
        throw HiveError('The box "$lowerCaseName" is already open '
            'and of type $typeName.');
      }
    } else {
      throw HiveError('Box not found. Did you forget to call Hive.openBox()?');
    }
  }

The box.valueType would be dynamic, the E would be your class model type MyModel. Thus they won't be equal.

But if you passing the particular type of model

await Hive.openBox<MyModel>("MyModelBox");

The error vanishes.

Solution 3

This happens because you have already defined another box with type User somewhere with the same name (using

final myBoxName = 'userBox';
Hive.openBox<User>(myBoxName);

and trying to open the box with the same name with another type e.g.

Hive.openBox<bool>(myBoxName);

So ofcourse flutter will have problem openning the box, as it is already there with another type.

You have to find where you are using different types & fix the problem by using different names.

Share:
4,066
DolDurma
Author by

DolDurma

Updated on December 18, 2022

Comments

  • DolDurma
    DolDurma over 1 year

    I'm trying to use Hive inside flutter Mobx, after checking user data in Hive I switched to another screens such as HomeView or Intro

    main.dart:

    Future<void> main() async {
      ...
    
      final appDocumentDirectory = await path_provider.getApplicationDocumentsDirectory();
      Hive.init(appDocumentDirectory.path);
      Hive.registerAdapter(UserAdapter());
    
      _setUpLogging();
    
      runApp(MultiProvider(providers: providers, child: StartupApplication()));
    }
    

    StartupApplication class: I don't use Hive

    class StartupApplication extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final isPlatformDark = WidgetsBinding.instance.window.platformBrightness == Brightness.dark;
        final initTheme = isPlatformDark ? nebrassLightTheme : nebrassLightTheme;
        return ThemeProvider(
          initTheme: initTheme,
          duration: const Duration(milliseconds: 400),
          child: Builder(builder: (context) {
            return MaterialApp(
              title: 'TEST',
              theme: ThemeProvider.of(context),
              home: const OverlaySupport(child: OKToast(
               child: MyHomePage() //--> checking user data widget
              )),
              onGenerateRoute: Routes.sailor.generator(),
              navigatorKey: Routes.sailor.navigatorKey,
            );
          }),
        );
      }
    }
    

    checking User in Hive inside MyHomePage class:

    class MyHomePage extends StatefulWidget {
      const MyHomePage({Key key}) : super(key: key);
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
        class _MyHomePageState extends State<MyHomePage> {
          @override
          Widget build(BuildContext context) {
            return FutureBuilder<Box<User>>(
                future: Hive.openBox('user'),
                builder: (context, snapshot) {
                  if (snapshot.connectionState == ConnectionState.done) {
                    final Box<User> userBox = snapshot.data;
        
                    if (userBox.values.isNotEmpty && userBox.get(0).active == 1) {
                      return HomeView();
                    } else {
                      return Intro();
                    }
                  } else {
                    return Container();
                  }
                });
          }
        
          @override
          void dispose() {
            Hive.close();
            super.dispose();
          }
        }
    

    now in other screen such as RegisterScreen class I implemented MobX and inside that I want to use user box, for example:

    class Register extends StatefulWidget {
      @override
      _RegisterState createState() => _RegisterState();
    }
    
    class _RegisterState extends State<Register> {
      TextEditingController _mobileNumber;
    
      final GlobalKey<ScaffoldState> _scaffoldState = GlobalKey<ScaffoldState>();
    
      @override
      void initState() {
        super.initState();
        _mobileNumber = TextEditingController();
      }
    
      @override
      Widget build(BuildContext context) {
        final _registerViewModel = Provider.of<RegisterViewModel>(context, listen: false);
    
        return Directionality(
          textDirection: TextDirection.ltr,
          child: Scaffold(
            key: _scaffoldState,
    
            ...
            //_registerViewModel.registerAccount(_mobileNumber.text, '111');
    
          ),
        );
      }
    
      void _showSnackBar(String message, BuildContext context) {
        _scaffoldState.currentState.showSnackBar(SnackBar(
            content: Directionality(
                textDirection: TextDirection.rtl,
                child: Text(
                  '$message',
                  style: AppTheme.of(context).caption().copyWith(color: Colors.white),
                ))));
      }
    }
    

    MobX implementation:

    enum RegisterLoadingState { loading, done }
    enum ActiveLoadingState { loading, done }
    enum RegisteringState { initial, registered, activated, registerError, activeError }
    
    class RegisterViewModel = _RegisterViewModel with _$RegisterViewModel;
    
    abstract class _RegisterViewModel with Store {
      final WebApi _webApi;
    
      _RegisterViewModel({@required WebApi webApi}) : _webApi = webApi;
    
      ...
    
      @action
      Future<void> registerAccount(String mobileNumber, String deviceId) async {
        final RegisterRequest _request = RegisterRequest(mobileNumber, deviceId);
        try {
          loadingState = RegisterLoadingState.loading;
          final _res = await _webApi.register(_request);
          loadingState = RegisterLoadingState.done;
    
          _registerResponse = RegisterResponse.fromJson(_res.body as Map<String, dynamic>);
    
          /* I GET ERROR IN THIS LINE -- HiveError: The box "user" is already open and of type Box<User>.*/
    
          final userBox = await Hive.openBox('user');
          final user = User(/*...*/);
          userBox.putAt(0, user);
      }
    
      @action
      Future<void> activeAccount(String mobileNumber, String verifyCode) async {
        final ActiveAccountRequest _activeAccount = ActiveAccountRequest(mobileNumber, verifyCode);
    
        final userBox = await Hive.openBox('user');
        final User currentUser = userBox.getAt(0) as User;
        final user = User(/*...*/);
        userBox.putAt(0, user);
      }
    }
    
    • void
      void over 3 years
      You are opening multiple boxes of the same type User. What is the question here ?
    • DolDurma
      DolDurma over 3 years
      @void my question is how can i use previously opened box? i want to use box on different screens or how can i open again box?
    • void
      void over 3 years
      You can open a Box in the main method of your app and access a Box like => Box userBox = Hive.box('user');
    • DolDurma
      DolDurma over 3 years
      @void and how about after that? my mean is MyHomePage class
    • void
      void over 3 years
      I added an answer. Check it out.
  • DolDurma
    DolDurma over 3 years
    how can i use FutureBuilder instead of opening box in main function?
  • void
    void over 3 years
    If you want to use the FutureBuilder widget in the HomePage, you can move the opening of box from main to the HomePage and use a FutureBuilder widget. @DolDurma
  • void
    void over 3 years
    Did you come across any other issues with my provided answer ?
  • DolDurma
    DolDurma over 3 years
    I changed code and I don't get error now. But I don't want opening box in main function
  • void
    void over 3 years
    Like I said in my previous comment, you can move opening of box to HomePage and use a FutureBuilder widget. @DolDurma
  • DolDurma
    DolDurma over 3 years