Search code examples
flutterflutter-hiveflutter-riverpod

Flutter Locales Using Hive and Riverpod


I am using Hive with app_profile data model to store the app settings in local DB, and using riverpod to call HivedataStore (dependency injection). The problem is that when I update the hive box of the local type, the app needs to be restarted to take effect, but if I update the Theme to Dark it work as it supposed to.
Here is my code:

main.dart

 void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // await AppAssets.preloadSVGs();
  final dataStore = HiveDataStore();
  await dataStore.init();
  await dataStore.createDefaultProfile(
      appProfile: AppProfile(
    onBoardingCompleted: false,
    locale: 'en',
    themeMode: ThemeModeCustomized.light,
  ));

  runApp(ProviderScope(overrides: [
    hiveDataStoreProvider.overrideWithValue(dataStore),
  ], child: const MyApp()));
}

hive_data_store.dart

class HiveDataStore {
  static const profileBoxName = 'appProfile';
  static const themeColor = 'themeColor';

  Future<void> init() async {
    await Hive.initFlutter();

    Hive.registerAdapter<AppProfile>(AppProfileAdapter());
    Hive.registerAdapter(ThemeModeCustomizedAdapter());

    await Hive.openBox<AppProfile>(profileBoxName);
  }

  Future<void> createDefaultProfile({
    required AppProfile appProfile,
  }) async {
    final box = Hive.box<AppProfile>(profileBoxName);
    if (box.isEmpty) {
      await box.put('0', appProfile);
    } else {
      print('box already have these Values : ${box.get(0)?.locale}');
    
    }
  }

  Future<void> updateBox({
    bool? onBoardingCompleted,
   String? locale,
    ThemeModeCustomized? themeMode,
  }) async {
    final box = Hive.box<AppProfile>(profileBoxName);
    final userProfile = box.get('0');
    final newProfile = userProfile!.copyWith(
        onBoardingCompleted: onBoardingCompleted,
        locale: locale,
        themeMode: themeMode);
    await box.put('0', newProfile);
  }

  AppProfile? appProfile() {
    final box = Hive.box<AppProfile>(profileBoxName);
    return box.get(0);
  }

  ValueListenable<Box<AppProfile>> appProfileListenable() {
    return Hive.box<AppProfile>(profileBoxName).listenable();
  }
}

final hiveDataStoreProvider = Provider<HiveDataStore>((ref) {
  throw UnimplementedError();
});

myapp.dart

class MyApp extends ConsumerWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
 
    final provider = ref.watch(hiveDataStoreProvider);
    return ValueListenableBuilder(
        valueListenable: provider.appProfileListenable(),
        builder: (context, Box<AppProfile> box, __) {
          print('myapp rebuilds listenablebuilder');
          final appProfile =  box.get('0');
          return MaterialApp(
            debugShowCheckedModeBanner: false,
            localizationsDelegates: AppLocalizations.localizationsDelegates,
            supportedLocales: AppAssets.getLocals(appProfile!),
            onGenerateTitle: (context) {
              var t = AppLocalizations.of(context);
              return t!.appTitle;
            },
            themeMode: AppAssets.themeModeGeter(appProfile),
            theme: ThemeData.light(),
            darkTheme: ThemeData.dark(),
            initialRoute: '/',
            routes: {
              '/': (context) {
                return  const HomePage();
              }
            },
          );
        });
  }
}

homepage.dart

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

  @override
  Widget build(BuildContext context) {
    print('homepage rebuils');
    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context)!.appTitle),
      ),
      body: Center(
        child: Consumer(builder: (context, WidgetRef ref, __) {
          return Column(
            children: [
              TextButton(
                  onPressed: () {
                    ref.read(hiveDataStoreProvider).updateBox(
                          locale: 'ar', themeMode: ThemeModeCustomized.dark
                        );
                  },
                  child: const Text('العربية')),
              TextButton(
                  onPressed: () {
                    ref.read(hiveDataStoreProvider).updateBox(
                          locale: 'en', themeMode: ThemeModeCustomized.light
                        );
                  },
                  child: const Text('English')),
            ],
          );
        }),
      ),
    );
  }
}

I think something need to be Changed in MyApp class, I use ValueListenableBuilder because I've seen in it in the Hive official package page. Why the app need to be restarted to take effect for the locales? Unlike the app Theme which works perfectly.

Here my app_assets.dart code (just extra info):

class AppAssets {
 

  static List<Locale> getLocals(AppProfile appProfile) {
    switch (appProfile.locale) {
      case 'ar':
        return [const Locale('ar', '')];

      case 'en':
        return [const Locale('en', '')];
      case '':
        return AppLocalizations.supportedLocales;
      default:
        return AppLocalizations.supportedLocales;
    }
  }

  static ThemeMode themeModeGeter(AppProfile? appProfile) {
    switch (appProfile?.themeMode.name) {
      case 'dark':
        {
          return ThemeMode.dark;
        }

      case 'light':
        {
          return ThemeMode.light;
        }

      case 'system':
        {
          return ThemeMode.system;
        }
      default:
        ThemeMode.system;
    }
    return ThemeMode.system;
  }

 
}

Solution

  • my mistake is that I was updating supportedLocales without updating locale in the MaterialApp Widget , I will keep the question though it might have a hint for others ..

    supportedLocales: AppAssets.getLocals(appProfile),
     locale: Locale('$appProfile.locale',''),