kranfix / riverbloc

`flutter_bloc` implemented with `riverpod` instead of `provider`.
83 stars 17 forks source link

hydrated_bloc throws StorageNotFound error after hot reload #72

Closed appano1 closed 2 years ago

appano1 commented 2 years ago

I'm using riverbloc with hydrated_bloc.

HydratedBloc throws StorageNotFound error after hot reloaded even the storage was initialized.

example

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:path_provider/path_provider.dart';
import 'package:riverbloc/riverbloc.dart';

class CounterCubit extends HydratedCubit<int> {
  CounterCubit(String id)
      : _id = id,
        super(0);

  final String _id;

  void increase() => emit(state + 1);
  void decrease() => emit(state - 1);

  @override
  String get id => _id;

  @override
  int fromJson(Map<String, dynamic> json) {
    return json['value'];
  }

  @override
  Map<String, dynamic> toJson(int state) {
    return {'value': state};
  }
}

final counterProvider = BlocProvider<CounterCubit, int>(
  (_) => throw UnimplementedError(),
);

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final storage = await HydratedStorage.build(
    storageDirectory: await getTemporaryDirectory(),
  );

  HydratedBlocOverrides.runZoned(
    () => runApp(const ProviderScope(
      child: App(),
    )),
    storage: storage,
  );
}

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ProviderScope(
      overrides: [counterProvider.overrideWithValue(CounterCubit('home'))],
      child: Scaffold(
        appBar: AppBar(title: const Text('Home')),
        body: SizedBox(
          width: double.maxFinite,
          child: Consumer(
            builder: (_, ref, __) => Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(ref.watch(counterProvider).toString()),
                Wrap(
                  spacing: 12,
                  children: [
                    ElevatedButton(
                      onPressed: ref.read(counterProvider.bloc).increase,
                      child: const Text('+1'),
                    ),
                    ElevatedButton(
                      onPressed: ref.read(counterProvider.bloc).decrease,
                      child: const Text('-1'),
                    ),
                  ],
                ),
                ElevatedButton(
                  onPressed: () => Navigator.push(
                    context,
                    MaterialPageRoute(builder: (_) => const SubPage()),
                  ),
                  child: const Text('Move to next page'),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class SubPage extends StatelessWidget {
  const SubPage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return ProviderScope(
      overrides: [counterProvider.overrideWithValue(CounterCubit('sub'))],
      child: Scaffold(
        appBar: AppBar(title: const Text('Sub')),
        body: SizedBox(
          width: double.maxFinite,
          child: Consumer(
            builder: (_, ref, __) => Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(ref.watch(counterProvider).toString()),
                Wrap(
                  spacing: 12,
                  children: [
                    ElevatedButton(
                      onPressed: ref.read(counterProvider.bloc).increase,
                      child: const Text('+1'),
                    ),
                    ElevatedButton(
                      onPressed: ref.read(counterProvider.bloc).decrease,
                      child: const Text('-1'),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

error

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following StorageNotFound was thrown building App(dirty):
Storage was accessed before it was initialized.
Please ensure that storage has been initialized.
kranfix commented 2 years ago

Thanks for reporting. I'll fix this ASAP.

But, in advance. The overrideWithValue is misused because the CounterCubit('sub') is not disposed. You can use counterProvider.overrideWithProvider(BlocProvider<CounterCubit, int>((ref) => CounterCubit('sub'))).

kranfix commented 2 years ago

I replied the error. I was surprised, but there are 2 mistakes in your code:

1) Prefer override with provider:

final counterProvider = BlocProvider<CounterCubit, int>(
  (_) => throw UnimplementedError(),
);

Widget build(BuildContext context) {
    return ProviderScope(
      overrides: [
        counterProvider.overrideWithValue(
          BlocProvider((ref) => CounterCubit('sub'))
        ),
      ],
      child: Container(),
    );
  }

or its equivalent:

final counterProvider = BlocProvider<CounterCubit, int>(
  (_) => CounterCubit('sub'),
);

Widget build(BuildContext context) {
    return ProviderScope(
      overrides: [counterProvider], // auto overrides itself
      child: Container(),
    );
  }

2) The methods must be always called as following (with either riverbloc or flutter_bloc) DONT pass the methods as arguments. DO create a callback that reads the bloc and calls the method.

Wrap(
  spacing: 12,
  children: [
    ElevatedButton(
      onPressed: () => ref.read(counterProvider.bloc).increase(),
      child: const Text('+1'),
    ),
    ElevatedButton(
      onPressed: () => ref.read(counterProvider.bloc).decrease(),
      child: const Text('-1'),
    ),
  ],
),

These steps are required to ensure the store is initialized. Remember that its initialization is asynchronous and need some micro or millisecond to be ready.

appano1 commented 2 years ago

Thank you for letting me know the mistake... Thanks a lot..! 😁

kranfix commented 2 years ago

You are welcome. Thanks for using riverbloc.