mvvm 0.1.3+3
mvvm: ^0.1.3+3 copied to clipboard
A Flutter MVVM. It uses property-based data binding to establish a connection between the ViewModel and the View, and drives the View changes through the ViewModel.
A Flutter MVVM (Model-View-ViewModel) implementation. It uses property-based data binding to establish a connection between the ViewModel and the View, and drives the View changes through the ViewModel.
一个 Flutter 的 MVVM(Model-View-ViewModel) 实现。 它使用基于属性 (property) 的数据绑定在视图模型 (ViewModel) 与视图 (View) 之间建立关联,并通过视图模型 (ViewModel) 驱动视图 (View) 变化。
#
Documentation & Full example
import 'package:flutter/widgets.dart';
import 'package:mvvm/mvvm.dart';
import 'dart:async';
// ViewModel
class Demo1ViewModel extends ViewModel {
Demo1ViewModel() {
// define bindable property
propertyValue<String>(#time, initial: "");
// timer
start();
}
start() {
Timer.periodic(const Duration(seconds: 1), (_) {
var now = DateTime.now();
// call setValue
setValue<String>(#time, "${now.hour}:${now.minute}:${now.second}");
});
}
}
// View
class Demo1 extends View<Demo1ViewModel> {
Demo1() : super(Demo1ViewModel());
@override
Widget buildCore(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 100),
padding: EdgeInsets.all(40),
// binding
child: $.watchFor(#time,
builder: $.builder1((t) =>
Text(t, textDirection: TextDirection.ltr))));
}
}
// run
void main() => runApp(Demo1());
[mvvm]
APIs #
ViewContext ($.*) #
Methods
watch<TValue>(ValueListenable<TValue> valueListenable, { ValueWidgetBuilder<TValue> builder, Widget child }) → Widget
绑定到指定 valueListenable, 当 valueListenable 值发生变化时, 使用 builder 构建 Widget
child用于向构建方法中传入Widget
// example
@override
Widget buildCore(BuildContext context) {
return $.watch<String>($Model.prop1,
builder: $.builder1((value) => Text(value)));
}
watchFor<TValue>(Object propertyKey, { ValueWidgetBuilder<TValue> builder, Widget child }) → Widget
绑定到指定属性, 当 propertyKey 对应属性值发生变化时, 使用 builder 构建 Widget
child用于向构建方法中传入Widget
// example
@override
Widget buildCore(BuildContext context) {
return $.watchFor<String>(#account,
builder: $.builder1((value) => Text(value)));
}
watchAny(Iterable<ValueListenable> valueListenable, { ValueWidgetBuilder<Iterable> builder, Widget child }) → Widget
绑定到指定 valueListenable 集合, 当任一 valueListenable 值发生变化时, 使用 builder 构建 Widget
builder方法中TValue将被包装为Iterable<dynamic>child用于向构建方法中传入Widget
// example
@override
Widget buildCore(BuildContext context) {
return $.watchAny([$Model.prop1, $Model.prop2],
builder: $.builder1((values) => Text(values[0])));
}
watchAnyFor(Iterable<Object> prepertyKeys, { ValueWidgetBuilder<Iterable> builder, Widget child }) → Widget
绑定到指定属性集合, 当任一 propertyKeys 对应属性值发生变化时, 使用 builder 构建 Widget
builder方法中TValue将被包装为Iterable<dynamic>child用于向构建方法中传入Widget
// example
@override
Widget buildCore(BuildContext context) {
return $.watchAnyFor(const [#account, #password],
builder: $.builder1((values) => Text(values[0])));
}
builder0(Widget builder()) → ValueWidgetBuilder
生成 Widget 构建方法
- 通过
builder指定一个无参的Widget构建方法
// example
@override
Widget buildCore(BuildContext context) {
return $.watch<String>($Model.prop1,
builder: $.builder0(() => Text("hello!")));
}
builder1<TValue>(Widget builder(TValue)) → ValueWidgetBuilder<TValue>
生成 Widget 构建方法
- 通过
builder指定一个接收TValue的Widget构建方法
// example
@override
Widget buildCore(BuildContext context) {
return $.watch<String>($Model.prop1,
builder: $.builder1((value) => Text(value)));
}
builder2<TValue>(Widget builder(TValue, Widget)) → ValueWidgetBuilder<TValue>
生成 Widget 构建方法
- 通过
builder指定一个接收TValue,Widget的Widget构建方法
// example
@override
Widget buildCore(BuildContext context) {
return $.watch<String>($Model.prop1,
builder: $.builder2((value, child) => Column(children:[Text("$value"), child]),
child: Text("child"));
}
$cond<TValue>(ValueListenable<TValue> valueListenable, { ValueWidgetBuilder<TValue> $true, ValueWidgetBuilder<TValue> $false, Widget child, bool valueHandle(TValue) }) → Widget
绑定到指定 valueListenable, 当 valueListenable 值发生变化时, 若值判定结果为 true 则使用 $true 构建 Widget, 否则使用 $false 构建 Widget
- 当值类型不为
bool时, 非null即被判定为true, 否则判定为false - 可通过指定
valueHandle对值进行处理 child用于向构建方法中传入Widget
// example
@override
Widget buildCore(BuildContext context) {
return $.$cond<int>($Model.prop1,
$true: $.builder0(() => Text("tom!")),
$false: $.builder0(() => Text("jerry!")),
valueHandle: (value) => value == 1);
}
$condFor<TValue>(Object propertyKey, { ValueWidgetBuilder<TValue> $true, ValueWidgetBuilder<TValue> $false, Widget child, bool valueHandle(TValue) }) → Widget
绑定到指定属性, 当 propertyKey 对应属性值发生变化时, 若值判定结果为 true 则使用 $true 构建 Widget, 否则使用 $false 构建 Widget
- 当值类型不为
bool时, 非null即被判定为true, 否则判定为false - 可通过指定
valueHandle对值进行处理 child用于向构建方法中传入Widget
// example
@override
Widget buildCore(BuildContext context) {
return $.$condFor<String>(#account,
$true: $.builder0(() => Text("tom!")),
$false: $.builder0(() => Text("jerry!")),
valueHandle: (value) => value == "tom");
}
$if<TValue>(ValueListenable<TValue> valueListenable, { ValueWidgetBuilder<TValue> builder, Widget child, bool valueHandle(TValue) }) → Widget
绑定到指定 valueListenable, 当值发生变化时, 若值判定结果为 true 时使用 builder 构建 Widget 否则不构建 Widget
- 当值类型不为
bool时, 非null即被判定为true, 否则判定为false - 可通过指定
valueHandle对值进行处理 child用于向构建方法中传入Widget
// example
@override
Widget buildCore(BuildContext context) {
return $.$if<int>($Model.prop1,
builder: $.builder0(() => Text("tom!")),
valueHandle: (value) => value == 1);
}
$ifFor<TValue>(Object propertyKey, { ValueWidgetBuilder<TValue> builder, Widget child, bool valueHandle(TValue) }) → Widget
绑定到指定属性, 当 propertyKey 对应属性值发生变化时, 若值判定结果为 true 时使用 builder 构建 Widget 否则不构建 Widget
- 当值类型不为
bool时, 非null即被判定为true, 否则判定为false - 可通过指定
valueHandle对值进行处理 child用于向构建方法中传入Widget
// example
@override
Widget buildCore(BuildContext context) {
return $.$ifFor<String>(#account,
builder: $.builder0(() => Text("tom!")),
valueHandle: (value) => value == "tom");
}
$switch<TKey, TValue>(ValueListenable<TValue> valueListenable, { Map<TKey, ValueWidgetBuilder<TValue>> options, ValueWidgetBuilder<TValue> defalut, Widget child, TKey valueToKey(TValue) }) → Widget
绑定到指定 valueListenable, 当 valueListenable 值发生变化时, 其值做为 key 到 options 中查找对应 Widget 构建方法, 若未找到则使用 default 构建, 如 default 为 null 则不构建 Widget
- 如值与
options中key类型不同, 可通过指定valueToKey进行转换 child用于向构建方法中传入Widget
// example
@override
Widget buildCore(BuildContext context) {
return $.$switch<String, int>($Model.prop1,
options: { "1.": $.builder1((value) => Text("$value")),
"2.": $.builder0(() => Text("2")) },
default: $.builder0(() => Text("default")),
valueToKey: (value) => "${value}.");
}
$switchFor<TKey, TValue>(Object propertyKey, { Map<TKey, ValueWidgetBuilder<TValue>> options, ValueWidgetBuilder<TValue> defalut, Widget child, TKey valueToKey(TValue) }) → Widget
绑定到指定属性, 当 propertyKey 对应属性值发生变化时, 其值做为 key 到 options 中查找对应 Widget 构建方法, 若未找到则使用 default 构建, 如 default 为 null 则不构建 Widget
- 如值与
options中key类型不同, 可通过指定valueToKey进行转换 child用于向构建方法中传入Widget
// example
@override
Widget buildCore(BuildContext context) {
return $.$switchFor<String, int>(#account,
options: { "tom": $.builder1((value) => Text("${value}! cat")),
"jerry": $.builder0(() => Text("mouse")) },
default: $.builder0(() => Text("default"));
}
ViewModel #
Methods
propertyValue<TValue>(Object propertyKey, { TValue initial }) → ViewModelProperty<TValue>
创建一个值属性
propertyKey指定属性键initial指定初始值
// example
class PageViewModel extends ViewModel {
PageViewModel() {
propertyValue<String>(#name, initial: "tom");
}
}
propertyAdaptive<TValue, TAdaptee extends Listenable>(Object propertyKey, TAdaptee adaptee, TValue getAdapteeValue(TAdaptee), void setAdapteeValue(TAdaptee, TValue), { TValue initial }) → AdaptiveViewModelProperty<TValue, TAdaptee>
创建一个适配属性
propertyKey指定属性键adaptee被适配者实例,适配者必须继承自ListenablegetAdapteeValue指定从被适配者获取值的方法setAdapteeValue指定设置被适配者值的方法initial指定初始值
// example
class PageViewModel extends ViewModel {
final TextEditingController _nameCtrl = TextEditingController();
PageViewModel() {
propertyAdaptive<String, TextEditingController>(
#name, _nameCtrl,
(v) => v.text,
(a, v) => a.text = v,
initial: name);
}
// TextField used
TextEditingController get nameCtrl => _nameCtrl;
}
propertyAsync<TValue>(Object propertyKey, AsyncValueGetter<TValue> futureGetter, { TValue handle(TValue), TValue initial }) → AsyncViewModelProperty<TValue>
创建一个异步请求属性
要使用此属性的 ViewModel 需要 with 到 AsyncViewModelMixin
propertyKey指定属性键futureGetter用于获取Future<TValue>的方法handle指定请求成功时对结果进行处理的方法onStart指定请求发起时执行的方法onEnd指定请求结束时执行的方法onSuccess指定请求成功时执行的方法onError指定请求出错时执行的方法initial指定初始值
// example
class User {
String name;
User(this.name);
}
class RemoteService {
Future<User> findUser() async {
return Future.delayed(
Duration(seconds: 3), () => User("tom_${DateTime.now().second}"));
}
}
class PageViewModel extends ViewModel with AsyncViewModelMixin {
final RemoteService _service;
PageViewModel(this._service) {
propertyAsync<User>(
#findUserAsync,
() => _service.findUser(),
handle: (User user) {
user.name = "hello, ${user.name}";
return user;
});
}
// ViewModel used
find() => invoke(#findUserAsync);
}
class PageView extends View<PageViewModel> {
PageView(): super(PageViewModel(RemoteService()));
@override
Widget buildCore(BuildContext context) {
return Column(children: [
$.watchFor(#findUserAsync,
builder: $.builder2((AsyncSnapshot<User> snapshot, child) =>
snapshot.connectionState == ConnectionState.waiting && snapshot.hasData
? Text("${snapshot.data.name}", textDirection: TextDirection.ltr)
: child),
child: Text("empty", textDirection: TextDirection.ltr)),
RaisedButton(
child: $.watchFor(#findUserAsync,
builder: $.builder2((AsyncSnapshot<User> snapshot, child) =>
snapshot.connectionState == ConnectionState.waiting
? CircularProgressIndicator() : child),
child: Text("find", textDirection: TextDirection.ltr)),
// or: onPressed: () { $Model.find(); }
onPressed: $Model.link(#findUserAsync))]);
}
}
License #
MIT