4

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);
  }
}
padaleiana
  • 955
  • 1
  • 14
  • 23
DolDurma
  • 15,753
  • 51
  • 198
  • 377

3 Answers3

6

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.

nđq
  • 388
  • 5
  • 14
4
  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();
  }
}
void
  • 12,787
  • 3
  • 28
  • 42
  • how can i use `FutureBuilder` instead of opening box in `main` function? – DolDurma Aug 30 '20 at 19:40
  • 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 Aug 30 '20 at 19:56
  • Did you come across any other issues with my provided answer ? – void Aug 30 '20 at 20:17
  • I changed code and I don't get error now. But I don't want opening box in main function – DolDurma Aug 30 '20 at 20:19
  • Like I said in my previous comment, you can move opening of `box` to `HomePage` and use a `FutureBuilder` widget. @DolDurma – void Aug 30 '20 at 20:24
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/220717/discussion-between-doldurma-and-void). – DolDurma Aug 31 '20 at 04:39
2

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.

Sajad Rezvani
  • 366
  • 2
  • 14