flutterriverpodflutter-hive

HiveError: The box "recipebox" is already open and of type Box<Recipe>


import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:whateatgo2/riverpod/myState.dart';
import 'package:whateatgo2/screen/home_screen.dart';

import 'model/recipe.dart';

void main() async {
  await Hive.initFlutter();
  Hive.registerAdapter(RecipeAdapter());
  Box<Recipe> recipeBox = await Hive.openBox<Recipe>('recipeBox');
  if (recipeBox.isEmpty) {
    fetchData(); // JSON -> recipeBox 저장
  }

  runApp(
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

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

  @override
  ConsumerState<MyApp> createState() => _MyAppState();
}

class _MyAppState extends ConsumerState<MyApp> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    ref.read(shakeDetectorProvider.notifier).state.startListening();
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    // 앱의 라이프사이클 상태가 변경될 때마다 호출된다.
    super.didChangeAppLifecycleState(state);
    switch (state) {
      case AppLifecycleState.resumed:
        ref.read(shakeDetectorProvider.notifier).state.startListening();
      case AppLifecycleState.inactive:
        break;
      case AppLifecycleState.detached:
        break;
      case AppLifecycleState.hidden:
        break;
      case AppLifecycleState.paused:
        ref.read(shakeDetectorProvider.notifier).state.stopListening();
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(), //ListScreen(),
      theme: ThemeData(
        fontFamily: 'SongMyung',
        primaryColor: Colors.black,
        //refresh 버튼 색깔
        colorScheme: ColorScheme.fromSwatch().copyWith(
          secondary: Colors.black,
        ),
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            backgroundColor: Colors.black,
          ),
        ),
        appBarTheme: const AppBarTheme(
          color: Colors.transparent,
          elevation: 0,
          foregroundColor: Colors.black,
        ),
      ),
    );
  }
}

void fetchData() async {
  //로컬 파일로부터 모든 레시피를 불러와 DB에 넣는다.
  try {
    //파일을 읽어와 List<Recipe>로 변환시킨다.
    String jsonString = await rootBundle.loadString('sourceFile');
    Map<String, dynamic> dataMap = jsonDecode(jsonString);
    List<dynamic> dataList = dataMap['COOKRCP01']['row'];
    List<Recipe> recipes =
        dataList.map((json) => Recipe.fromJson(json)).toList();

    //DB에 넣는다.
    Box<Recipe> recipeBox = Hive.box('recipeBox');
    recipeBox.addAll(recipes);
  } catch (e) {
    throw Exception(e);
  }
}

import 'dart:math';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:shake/shake.dart';

import '../model/recipe.dart';

//1. 흔들었을때,refresh 버튼을 눌렀을때 random 인덱스값
final diceNumberProvider = StateNotifierProvider<DiceNumberNotifier, int>(
  (ref) => DiceNumberNotifier(),
);

class DiceNumberNotifier extends StateNotifier<int> {
  DiceNumberNotifier() : super(-1);

  //주사위를 굴립니다.
  void roll() {
    state = Random().nextInt(1115); // 시작은 -1 , 0~1114
    print(state);
  }
}

//2. ShakeDetector Provider
final shakeDetectorProvider = StateProvider(
  (ref) => ShakeDetector.waitForStart(
    shakeThresholdGravity: 2,
    onPhoneShake: () {
      ref.read(diceNumberProvider.notifier).roll();
    },
  ),
);

//3. 홈 화면에 후보군 리스트(초기값은 모든 리스트)
final homeScreenRecipesProvider = StateProvider((ref) =>
    Hive.box('recipeBox').values.map((item) => Recipe.fromJson(item)).toList());

//4. 리스트 화면에 후보군 리스트(초기값은 모든 리스트)
//검색 필터를 적용한 리스트 Provider
//사용자가 화면상에서 떠나고 다시 진입했을 때 상태를 초기화 할 경우 autoDispose를 사용한다.
final listScreenRecipesProvider =
    StateNotifierProvider.autoDispose<FilteredRecipeListNotifier, List<Recipe>>(
  (ref) => FilteredRecipeListNotifier(),
);

class FilteredRecipeListNotifier extends StateNotifier<List<Recipe>> {
  FilteredRecipeListNotifier()
      : super(Hive.box('recipeBox')
            .values
            .map((item) => Recipe.fromJson(item))
            .toList());

  //필터할 대상(부모 리스트)
  void setDefaultList() {
    state = Hive.box('recipeBox')
        .values
        .map((item) => Recipe.fromJson(item))
        .toList();
  }

  List<Recipe> normalFilterList(String keyword) {
    //검색어를 포함하고있는 레시피 리스트
    List<Recipe> filtered;
    filtered = Hive.box('recipeBox')
        .values
        .map((item) => Recipe.fromJson(item))
        .toList()
        .where((element) =>
            (element.rcpnm!.contains(keyword)) || //메뉴명
            (element.rcppat2!.contains(keyword)) || //요리종류
            (element.hashtag!.contains(keyword)) || //해쉬태그
            (element.rcppartsdtls!.contains(keyword))) //재료정보
        .toList();
    state = filtered;
    return filtered;
  }

  List<Recipe> filterInFilteredResult(String keyword) {
    //검색어를 포함하고있는 레시피 리스트
    List<Recipe> filtered;
    filtered = state
        .where((element) =>
            (element.rcpnm!.contains(keyword)) || //메뉴명
            (element.rcppat2!.contains(keyword)) || //요리종류
            (element.hashtag!.contains(keyword)) || //해쉬태그
            (element.rcppartsdtls!.contains(keyword))) //재료정보
        .toList();
    state = filtered;
    return filtered;
  }
}

This is my repository.

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

The box "name" is already open and of type Box<dynamic>

Flutter - HiveError: Box is Already Open

But, still I don't know how to fix my problem. Is it because of riverpod scope?

======== Exception caught by widgets library =======================================================
The following HiveError was thrown building HomeScreen(dirty, dependencies: [UncontrolledProviderScope], state: _ConsumerState#00fc9):
The box "recipebox" is already open and of type Box<Recipe>.

The relevant error-causing widget was: 
  HomeScreen HomeScreen:file:///C:/Users/kangs/StudioProjects/whateatgo/lib/main.dart:69:13
When the exception was thrown, this was the stack: 
#1      _ExternalProviderSubscription.read (package:riverpod/src/framework/provider_base.dart:179:29)
#2      ConsumerStatefulElement.watch (package:flutter_riverpod/src/consumer.dart:571:8)
#3      HomeScreen.build (package:whateatgo2/screen/home_screen.dart:16:41)
#4      _ConsumerState.build (package:flutter_riverpod/src/consumer.dart:479:19)
#5      StatefulElement.build (package:flutter/src/widgets/framework.dart:5409:27)
#6      ConsumerStatefulElement.build (package:flutter_riverpod/src/consumer.dart:542:20)
#7      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5297:15)
#8      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5462:11)
#9      Element.rebuild (package:flutter/src/widgets/framework.dart:5016:7)
#10     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:5279:5)
#11     StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5453:11)
#12     ComponentElement.mount (package:flutter/src/widgets/framework.dart:5273:5)
...     Normal element mounting (220 frames)
#232    Element.inflateWidget (package:flutter/src/widgets/framework.dart:4182:16)
#233    MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6569:36)
#234    MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6581:32)
...     Normal element mounting (449 frames)
#683    _UncontrolledProviderScopeElement.mount (package:flutter_riverpod/src/framework.dart:309:11)
...     Normal element mounting (35 frames)
#718    Element.inflateWidget (package:flutter/src/widgets/framework.dart:4182:16)
#719    Element.updateChild (package:flutter/src/widgets/framework.dart:3707:18)
#720    RenderObjectToWidgetElement._rebuild (package:flutter/src/widgets/binding.dart:1253:16)
#721    RenderObjectToWidgetElement.mount (package:flutter/src/widgets/binding.dart:1222:5)
#722    RenderObjectToWidgetAdapter.attachToRenderTree.<anonymous closure> (package:flutter/src/widgets/binding.dart:1169:18)
#723    BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2719:19)
#724    RenderObjectToWidgetAdapter.attachToRenderTree (package:flutter/src/widgets/binding.dart:1168:13)
#725    WidgetsBinding.attachRootWidget (package:flutter/src/widgets/binding.dart:1001:7)
#726    WidgetsBinding.scheduleAttachRootWidget.<anonymous closure> (package:flutter/src/widgets/binding.dart:981:7)
#730    _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:189:12)
#731    HiveImpl._getBoxInternal (package:hive/src/hive_impl.dart:182:9)
#732    HiveImpl.box (package:hive/src/hive_impl.dart:197:33)
#733    homeScreenRecipesProvider.<anonymous closure> (package:whateatgo2/riverpod/myState.dart:36:10)
#734    ProviderContainer.listen (package:riverpod/src/framework/container.dart:285:21)
#735    ConsumerStatefulElement.watch.<anonymous closure> (package:flutter_riverpod/src/consumer.dart:567:25)
#736    _LinkedHashMapMixin.putIfAbsent (dart:collection-patch/compact_hash.dart:535:23)
#737    ConsumerStatefulElement.watch (package:flutter_riverpod/src/consumer.dart:560:26)
#738    HomeScreen.build (package:whateatgo2/screen/home_screen.dart:16:41)
#739    _ConsumerState.build (package:flutter_riverpod/src/consumer.dart:479:19)
#740    StatefulElement.build (package:flutter/src/widgets/framework.dart:5409:27)
#741    ConsumerStatefulElement.build (package:flutter_riverpod/src/consumer.dart:542:20)
#742    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5297:15)
#743    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5462:11)
#744    Element.rebuild (package:flutter/src/widgets/framework.dart:5016:7)
#745    ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:5279:5)
#746    StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5453:11)
#747    ComponentElement.mount (package:flutter/src/widgets/framework.dart:5273:5)
...     Normal element mounting (220 frames)
#967    Element.inflateWidget (package:flutter/src/widgets/framework.dart:4182:16)
#968    MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6569:36)
#969    MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6581:32)
...     Normal element mounting (449 frames)
#1418   _UncontrolledProviderScopeElement.mount (package:flutter_riverpod/src/framework.dart:309:11)
...     Normal element mounting (35 frames)
#1453   Element.inflateWidget (package:flutter/src/widgets/framework.dart:4182:16)
#1454   Element.updateChild (package:flutter/src/widgets/framework.dart:3707:18)
#1455   RenderObjectToWidgetElement._rebuild (package:flutter/src/widgets/binding.dart:1253:16)
#1456   RenderObjectToWidgetElement.mount (package:flutter/src/widgets/binding.dart:1222:5)
#1457   RenderObjectToWidgetAdapter.attachToRenderTree.<anonymous closure> (package:flutter/src/widgets/binding.dart:1169:18)
#1458   BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2719:19)
#1459   RenderObjectToWidgetAdapter.attachToRenderTree (package:flutter/src/widgets/binding.dart:1168:13)
#1460   WidgetsBinding.attachRootWidget (package:flutter/src/widgets/binding.dart:1001:7)
#1461   WidgetsBinding.scheduleAttachRootWidget.<anonymous closure> (package:flutter/src/widgets/binding.dart:981:7)
#1465   _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:189:12)
(elided 6 frames from class _Timer and dart:async-patch)

Solution

  • Reading Hive.box('recipeBox').values gives you Iterable<Recipe>, so you don't need to map it from JSON.

    //필터할 대상(부모 리스트)
    void setDefaultList() {
      state = Hive.box('recipeBox')
          .values
          // .map((item) => Recipe.fromJson(item)) <-- remove this line
          .toList();
    }
    

    You can notice the error if you add the type when calling .box like this: Hive.box<Recipe>('recipeBox'). Do this to every call on .box in your code.