mvvm 0.1.3 mvvm: ^0.1.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) 变化。
#
import 'package:flutter/widgets.dart';
import 'package:mvvm/mvvm.dart';
import 'dart:async';
// define ViewModel
class Demo1ViewModel extends ViewModel {
Demo1ViewModel() {
// define bindable property
property<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}");
});
}
}
// define 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());
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
被适配者实例,适配者必须继承自Listenable
getAdapteeValue
指定从被适配者获取值的方法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
指定当请求成功时对结果处理的方法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"))]);
}
}