Flutter MVVM 开发范式

基于Provider状态管理用来实现MVVM框架,采用的是 ViewModel 继承于 ChangeNotifier,数据请求,数据处理在ViewModel中进行处理,然后再通过 ChangeNotifierProvider 提供给子Widget数据,ViewModel数据刷新通过调用 notifyListeners() 来通知Widget进行刷新,Widget 通过 Provider.of 、Consumer、Selector 来监听数据变化重新 build 从而达到更新UI的目的

1.文件结构

建对应项目的总文件目录,下面放api(存放数据请求),model(模型),viewModel(数据处理),widget(该项目公共或封装的控件),page(页面)

346E0782-6BDE-45D4-BEE5-29E8265A37D5.png

可以新建一个page文件,将页面都放在page文件里面

2.构建viewModel文件,继承于 ChangeNotifier,使用provider进行状态管理

在viewModel中声明要用到的属性,声明数据状态改变之后界面会刷新的方法,同时在里面进行网络请求管理,获取请求数据,在回调中处理对应的请求数据,在需要刷新界面的地方调用notifyListeners()方法

class FeedbackHomeViewModel extends ChangeNotifier{
  FeedbackList processingList;
  FeedbackList resolvedList;

  int _processPage = 0;
  int _resolvedPage = 0;

  List typeList;

  String customerServiceTel;

  bool hasMore(int status) {
    return status == STATUS_PROCESSING
        ? (_processPage * 10 < (processingList?.total ?? 0))
        : (_resolvedPage * 10 < (resolvedList?.total ?? 0));
  }

  Future refresh(int status) {
    status == STATUS_PROCESSING ? _processPage = 0 : _resolvedPage = 0;
    return _fetchFeedbackList(status, pageNo: 1).then((_) {
      status == STATUS_PROCESSING ? _processPage ++ : _resolvedPage ++;
    });
  }

  Future loadMore(int status) {
    var page = status == STATUS_PROCESSING ? _processPage : _resolvedPage;
    return _fetchFeedbackList(status, pageNo: page + 1).then((_) {
      status == STATUS_PROCESSING ? _processPage ++ : _resolvedPage ++;
    });
  }

  Future _fetchFeedbackList(int status, {int pageNo = 1, int pageSize = 10}) {
    return FeedbackApi.fetchFeedbackList(status, pageNo: pageNo, pageSize: pageSize)
      .then((json) {
        var resultList = FeedbackList.fromJson(json);
        var feedbackList = status == STATUS_PROCESSING ? processingList : resolvedList;

        if(pageNo > 1) {
          resultList.data.insertAll(0, feedbackList.data);
        }

        status == STATUS_PROCESSING ? processingList = resultList : resolvedList = resultList;

        notifyListeners();
    }).catchError((error) {
      print(error);
    });
  }

  Future fetchHotLine() {
    return FeedbackApi.fetchHotline().then((value) {
      customerServiceTel = HotLine.fromJson(value).customerServiceTel;
      notifyListeners();
    }).catchError((error) => print(error));
  }

  void fetchFeedbackType() async {
    return FeedbackApi.fetchFeedbackType()
        .then((json) {
          typeList = json.map((value) => FeedbackType.fromJson(value)).toList();
          notifyListeners();
    });
  }
}

如果provider数据需要从page页面传入的时候,或者是本地数据需要进行修改的时候,同样声明私有,可以声明一个对应的方法,修改属性值。

class CounterModel with ChangeNotifier {
  int _count = 0;
  int get value => _count;
    // // ///选中的执行人
  TaskEmployeModel _taskSelectExecutor;

  void increment() {
    _count++;
    notifyListeners();
  }

  TaskEmployeModel get taskSelectExecutor => _taskSelectExecutor;
  set taskSelectExecutor(TaskEmployeModel value) {
    _taskSelectExecutor = value;
    notifyListeners();
  }

}

3.page界面的使用

初始化并获取请求数据

final _viewModel = FeedbackHomeViewModel();

  @override
  void initState() {
    super.initState();
    _viewModel.fetchFeedbackType();
    _viewModel.fetchHotLine();
    _viewModel.refresh(STATUS_PROCESSING);
    _viewModel.refresh(STATUS_RESOLVED);
  }

在创建页面的时候,采用provider进行管理,create传入对应的viewmodel

 Widget build(BuildContext context) {
    return ChangeNotifierProvider(
        create: (_) => _viewModel,
        child: Scaffold(
            body: Container()));
  }

读状态的时候有Consumer和Selector模式,Consumer是监听provider中的所有数据,只要provider中的数据发生了改变,就会重新进行build,然后刷新界面;Selector可以更加细致化的监听provider中对应的具体的数据,只有当对应的具体数据发生改变的时候,才会重新build,刷新界面,这边推荐使用Selector来读取数据,更加细致化的管理,降低无效build次数

Consumer获取数据

Consumer(
        builder: (_, provider, __) {
          return Column(
            children: [
              Expanded(
                child: ListView.builder(
                    itemCount: provider?.warehouseList?.length ?? 0,
                    itemBuilder: (BuildContext context, int index) {
                      var brand = provider.warehouseList[index];
                      return ListTile(
                        onTap: () {
                          widget.onSelected(brand);
                        },
                        title: Text(brand.warehouseName),
                      );
                    }),
              ),
            ],
          );
        },
      ),

Selector读取数据

其中Selector中的<>需要指定对应的provider和监听的数据类型,否则会报错

     Selector(
            builder: (_, value, __) => _buildTaskType(context),
            selector: (_, taskCreateProvider) =>
                taskCreateProvider.taskSelectType),

修改状态

 FloatingActionButton(
    onPressed: () =>
        Provider.of(context, listen: false).increment(),
  ),

 onRefresh: () async {
     Provider.of(context, listen: false)
     .refresh(status);
 },

多个provider的时候,在build初始化的时候,可以选择使用MultiProvider进行管理操作

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
        providers: [
          ChangeNotifierProvider(
            create: (context) => model1,
          ),
          ChangeNotifierProvider(
            create: (context) => model2,
          )
        ],
        child: Consumer2(
          builder: widget.builder,
          child: widget.child,
        ));
  }
}

其中model1 和model2 是对应的provider管理,Consumer2和Selector2,支持两个provider,最多能支持到6个

这样使用了MVVM模式,在page页面只要使用对应的数据即可,在viewModel中将对应的数据处理好,或者提供改变数据状态的方法,是的可以更好的管理数据状态,更加的清晰易读,在复杂的场景的可以更好的处理逻辑

image.png

你可能感兴趣的:(Flutter MVVM 开发范式)