A Falcon micro-framework package to manage UI states reactively based on custom BLoC and reactive extenstions.
Renderer came to be able to inject the refeshing UI states reactively in flutter widget tree with minimum effort.
dependencies:
renderer: [latest-version]
- Create your BLoC event abstract class and let it inherit from
RendererEvent
abstract class AuthEvent extends RendererEvent {}
- Create your BLoC state abstract class and let it inherit from
RendererState
abstract class AuthState extends RendererState {}
- Create a BLoC class to include your logic and let it inherit from
RendererBLoC
abstract class.RendererBLoC<V, S>
is a generic class so it expects to start with both Event and State types respectively. Finally, implement bothdispatch()
anddispose()
functions, noting thatdispose()
only needs to call the already inheritedcloseSubject()
function.
class AuthBloc extends RendererBLoC<AuthEvent, AuthState> {
@override
void dispatch(AuthEvent event) async {}
@override
void dispose() {
closeSubject();
}
}
4- Finally, to notify Renderers about a new state, all you need to do is calling the already inherited function notifyRenderers()
and pass your great state object to it as an argument.
class AuthBloc extends RendererBLoC<AuthEvent, AuthState> {
@override
void dispatch(AuthEvent event) async {
if (event is LoginEvent) {
notifyRenderers(LoginSuccess('User Logged In'));
}
}
@override
void dispose() {
closeSubject();
}
}
Renderer carries the burden of using always a Stateful Widget to repaint your UI states away of your shoulders. It helps get rid of the extensive boilerplate code used to be added inside initState()
as will as cancelling any state stream subscriptions inside dispose()
.
- Inside your Stateless Widget let your class implements
RendererFire
mixin.
class HomeScreen extends StatelessWidget with RendererFire {}
- In your
build()
widget tree, wrap the widget that expects data from a state inside aRenderer
widget. ARenderer<B, S>
is a generic widget that expects both a BLoC and success State types respectively. The Renderer will NOT allow its child widget to refresh until it receives one of the 3 pre-defined (Success, Error or Loading) states.
class HomeScreen extends StatelessWidget with RendererFire {
const HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: () {
fireEvent<AuthBloc, AuthEvent>(LoginEvent(status: 'User Logged In'));
}),
body: Center(
child: Renderer<AuthBloc, LoginSuccess>(
onInit: (timeStamp, context) => showDialog(context: context, builder: (...))
initial: CustomInitialWidget(),
errorWhen: (errorState) => errorState is LoginError,
loadingWhen: (loadingState) => loadingState is LoginLoading,
stateBuilder: (state) => Center(
child: Text(
state.message,
style: const TextStyle(color: Colors.green, fontSize: 40),
),
),
loading: const CircularProgressIndicator(),
errorBuilder: (RendererError error) => Center(
child: Column(
children: [
Text(
error.title,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.red, fontSize: 60),
),
Text(
'${error.message}, CODE: ${error.code}',
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.red, fontSize: 60),
),
],
),
)
),
),
);
}
}
-
A Renderer now has
onInit
field to help you override the usage ofinitState
when you need to call/execute a function immediately on a widget starts.onInit
guarantees to execute your code right after the last frame of a "renderer widget" has been drawn and setteled on screen.onInit
provides a timestamp that indicates when exactly the Renderer has been mounted and aBuildContext
instance to be used to show Dialogs, Snackbars,...etc. -
For each renderer widget, you're required to guide the renderer when to replace the success state UI with an error or loading UI using
errorWhen
,loadingWhen
callbacks. -
For a loading state, you're required to provide the
onLoading
widget to render when the renderer receives the loading state using the defined earlierloadingWhen
. -
For an error state, you're required to provide one of the following:
error
: A widget to statically render an error widget.errorBuilder
: A callback to get aRendererError
object (containts error data: title, message and code) when the renderer receives its error state. You may use the error data to show your custom error widget.onError
: A callback to get bothRendererError
object and the currentbuildContext
object. This callback is useful in case you need to show Dialog, Snackbar, ...etc that require abuildContext
to build.
PLEASE NOTE: If you intend to useerrorBuilder
oronError
callbacks then you MUST provide an error state object that contains AT LEAST aRendererError
field toerrorWhen
.
class LoginError extends AuthState {
final RendererError rendererError; //Must be exatcly named
final Oject1 someObject;
final Object2 someOtherObject;
}
- Finally, Firing the event is no longer needs an instance of a BLoC as usual. You just need to call
fireEvent()
function that is already inherited fromRendererFire
mixin.fireEvent<B, V>
is a generic function that expects both BLoC and Event types respectively.
fireEvent<AuthBloc, AuthEvent>(LoginEvent(status: 'User Logged In'));