simplex 0.2.0 simplex: ^0.2.0 copied to clipboard
A simple state management library for Flutter.
Note that Simple SM at its current state now is not stable, use it at your own risk.
Simple SM is a simple state management library for Flutter. It is heavily inspired by GetX in a sense that
- their APIs are quite similar; and
- they both allow the use of mutable variables for state management.
Prior to using this GetX, my team and I have searched through countless other state management libraries. We started from BLoC, moved to MobX, then moved to Riverpod. The benefit of using these libraries is that they all make the state very "reactive" and "observable," however, one significant downside is that they all have much boilerplate code and that they require the use of immutable states (not with MobX, but it requires the use of code generation).
I created this library because I ran into a bug with GetX's builder, because that widget contains some global getter, and I only wanted to create some local states. Also, GetX's documentation, despite being long, is not as clear as what I would like. Hence, there comes this library.
More specifically, I wish to accomplish the following two goals:
- Provide a separation between the UI and the business logic;
- Make some common operations easier by reducing as much boilerplate code as possible.
Features #
The core of this library consists of two classes: SimpleController
and
SimpleView
. The former is a base class, the extension of which shall be
used to contain the business logic. The latter is a widget that can be
extended to work with a SimpleController
.
SimpleController #
A SimpleController
is a controller that is, well, simple. You cannot
instantiate it directly, so to use it, you will have to create a
subclass. We will use the Flutter's counter app as an example.
class CounterController extends SimpleController {
int counter = 0;
void increment() {
counter++;
refresh();
}
}
You can do whatever with the counter variable, and when you want to ask the
UI to rebuild, you just call refresh()
.
Now, there are more things that you can do with a SimpleController
. For
example, if, instead of setting the counter to 0 in the beginning, you want
it to be read from the database, which would require an asynchronous
operation, you can do the following:
class CounterController extends SimpleController {
int counter = 0;
@override
Future<void> onFirstReady() async {
counter = await getCounterFromDatabase();
}
}
and you are done.
Below is a list of the methods that are available to be overridden in a
SimpleController
:
onBound()
: Called whenever the controller is bound to a widget. It will fire for all the widgets that are bound to the controller.onInit()
: Called after controller is bound to the first widget, and returns thevoid
type. You can do some initialization here, but calling therefresh()
will have no immediate effects. If you wish to rebuild the widgets after initialization, you can callrefresh()
in theFuture.then
callback:@override void onInit() { final counterFuture = getCounterFromDatabase(); counterFuture.then((value) { counter = value; refresh(); }); }
onFirstReady()
: Called when the first widget is ready. This method will only be called once. This function can be asynchronous, and it returns aFutureOr<void>
.onReady()
: Called each time when a widget that's bound to this controller is ready. This function can be asynchronous as well.onUnbind()
: Called when the controller is unbound from a widget. It will fire for all the widgets that are bound to the controller.onDispose()
: Called when the controller is disposed. This method will only be called once. By default, this method would be called after all the bindings to the widgets are removed. You can, however, disable this by overriding thekeepAlive
property totrue
.
SimpleView #
A SimpleView is a widget that can be used to bind a SimpleController
to
rebuild after the controller.refresh()
is called. To use it, you have to
extend it:
class CounterView extends SimpleView<CounterController> {
const CounterView({
Key? key,
required super.controller,
super.groupKey,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Counter'),
),
body: Center(
child: Text(
'You have pushed the button this many times:',
),
),
floatingActionButton: FloatingActionButton(
onPressed: controller.increment,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Alternatively, if extending a widget it too much hassle, I have also
provided two other concrete extensions of SimpleView
: SimpleComponent
and SimpleBuilder
. The difference between them is that SimpleComponent
asks you to pass in a child
while SimpleBuilder
asks you to pass in a
builder function.
@override
Widget build(BuildContext context) {
return SimpleComponent(
controller: controller,
// it is your responsibility to use the same instance of controller here
// inside the builder function
child: ...
);
}
or
@override
Widget build(BuildContext context) {
return SimpleBuilder(
controller: controller,
builder: (context, controller) {
return ...
},
);
}
Getting started #
To use this library, add simple_sm
as a dependency in your pubspec.yaml,
then it should be good to go.
Usage #
Has been covered in the Features section.
Additional information #
TODOS:
- Write tests to test all the features;
- Create some helper functions like
useTextEditingController
etc. to help with composing and disposing of the controllers; - Add support for animations;
- Set up CI/CD.