Built on top of the Flutter BLoC library, RiveBloc is a Programming Interface Layer deeply inspired by Riverpod that makes working with BLoC less verbose and less time-consuming.
The main building blocks of RiveBloc are providers
and builders
.
A Provider
is a way to get access to a piece of state (an Object
value or a Bloc
/Cubit
instance), while a Builder
is a way to
use that state in the widgets tree.
RiveBlocProvider
is the main entry point for creating providers.
IMPORTANT: For providers to work, you need to add RiveBlocScope
at the root of your Flutter applications, like this:
void main() {
runApp(RiveBlocScope(child: MyApp()));
}
Providers solve the following problems:
- Providers have the flexibility of global variables, without their downsides. They can be accessed from anywhere, while ensuring testability and scalability,
- Providers are safe to use. It is not possible to read a value in an uninitialized state,
- Providers can be accessed in a single line of code.
RiveBloc offers 5 providers, divided into two main categories:
- 1
Final
Provider for exposing final DartObject
values, - 4
RiveBloc
Providers for exposingBloc
/Cubit
dynamicstate
values.
The FinalProvider
is created with RiveBlocProvider.finalValue
, while the other ones are created with
RiveBlocProvider.value
, RiveBlocProvider.state
, RiveBlocProvider.async
and RiveBlocProvider.stream
.
Providers come in many variants, but they all work the same way.
The most common usage is to declare them as global variables, like this:
final myProvider = RiveBlocProvider.finalValue(() => MyValue());
final myBlocProvider = RiveBlocProvider.state(() => MyBloc());
Secondly, all the providers should be declared through the RiveBlocScope
widget (not really mandatory, but highly recommended), so that they are
for sure accessible from anywhere in the application:
RiveBlocScope(
providers: [
myProvider,
myBlocProvider,
...
],
child: MyApp(),
);
IMPORTANT: Multiple providers cannot share the same value type!!
// This, does not work:
final value1 = RiveBlocProvider.value(() => MyValueCubit(1));
final value2 = RiveBlocProvider.value(() => MyValueCubit(2));
// Do this, instead:
final value1 = RiveBlocProvider.value(() => MyValueCubit1());
final value2 = RiveBlocProvider.value(() => MyValueCubit2());
Once the providers are declared, you can access the values and instances
they expose, by using the read
and watch
methods:
RiveBlocBuilder(
builder: (context, ref) {
final myValue = ref.read(myProvider);
final myBloc = ref.watch(myBlocProvider);
},
);
myValue
is an instance ofMyValueCubit
,myBloc
is an instance ofMyBloc
.