flutter_mediator 1.0.0-beta.2 flutter_mediator: ^1.0.0-beta.2 copied to clipboard
A state management package for flutter.
Flutter Mediator #
Flutter mediator is a state management package that targets as easy and intuitive to use.
Features #
- Easy to use - only three steps needed to make things work
- Lightweight - small package size
- Efficiency and Performance - use InheritModel as the underlying layer
- Flexible - provide both automatic and manual observation
- Intuitive - three main classes:
Host
,Publisher
,Subscriber
Setting up #
Add the following dependency to pubspec.yaml of your flutter project:
dependencies:
flutter_mediator: "^1.0.0"
Import flutter_mediator in files that will be used:
import 'package:flutter_mediator/mediator.dart';
For help getting started with Flutter, view the online documentation.
Getting Started Quick Steps: #
- Extend the model from
Publisher
- Declare rx variables with
.rx
which will observe the value automatically. - Implement the update methods.
MyModel.dart
class MyModel extends Publisher {
// `.rx` make the var automatically rebuild the widget when updated
var int1 = 0.rx;
void updateInt1() {
// int1 is a rx variable, it will automatically rebuild the widget when updated
int1 += 1;
}
- Execute the update method, for example, within a RaisedButton.
RaisedButton(
child: const Text('Update Int1'),
onPressed: () => context.getModel<MyModel>().updateInt1(),
);
- Inject host at the top level of the widget tree.
void main() {
runApp(
MultiHost.create2(
MyModel(updateMs: 1000), // model that extends Publisher
ListModel(updateMs: 500),// model that extends Publisher
child: MyApp(),
),
/// MultiHost.create1 to MultiHost.create9 are provided by the package.
/// the original form, for one model:
// Host(
// model: MyModel(updateMs: 1000),
// child: MyApp(),
// ),
);
}
- Subscribe the widget to the host with designated one or more aspect.
6-1. Choice an aspect to represent the widget.
In this example, a string type aspect with value of'int1'
is designated.
6-2. Designate the model to be used.
In this example, myModel of class MyModel is used.
class Int1Listener extends StatelessWidget {
@override
Widget build(BuildContext context) {
return 'int1'.subModel<MyModel>(
create: (context, model) {
// automatically update when value get updated
return Text('Int1 is ${model.int1}');
},
);
}
}
- Finally, rx variables used inside the create method will automatically rebuild the widget when its value get updated. (trigger by getter and setter of the value)
Example #
You can find the example in the example folder.
Detail #
- single model - host
- multiple models - host
- automatically update the widget with rx variable - Publisher
- access the underlying value directly of rx variable - Publisher
- update the value by call style of rx variable - Publisher
- manually publish an aspect of the model - Publisher
- manually publish more than one aspect of the model - Publisher
- manually publish to all aspects of the model - Publisher
- future publish of the model - Publisher
- rebuild only once per frame for the same aspect - Publisher
- writing model helper - Publisher
- get model value - Subscriber
- subscribe with a single aspect of the model - Subscriber
- subscribe with more than one aspect of the model - Subscriber
- subscribe with all aspects of the model - Subscriber
- subscribe with enum aspects of the model - Subscriber
- writing custom rx class
1. single model #
void main() {
runApp(
Host(
model: AppModel(), // model that extends Publisher
child: MyApp(),
),
);
}
2. multiple models #
MultiHost.create1
to MultiHost.create9
are provided by the package.
You can add more MultiHost.createXX
method, see multi_host.dart for example.
void main() {
runApp(
MultiHost.create2(
MyModel(updateMs: 1000), // model that extends Publisher
ListModel(updateMs: 500), // model that extends Publisher
child: MyApp(),
),
);
}
3. automatically update the widget #
.rx
make the var automatically rebuild the widget when updated
int:
class MyModel extends Publisher {
// `.rx` make the var automatically rebuild the widget when updated
var int1 = 0.rx;
void updateInt1() {
int1 += 1; // automatically update aspect related widgets
}
List:
class ListModel extends Publisher {
// `.rx` make the var automatically rebuild the widget when updated
final data = <ListItem>[].rx;
void updateListItem() {
// get new item data...
final newItem = ListItem(itemName, units, color);
data.add(newItem); // automatically update aspect related widgets
}
rx variable type of int
, double
, num
, string
, bool
, list
, map
, set
are provided by the package.
See also RxInt class,
RxList class,
RxList.add
4. access the underlying value directly #
Access the underlying value directly by .value
.
MyModel.dart
var int1 = 0.rx;
void updateInt1() {
// int1 += 1; // automatically update aspect related widget
/// is the same as
int1.value += 1; // automatically update aspect related widget
}
5. update the value by call style #
MyModel.dart
var _foo = 1.rx;
set foo(int value) {
_foo(value); // update rx variable by call() style
/// is the same as
// _foo = value;
/// is the same as
// _foo.value = value;
}
6. manually publish an aspect #
MyModel.dart
int manuallyInt = 0;
void manuallyPublishDemo(int value) {
manuallyInt = value;
publish('manuallyInt'); // manually publish aspect of 'manuallyInt'
}
7. manually publish more than one aspect #
Putting aspects in a list to publish more than one aspect.
MyModel.dart
int _foo = 0;
int _bar = 0;
void increaseBoth() {
_foo += 1;
_bar += 1;
publish(['foo', 'bar']); // manually publish aspects in a list
}
8. manually publish to all aspects #
Publish null value to publish to all aspects of the model.
MyModel.dart
void increaseAll() {
//...
publish(); // publish to all aspects of the model
}
9. future publish #
Use with a future method, for example,
MyModel.dart
int int1 = 0.rx;
Future<void> futureInt1() async {
await Future.delayed(const Duration(seconds: 1));
int1 += 1; // int1 is Rx, automatically update aspect related widgets
}
10. rebuild only once per frame #
Rebuild only once per UI frame for the same aspect.
Following code only cause the aspect related widgets to rebuild once.
MyModel.dart
int int1 = 0.rx;
void incermentInt1() async {
int1 += 1; // int1 is Rx, automatically update aspect related widgets
publish('int1'); // manually publish 'int1'
publish('int1'); // manually publish 'int1', again
// only cause the related widgets to rebuild once per UI frame
}
11. writing model helper #
You can write model helpers to simplified the typing. For example,
MyModel.dart
/// Helper function of MyModel
MyModel getMyModel(BuildContext context) {
return Host.getInheritOfModel<MyModel>(context);
}
Subscriber<MyModel> subMyModel({Key key, Object aspects, @required CreaterOfSubscriber<MyModel> create}) {
return aspects.subModel<MyModel>(key: key, create: create);
}
extension MyModelHelperT<T> on T {
Subscriber<MyModel> subMyModel({Key key, @required CreaterOfSubscriber<MyModel> create}) {
return Subscriber<MyModel>(key: key, aspects: this, create: create);
}
}
ListModel.dart
/// Helper function of ListModel
ListModel getListModel(BuildContext context) {
return Host.getInheritOfModel<ListModel>(context);
}
Subscriber<ListModel> subListModel({Key key, Object aspects, @required CreaterOfSubscriber<ListModel> create}) {
return aspects.subModel<ListModel>(key: key, create: create);
}
extension ListModelHelperT<T> on T {
Subscriber<ListModel> subListModel({Key key, @required CreaterOfSubscriber<ListModel> create}) {
return Subscriber<ListModel>(key: key, aspects: this, create: create);
}
}
12. get model value #
To get the model, for example, getting MyModel
,
original form
final model = Host.getInheritOfModel<MyModel>(context);
by package helper of context extension
final model = context.getModel<MyModel>();
(See mediator_helper.dart for package helper.)
by model helper on your own
final model = getMyModel(context);
Get current triggered frame aspects of the model.
See also AllListener@main.dart.
final model = context.getModel<MyModel>();
final aspects = model.frameAspect;
13. subscribe with a single aspect #
For example, listen to a string type aspect of 'int1'
.
original form
class Int1Listener extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Subscriber<MyModel>(
aspects: 'int1',
create: (context, model) {
return Text('Int1 is ${model.int1}');
},
);
}
}
with helper
class Int1Listener extends StatelessWidget {
@override
Widget build(BuildContext context) {
// return 'int1'.subMyModel( // with model helper on your own
return 'int1'.subModel<MyModel>( // with package helper
create: (context, model) {
return Text('Int1 is ${model.int1}');
},
);
}
}
14. subscribe with more than one aspect #
Putting aspects in a list to subscribe more than one aspect.
original form
class TwoListener extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Subscriber<MyModel>(
aspects: ['int1', 'star'],
create: (context, model) {
return Text('Int1 is ${model.int1} and Star is ${model.star}');
},
);
}
}
with helper
class TwoListener extends StatelessWidget {
@override
Widget build(BuildContext context) {
// return ['int1', 'star'].subMyModel( // with model helper on your own
return ['int1', 'star'].subModel<MyModel>( // with package helper
create: (context, model) {
return Text('Int1 is ${model.int1} and Star is ${model.star}');
},
);
}
}
15. subscribe with all aspects #
Provide no aspects parameter, or use null as aspect to listen to all aspects.
See also AllListener@main.dart.
original form
class AllListener extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Subscriber<MyModel>(
// aspects: , // no aspects parameter means listen to all aspects
create: (context, model) {
final aspects = model.frameAspect;
final str = aspects == null ? '' : '$aspects received';
return Text(str);
},
);
}
}
with helper
class AllListener extends StatelessWidget {
@override
Widget build(BuildContext context) {
// return null.subMyModel( // with model helper on your own
return null.subModel<MyModel>( // with package helper
create: (context, model) {
final aspects = model.frameAspect;
final str = aspects == null ? '' : '$aspects received';
return Text(str);
},
);
}
}
16. subscribe with enum aspects #
You can use enum as aspect type. For example, first, define the enum.
ListModel.dart
enum ListEnum {
ListUpdate,
}
Then everything is the same as string type aspect just to replace the string with enum.
See also ListItemView@main.dart.
original form
class ListItemView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Subscriber<ListModel>(
aspects: ListEnum.ListUpdate,
create: (context, model) {
//...
});
}
}
with helper
class ListItemView extends StatelessWidget {
@override
Widget build(BuildContext context) {
// return ListEnum.ListUpdate.subListModel(create: (context, model) { // with model helper on your own
return ListEnum.ListUpdate.subModel<ListModel>(create: (context, model) { // with package helper
//...
});
}
}
17. writing custom rx class #
If you need to write your own rx class, see custom_rx_class.dart for example.
Changelog #
Please see the Changelog page.