Closed MichelMelhem closed 2 years ago
Hi @MichelMelhem 👋 Thanks for opening an issue!
This is due to a bug in Flutter https://github.com/flutter/flutter/issues/93676. It would be awesome if you could leave a 👍 on the issue.
Sorry for the inconvenience!
Hello @felangel , Thanks for your answer ! As it is a flutter related issue would you consider adding back, even as a deprecated function, the old way of initializing the storage ? In fact I updated all my blocs to be compatible with version 8 of the library with the new syntax so it is not possible for me to revert back my app.
Hi @felangel , any updates on this issue ?
Absolutely same here, any update on this issue or temporary solution how to avoid/fix it?
I am also waiting for any solution.
Try out the temporary solution which is suggested by @felangel. You can go through the example code on hydrated bloc in here. Or just add flutter_services_binding
to pubspec.yaml, and replace WidgetsFlutterBinding.ensureInitialized();
with FlutterServicesBinding.ensureInitialized();
. This solution works for me and I am currently testing it.
The flutter_services_binding was a solution but it’s only temporary (changes in flutter have made it not feasible on beta and master channels). I’m currently working on an alternative solution, sorry for the inconvenience!
You can use the 9.0.0-dev version of hydrated_bloc (via the createStorage
api) now:
For example:
HydratedBlocOverrides.runZoned(
() => runApp(WeatherApp(weatherRepository: WeatherRepository())),
blocObserver: WeatherBlocObserver(),
createStorage: () async {
WidgetsFlutterBinding.ensureInitialized();
return HydratedStorage.build(
storageDirectory: kIsWeb
? HydratedStorage.webStorageDirectory
: await getTemporaryDirectory(),
);
},
);
}
It's important not to call WidgetsFluitterBinding.ensureInitialized
outside of the HydratedBlocOverrides zone.
Closing for now but feel free to comment with any questions and I'm happy to take a closer look.
Hey @felangel
I am using firebase, hive and service locators, and the hydrated bloc. So I'm not sure how to initialize other services and create storage together. Any combination that I tried threw errors.
This is my code before using hydrated bloc
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await initialiseServices();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
FlutterError.onError = (FlutterErrorDetails details) {
debugPrint("=================== CAUGHT FLUTTER ERROR $details");
};
runZonedGuarded(() {
BlocOverrides.runZoned(
() => runApp(const App()),
blocObserver: SimpleBlocObserver(),
);
}, FirebaseCrashlytics.instance.recordError);
}
@rahulrmishra can you share a link to a minimal reproduction sample?
Hi @felangel,
I'm having the similar problem like @rahulrmishra. I have this error if I try to initialize Firebase before using WidgetsFlutterBinding.ensureInitialized();
E/flutter ( 8283): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: Binding has not yet been initialized.
E/flutter ( 8283): The "instance" getter on the ServicesBinding binding mixin is only available once that binding has been initialized.
E/flutter ( 8283): Typically, this is done by calling "WidgetsFlutterBinding.ensureInitialized()" or "runApp()" (the latter calls the former). Typically this call is done in the "void main()" method. The "ensureInitialized" method is idempotent; calling it multiple times is not harmful. After calling that method, the "instance" getter will return the binding.
I have created a sample here: https://github.com/minhdanh/flutter-hydrated-bloc-firebase/blob/master/lib/main.dart
@minhdanh You also needs to call WidgetsFlutterBinding.ensureInitialized(); before await Firebase.initializeApp(); So you call WidgetsFlutterBinding.ensureInitialized(); twice in total.
Thank you @aaassseee.
@felangel I use your example with a minor change and could see that instead of using:
return BlocProvider(
create: (_) => BrightnessCubit(),
child: AppView(),
);
when I use:
final brightnessCubit = BrightnessCubit();
return BlocProvider.value(
value: brightnessCubit,
child: AppView(),
);
(I use BlocProvider.value to reuse brightnessCubit somewhere else)
I'll have the error Storage was accessed before it was initialized
when the code is reloaded. Why this happens and what can I do about this?
I updated my sample repo in case you want to have a look.
Best regards
@minhdanh I think the zone get rebuild when hot reload. Because you are initing storage inside the create storage function. so at that moment the storage getting create when zone rebuild. I suggest you init the hydrated storage before zone creation.
Hi @aaassseee, I made the change as you suggested but it's still same error for me:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final storage = await HydratedStorage.build(
storageDirectory: kIsWeb
? HydratedStorage.webStorageDirectory
: await getTemporaryDirectory(),
);
HydratedBlocOverrides.runZoned(
() => runApp(App()),
storage: storage,
);
}
As mentioned in my previous comment, I think there's something with BlocProvider.value that causing this error.
I think it's not a problem with BlocProvider.value
but with the way the bloc is created outside of BlocProvider().
If I add:
+ final brightnessCubit = BrightnessCubit();
return BlocProvider(
create: (_) => BrightnessCubit(),
child: AppView(),
);
I'll also have the error when hot reloading. Still investigating
@minhdanh May I ask what is the use case with init cubit outside of BlocProvider? I think this is anti pattern with bloc design spec.
@aaassseee What I'm trying to do is to reuse a bloc I declared (bloc 1), and pass it to another bloc (bloc 2) so that in bloc 2 I can listen for the events of bloc 1 and react accordingly. Please see following example:
final _themeSettingsBloc = ThemeSettingsBloc();
return MultiBlocProvider(
providers: [
BlocProvider.value(value: _themeSettingsBloc),
BlocProvider<UserSettingsBloc>(
lazy: false,
create: (BuildContext context) => UserSettingsBloc(
themeSettingsBloc: _themeSettingsBloc,
),
),
],
child: const AppView(),
),
Is there any problem with this approach? :sweat_smile:
@minhdanh You can actually use context.read to get the ThemeSettingsBloc
@aaassseee Thank you so much. I changed my code like you suggested:
return MultiBlocProvider(
providers: [
BlocProvider(create: (_) => ThemeSettingsBloc()),
BlocProvider<UserSettingsBloc>(
lazy: false,
create: (BuildContext context) => UserSettingsBloc(
themeSettingsBloc: context.read<ThemeSettingsBloc>(),
),
),
],
child: const AppView(),
),
Now I don't have the error when reloading any more.
Best regards!
@minhdanh Happy to help
wha
@minhdanh Happy to help
What about when using get_it injection for bloc... i still have this annoying storage error🥲🥲
@farisbasha Any example code?
@farisbasha Any example code?
HydratedBlocOverrides.runZoned(
() async => runApp(await builder()),
blocObserver: AppBlocObserver(),
createStorage: () async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitDown,
DeviceOrientation.portraitUp,
]);
getIt.registerSingleton<AppRouter>(AppRouter());
configureDependencies(env);
return HydratedStorage.build(
storageDirectory: kIsWeb
? HydratedStorage.webStorageDirectory
: await getApplicationDocumentsDirectory(),
);
},
);
And Bloc Provider Look like
MultiBlocProvider(
providers: [
BlocProvider<CoreBloc>(
lazy: false,
create: (context) => getIt<CoreBloc>(),
),
BlocProvider<AuthBloc>(
lazy: false,
create: (ctx) => getIt<AuthBloc>(),
),
],
Both CoreBloc and AuthBloc are HydatedBloc
This error occur whenever i hot relode
Storage was accessed before it was initialized.
I/flutter (13213): Please ensure that storage has been initialized.
I/flutter (13213):
I/flutter (13213): For example:
I/flutter (13213):
I/flutter (13213): HydratedBlocOverrides.runZoned(
I/flutter (13213): () => runApp(MyApp()),
I/flutter (13213): createStorage: () => HydratedStorage.build(...),
I/flutter (13213): );
@farisbasha It feels anti pattern to me. Can you show me more code about where you construct the bloc with get_it? I didn't see where you init the bloc, for example CoreBloc. Also, if you show me a runnable example would help me a lot to identify the problem.
For anyone interested, I had the same problem as @farisbasha and I found that the solution is just to call the getIt injector (I'm using injectable) inside the body of runZoned
so that the storage field on HydratedBlocOverrides is set prior to the blocs being built by the injector.
Ex:
Future<void> main() async {
HydratedBlocOverrides.runZoned(
() async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
configureDependencies("dev");
runApp(MyApp(init: getIt.allReady));
},
storage: await _buildStorage(),
);
}
Future<HydratedStorage> _buildStorage() async {
WidgetsFlutterBinding.ensureInitialized();
final storage = await HydratedStorage.build(
storageDirectory: kIsWeb ? HydratedStorage.webStorageDirectory : await getTemporaryDirectory(),
);
return storage;
}
I highly recommend migrating to hydrated_bloc 9.0.0-dev.3 which moved away from the zone-based api
Hello, I recently updated the HydratedBloc lib from
7.0.0
to8.0.0
. With this new version, the new way of initializing the storage is by usingHydratedBlocOverrides.runZoned
. But i'm facing issues with underlying app behaviors not working the same while using this new API. In fact fact i'm consistently getting the exceptionWhile hot reloading my app from the vscode DevTool. This is problematic as it block me in developing my app, each time i hot reload it i get an exception popping.
After some investigation i think that i found was was broken in the new version of Hydrated Bloc. My findings are that in version
7.0.0
of hydrated bloc you could use BlocProvider this way without any issues :In the new version
8.0.0
we are forced to do :If you use the first syntax, each time you reload the app using hydrated storage 8.0.0 you will get the
StorageNotFound
error.Here you will be able to find a modified version of the
main.dart
of the Hydrated Bloc example project that reproduces the bug : https://pastebin.com/RLjUJVXqWhy this bug is problematic ?
With this bug you can't do something like this anymore :
Flutter doctor output:
Thank you for your help, Michel M