欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MVVM

程序员文章站 2022-07-13 23:10:15
...

MVVM in Flutter using Dart Streams

本文将展示MVVM在Flutter中的代码实现。我们将使用Dart的Stream API创建函数响应式的ViewModel。

MVVM(Model-View-ViewModel)

MVVM的主要目标就是尽可能将View里的状态及逻辑分离出来,移到ViewModel实体里。ViewModel同样包含业务逻辑,这样ViewModel就相当于一个介于View和Model的中间层。

ViewModel有两个基本的只能:

  • 响应用户的输入(比如改变模型,初始化网络请求或者路由到其他页面)。
  • 提供数据的输出,这些数据View可以订阅。

View不包含任何业务逻辑。View的只能如下:

  • 响应ViewModel新的输出状态,并且渲染出与之相应的界面(比如显示一个字符串或者文本框等)。
  • 通知ViewModel用户新的输入(比如按钮点击,文本改变,屏幕触摸等)。

##Flutter中的MVVM
Flutter中,Widget表示MVVM模式中的View。逻辑代码包含在ViewModeel类中。ViewModel是完全平台无关的,对Flutter没有依赖,因此,在一个Web项目中服用也比较容易。

Example:邮件订阅Widget

例子:实现一个简单注册表单,由一个输入框和一个提交按钮组成。邮件地址非法的话,按钮不可操作,用户也可以看到一个错误提示。
视图逻辑,视图状态,业务逻辑全部混在一起。将导致一下问题:
1.难以进行单元测试。
2.难以复用,其它的Dart项目不能复用业务逻辑,因为它混入了Flutter相关的视图逻辑。
3.代码庞大混乱,当Widget变得巨大时会比较凌乱。

使用MVVM解决

前面解释过,ViewModel有输入输出。所以我们将添加输入输出。
所有的输入都是Skins。View可以通过它向ViewModel添加数据。
所有的输出都是Streams。View可以通过订单这些流监听改变。

ViewModel的接口如下:


	abstract class SubscriptonViewModel {
		Sink get inputMailText;
		Stream<bool> get outputIsButtionEnabled;
		Stream<String> get outputErrorText;
	
		void dispose();
	}

可以使用StreamController作为输入。可以在内部使用StreamController提供的流处理这些输入事件。

	class SubscriptionViewModelImpl implements SubscriptionViewModel {
		var _mailTextController = StreamController<String>.broadcast();

		@override
		Skin get inputMailText => _mailTextController;

		@override
		Stream<bool> get outputIsButtonEnable => _mailTextController.stream.map((email) => EmailValidator.isEmailValid(email));

		@override
		Stream<String> get outputErrorText => outputIsButtonEnabled.map((isEnabled) => isEnabled ? null : "Invalid email");

		@override
		void dispose() => _mailTextController.close();
	}

绑定View到ViewModel

怎样提供输入及处理输出事件?

通过给ViewModel的Skins添加一个输入值,这样就给ViewModel提供了输入。将Widget绑定到ViewModel。这个例子中,当文本框文本改变后,我们添加该文本到ViewModel的Sinks。


	@override
	void initState() {
		controller.addListener(() => _viewModel.inputMailText.add(controller.text));
		super.initState();
	}

需要通过订阅Output-Streams监听ViewModel的输出。

Flutter提供了一种Widget叫StreamBuilder,当一个Stream提供了新值时它可以自动更新。不再需要调用"setState"。

StreamBuilder的"build"方法给我们提供了快照。快照包含于流的信息,数据及错误。如果流没有发射任何值,数据将是null。


	StreamBuilder<String>(
		stream: _viewModel.outputErrorText,
		build: (context, snapshot) {
			return MyTextField(
				controller: controller,
				hintText: "Email",
				errorText: snapshot.data);
		}
	)  

在使用Stream时为了帮助Dart编译器,避免你运行时错误,要为Stream添加所有需要的类型。

全部代码:


	@override
	Widget build(BuildContext context) {
		return Row(
			mainAxisAlignment: MainAxisAlignment.center,
			children: <Widget>[
				Expanded(
					child: StreamBuilder<String>(
						stream: _viewModel.outputErrorText,
						builder: (context, snapshot) {
							return MyTextField(
								controller: controller,
								hintText: "Email",
								errorText: snapshot.data);
						}
					)
				),
				StreamBuilder(
					stream: _viewModel.outputIsButtonEnabled,
					builder: (context, snapshot) {
						return SubmitButton(enabled: snapshot.data ?? false);
					}
				)
			]
		);
	}

在复杂任务中可以使用RxDart。

相关标签: MVVM

上一篇: Checkbox 多选框

下一篇: 点击结账.