Flutter Mediator Simple
Flutter Mediator Simple | A rework of the Flutter Mediator, simple, efficient and easy to use. |
Flutter Mediator Simple is a state management package for flutter. Simple, efficient and easy to use.
Table of Contents
- Flutter Mediator Simple
- Table of Contents
- Getting started
- Usage
- Use Case 1: Mediator Variable of type
Int
- Use Case 2: Mediator Variable of type
List
- Use Case 3: Indirect use of Mediator Variable and Persistence
- Use Case 4: Computed Mediator Variable
subscribe
notify
Signal
subscriberContext
- VS Code Snippet
- State Management with Animation
- Changelog
- License
Getting started
Run this command:
With Flutter:
$ flutter pub add flutter_mediator_simple
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):
dependencies:
flutter_mediator_simple: "^1.2.1"
Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.
Import it
Now in your Dart code, you can use:
import 'package:flutter_mediator_simple/flutter_mediator_simple.dart';
For help getting started with Flutter, view the online documentation.
Usage
-
Declare variable which needs to be managed with
.rx
suffix, to make it a mediator variable.
Suggest to put mediator variables into the file var.dart and then import it(with show, hide capability of the import). -
Create
Subscriber
widgets. Any mediator variables used inside theSubscriber
widget will automatically rebuild the widget when updated.
Use Case 1: Mediator Variable of type Int
Step 1: Declare the mediator variable _int1
in var.dart.
/// Mediator Variable: int1
final _int1 = 0.rx; // or `final _int1 = Rx(0);`
int get int1 => _int1.value;
set int1(value) => _int1.value = value;
// optional
get int1Subscribe => _int1.subscribe;
String get int1Notify => _int1.notify;
Step 2: Create a Subscriber
widget using int1
which is _int1.value
.
Subscriber(
() {
return Text(
'int1: $int1', // using the `_int1` mediator variable
style: Theme.of(subscriberContext).textTheme.headlineMedium,
);
},
),
Update int1
will rebuild corresponding Subscriber widgets automatically.
@override
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: 0,
length: myTabs.length,
child: Scaffold(
// ...
floatingActionButton: FloatingActionButton(
onPressed: () {
switch (_tabController.index) {
case 0:
int1++;
break;
case 1:
int2++;
break;
case 2:
int3++;
break;
case 3:
updateListItem();
break;
case 4:
int1++;
break;
default:
}
},
// ...
),
),
);
Use Case 2: Mediator Variable of type List
Step 1: Declare the mediator variable _data
in var.dart.
/// Mediator Variable: data
final _data = <ListItem>[].rx; //or `RxList(<ListItem>[]);`
List<ListItem> get data => _data.value;
set data(List<ListItem> value) => _data.value = value;
SubscriberFn get dataSubscribe => _data.subscribe;
List<ListItem> get dataNotify => _data.notify;
Step 2: Create a Subscriber
widget using data
which is _data.value
.
return Scaffold(
appBar: AppBar(title: const Text('List Demo')),
//* Step: Create Subscriber widget
body: Subscriber(
() => GridView.builder(
itemCount: data.length, // using the `_data` mediator variable
// ...
Step 3: Implement an update function for data
.
void updateListItem() {
final units = Random().nextInt(maxUnits) + 1;
final itemIdx = Random().nextInt(itemNames.length);
final itemName = itemNames[itemIdx];
final color = itemColors[Random().nextInt(itemColors.length)];
if (data.length >= maxItems) data.clear();
//* Make an update to the collection type mediator variable.
// data.add(ListItem(itemName, units, color));
_data.notify.add(ListItem(itemName, units, color)); // notify the widget to rebuild
}
Use notify
to notify the Subscribe widget to rebuild when the type of the mediator variable is a class and add items by method.
Use Case 3: Indirect use of Mediator Variable and Persistence
Indirect use means the Subscriber widget doesn't use the value of the mediator variable but depends on it.
Step 1: Install i18n package flutter_i18n and follow the instructions to set it up.
Step 1-1: Setup locale delegates in main.dart.
return MaterialApp(
// ...
// add flutter_i18n support
localizationsDelegates: [
FlutterI18nDelegate(
translationLoader: FileTranslationLoader(
forcedLocale: Locale(locale),
// useCountryCode: true,
fallbackFile: 'en',
basePath: 'assets/flutter_i18n',
decodeStrategies: [JsonDecodeStrategy()],
),
missingTranslationHandler: (key, locale) {
// ignore: avoid_print
print(
'--- Missing Key: $key, languageCode: ${locale!.languageCode}');
},
),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
);
Step 1-2: Add assets in pubspec.yaml and prepare locale files in that folder
# pubspec.yaml
flutter:
assets:
- assets/flutter_i18n/
Step 2: Install the persistence package shared_preferences
Step 3: Declare the mediator variable _locale
and SharedPreferences in var.dart.
/// Locale page
late SharedPreferences prefs; // for persistence
// Mediator Variable: locale
late Rx<String> _locale;
String get locale => _locale.value;
// set locale(String value) => _locale.value = value; // comment out
get localeSubscribe => _locale.subscribe;
String get localeNotify => _locale.notify;
Step 4: Implement a function to initial the persistence.
initPersistence() async {
WidgetsFlutterBinding.ensureInitialized(); // for shared_Preferences
prefs = await SharedPreferences.getInstance();
final loc = prefs.getString("locale") ?? 'en'; // default locale to 'en'
_locale = loc.rx; // make _locale a mediator variable of type `Rx<String>`
}
Step 5: Implement setter of _locale
to use persistence.
set locale(String value) {
_locale.value = value;
prefs.setString("locale", value);
}
Step 6: Implement a function to change locale.
/// Change the locale, by `String`[countryCode]
Future<void> changeLocale(BuildContext context, String countryCode) async {
if (countryCode != locale) {
final loc = Locale(countryCode);
await FlutterI18n.refresh(context, loc);
locale = countryCode; // Rebuild associate Subscriber widgets.
}
}
Step 7: Initial the persistence in main.dart.
void main() async {
await initPersistence();
runApp(const MyApp());
}
Step 8(optional): Implement extensions on String
to help building Subscriber
widget of locale in var.dart.
extension StringI18n on String {
/// String extension for i18n.
String i18n(BuildContext context) {
return FlutterI18n.translate(context, this);
}
/// String extension for i18n
/// use `_locale.subscribe` to create Subscriber widget of locale.
Widget sub18n(BuildContext context, {TextStyle? style}) {
return _locale.subscribe(
() => Text(FlutterI18n.translate(context, this), style: style),
);
}
}
Step 9: Create Subscriber
widgets of locale in main.dart.
Widget localeTxt(BuildContext context, String name) {
return SizedBox(
width: 250,
child: Row(
children: [
//* Step: Create a Subscriber widget
localeSubscribe(() => Text('${'app.hello'.i18n(context)} ')),
Text('$name, '),
//* Or use the string extension `sub18n`(in var.dart)
'app.thanks'.sub18n(context),
],
),
);
}
Step 10: Handle radio status to change locale in class _RadioGroupState
at main.dart.
Future<void> _handleRadioValueChange1(Object? value) async {
await changeLocale(context, value!.toString());
setState(() {});
}
Use Case 4: Computed Mediator Variable
Step 1: Declare the computed mediator variable _sum
with a computed function of compound of mediator variables in var.dart.
Specify the return type of the computed function as dynamic if the return type along with the function will change.
/// Computed Mediator Variable: sum
final _sum = Rx(() => int1 + int2 + int3 as dynamic);
get sum => _sum.value;
set sum(value) => _sum.value = value;
Step 2: Create a Subscriber
widget using sum
which is _sum.value
.
Subscriber(
() {
return Text(
'sum(computed): $sum',
style: Theme.of(subscriberContext).textTheme.headlineLarge,
);
},
),
Step 2a(Optional): Change the computed function when needed.
floatingActionButton: FloatingActionButton(
onPressed: () {
if (sum is int && sum >= 10) {
sum = () => "excess upper bound($sum)";
}
// ...
}
),
Or, combine into the computed function.
final _sum = Rx(() {
final res = int1 + int2 + int3;
if (res <= 10) {
return res;
}
return "excess upper bound($res)";
});
subscribe
To create a Subscriber widget for indirect use of the mediator variable.
Indirect use means the Subscriber widget doesn't use the value of the mediator variable but depends on it.
For example, the locale use case.
notify
Notify to rebuild with the aspects of the mediator variable.
Used when the type of the mediator variable is a Class
.
Mediator variable uses setter to automatically notify the rebuild. When it comes to a class value and adds item by method, then notify
is needed to inform the rebuild.
Signal
Mediator variables can be initialled by the Signal
annotation, through type alias.
For example,
final _int1 = 0.signal;
final _int2 = Signal(0);
final _int3 = Signal(0);
// computed mediator variable
final _sum = Signal(() => int1 + int2 + int3);
subscriberContext
Can get the context of Subscriber through the getter subscriberContext
.
For example:
Subscriber(
() {
return Text(
'int1: $int1',
style: Theme.of(subscriberContext).textTheme.headlineMedium,
);
},
),
VS Code Snippet
Use VS Code snippet to help typing the boilerplates. Take for example snippet_Flutter_Mediator__statelessful.code-snippets,
getset
- Getter/Setter for Mediator Variablecgetset
- Getter/Setter for Computed Mediator Variablesub1
- Create a Subscriber Widget (Arrow Function)subs
- Create a Subscriber Widget (Multiline)
{
"Getter/Setter for Mediator Variable": {
"prefix": "getset",
"body": [
"/// Mediator Variable: ${1:var}",
"final _${1:var} = ${2:initialValue}.rx;",
"${3:type} get $1 => _${1:var}.value;",
"set $1(${3:type} value) => _${1:var}.value = value;",
"SubscriberFn get ${1:var}Subscribe => _${1:var}.subscribe;",
"${3:type} get ${1:var}Notify => _${1:var}.notify;",
"$0"
],
"description": "Getter/Setter for Mediator Variable"
},
"Getter/Setter for Computed Mediator Variable": {
"prefix": "cgetset",
"body": [
"/// Computed Mediator Variable: ${1:var}",
"final _${1:var} = Rx(() => ${2:initialValue});",
"get $1 => _${1:var}.value;",
"set $1(value) => _${1:var}.value = value;",
"$0"
],
"description": "Getter/Setter for Computed Mediator Variable"
},
"Create a Subscriber Widget (Arrow Function)": {
"prefix": "sub1",
"body": [
"${1:Subscriber}(",
"\t() => ${2:Text}(\"$3\"),",
"),",
"$0"
],
"description": "Create a Subscriber Widget (Arrow Function)"
},
"Create a Subscriber Widget (Multiline)": {
"prefix": "subs",
"body": [
"${1:Subscriber}(",
"\t() {",
"\t\treturn ${2:Text}(\"$3\");",
"\t},",
"),",
"$0"
],
"description": "Create a Subscriber Widget (Multiline)"
},
}
State Management with Animation
By using flutter_animate animation can easily add to the mediator variable. If animation is needed every time the mediator variable changes, just add a ValueKey
to the animate
. For example, example/lib/main.dart
Subscriber(
() {
return Text(
'int1: $int1', // using the `_int1` mediator variable
style: Theme.of(subscriberContext).textTheme.headlineMedium,
)
.animate(key: ValueKey(int1))
.fade(duration: 125.ms)
.scale(delay: 125.ms);
},
Changelog
Please see the Changelog page.
License
Flutter Mediator Simple is distributed under the MIT License. See LICENSE for more information.