likes
comments
collection
share

Flutter 使用 MVC 架构如何做状态管理?

作者站长头像
站长
· 阅读数 8

前言

众所周知,在 Java 后端普遍所采用的 Spring 框架中,无论是 Controller,还是 Service,都是通过注入到容器中,在哪里用就从哪里创建一个成员变量,然后使用注解 @Autowire 实现自动化引用。

但是,在 Flutter 中,因为无法使用反射,所以就很难实现这么方便的自动注入框架了。

并且,在 Flutter 中,Controller 和 Service 与 View 之间的关系,基本上是复杂树形嵌套结构的。

这就有点难受了🤣。

先引入状态管理的概念,然后再来看看,怎么整合咱们的 Controller 和 Service。

状态管理

对于状态管理,官方文档给出一个言简意赅的公式:UI = f(state),也就是说,界面 UI 的渲染是一个函数,这个函数的参数是状态 state,即 UI 显示与状态参数 state 有关系,而状态管理就是对状态参数 state 进行管理。

另外可以思考一下, Flutter 为啥取名 Flutter?

Flutter 使用 MVC 架构如何做状态管理?

正如 Flutter 这个单词的的含义一样,界面是多变的,每一帧画面都来自于状态参数,例如,用户界面中页面是否登录,需要不同的状态参数。

一个需要根据状态动态变化的界面,Flutter 官方给出了 StatuefulWidget 动态组件,通过继承实现 StatefulWidget 中的 createState 方法,返回一个 State 对象,然后在 State 对象中实现具体的 buildWidget 方法,返回我们设计好的的 Widget 界面组件"嵌套地狱"。

状态更新

Flutter 中的 Widget 是多种多样的,这里引入一个 TextButton 组件(TextButton 继承 Widget)作为切入点,来理解状态更新。

代码如下:

import 'package:flutter/material.dart';

class CustomView extends StatefulWidget {
  const CustomView({Key? key}) : super(key: key);

  @override
  State<CustomView> createState() => _CustomViewState();
}

class _CustomViewState extends State<CustomView> {
  var count = 0;

  void addCount() {
    setState(() {
      count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text("点击次数: $count"),
        TextButton(
          onPressed: () {
            addCount();
          },
          child: Text("点我"),
        ),
      ],
    );
  }
}

这个代码实现了一个效果,即点击文字按钮"点我",界面就会更新点击次数。

其实现方式是通过调用 setState 方法,来通知界面更新状态。

在 setState 方法的参数中传入一个闭包函数,用来更新状态参数 "count"。

每个动态视图,都有对应状态参数,视图和参数组合成一个动态组件,在其它组件里面可以引用这个组件,可以传入参数变量。

嵌套结构中的状态管理

我刚学习 Flutter 时,就产生一个疑问,对于嵌套的视图,父视图和其子内容视图之间应该如何交互数据呢?

答案是通过 BuildContext 中的方法 findAncestorStateOfType 来访问父组件的状态,如下所示:

Widget build(BuildContext context) {
    var parentState = context.findAncestorStateOfType<ParentWigetState>();
    ...
}

即 Flutter 渲染系统会通过 build 方法中的 BuildContext 参数给你个 context 对象,你可以通过这个 context 来获取上级视图中的状态参数 ParentWidgetState 实例化对象。

在嵌套结构中使用MVC

是时候解决之前抛出的问题了,在我们之前设计好的 MVC 架构中,Controller 应该如何构建?

其实很简单, 随着"嵌套地狱"的入参传入 Controller,也就是在 Widget 的构造参数中,构造 Controller。

我们还需要解决这个问题,我们的 Controller 应该如何应对这种嵌套结构呢?

我们只需要在 Controller 中,得到 BuildContext 实例化对象 context,就可以查询得到上级状态参数 state,通过上级 state 得到上级 widget,进而得到上级视图中的的 Controller。

import 'package:flutter/material.dart';

class ChildController extends MvcController { 
  late ParentController parentController;
  @override
  void onInitState(BuildContext context) {
    super.onInitState(context);
    var parentState = context.findAncestorStateOfType<ParentWidgetState>();
    parentController = parentState.widget.controller;
  }
}

当然,我不建议这么做,如何要用到 ParentController,不如直接在 ChildeController 构造方法中入参包含 ParentController。

import 'package:flutter/material.dart';

class ChildController extends MvcController { 
  ParentController parentController;
  ChildController({required this.parentController});
}

class ParentController extends MvcController{
  late ChildController childController;

  @override
  void onInitState(BuildContext context) {
    super.onInitState(context);
    childController = ChildController(parentController: this);
  }
}

也就是说,直接嵌套 Controller 就行了。

只是存在一个疑问,动态的界面,如何动态的创建 Controller?

答:如果是动态的界面,比如用户点击事件事件,可以通过这个事件绑定的回调方法,来调用 Controller 中的方法,动态更新 Controller 中的参数。

别忘了,还有一个疑问,Service 应该在哪里构建?

Service 一般就可以使用单例模式了,当然也可以通过某个顶级组件 ServiceManagerWidget 中的 ServiceManagerState 来构建 ServiceManager,然后在 ServiceManager 中来构建各种各样的 Service,这就是我的作品《温知笔记APP》中所采用的方式了。

结语

这期文章讲解了 flutter 中的状态管理,通过状态管理来管理界面所需要的各种参数,然后通过 MVC 架构中的 Controller 来接管状态参数。

好了,现在可以转换一下公式了,UI = MVC。

感谢大家的点赞和关注,每个点赞和关注,都是对我最大的支持~