fast_mvvm 1.1.9+4

  • Readme
  • Changelog
  • Example
  • Installing
  • 65

fast_mvvm #

博客讲解:https://blog.csdn.net/q948182974/article/details/106613565

掘金讲解:https://juejin.im/post/5ee86c9b51882543313a0de7

一个MVVM框架附带简单的demo,会一直更新,希望支持一下.有问题可以反馈QQ 275918180。

Demo 讲解 #

这里模拟了文章列表。 Model:UserModel BaseListViewModel: ArticleVM View:ArticlePage 数据实体类 BaseEntity:ArticleEntity 主要讲解了初始化配置, 状态页效果,根布局刷新,数据获取。

UserModel #

首先创建项目模块所需要用的Model,按大模块区分。 这里创建UserModel

class UserModel extends BaseModel {
  /// 登录
  Future<bool> login(String account, String psd) async {
    await Future.delayed(Duration(seconds: 3));
    return true;
  }

  /// 资讯列表
  Future<DataResponse<ArticleEntity>> getArticleList() async {
    await Future.delayed(Duration(seconds: 2));

    var entity = ArticleEntity([
      ArticleItem("1", "好的", "内容内容内容内容内容", DateTime.now().toString()),
      ArticleItem("1", "好的", "内容内容内容内容内容", DateTime.now().toString()),
    ]);

    DataResponse dataResponse =
        DataResponse<ArticleEntity>(entity: entity, totalPageNum: 3);
    return dataResponse;
  }
}

初始化 #

在APP首页启动的时候初始化框架。 调用initMVVM(),装载UserModel,配置上拉加载下拉刷新; 选择文章页面是否根布局刷新选项,是否配置单独的状态页。


class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  @override
  void initState() {
    initMVVM<BaseViewModel>(
      [UserModel()],
      controllerBuild: () => EasyRefreshController(),
      resetRefreshState: (c) =>
          (c as EasyRefreshController)?.resetRefreshState(),
      finishRefresh: (c, {bool success, bool noMore}) =>
          (c as EasyRefreshController)
              ?.finishRefresh(success: success, noMore: noMore),
      resetLoadState: (c) => (c as EasyRefreshController)?.resetLoadState(),
      finishLoad: (c, {bool success, bool noMore}) =>
          (c as EasyRefreshController)
              ?.finishLoad(success: success, noMore: noMore),
    );
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SelectPage(),
    );
  }
}

class SelectVM extends BaseViewModel {
  ValueNotifier<bool> isLoadData = ValueNotifier(true);
  ValueNotifier<bool> isConfigState = ValueNotifier(false);
}

class SelectPage extends StatelessWidget with BaseView<SelectVM> {
  @override
  ViewConfig<SelectVM> initConfig(BuildContext context) =>
      ViewConfig(vm: SelectVM());

  @override
  Widget vmBuild(
      BuildContext context, SelectVM vm, Widget child, Widget state) {
    return Scaffold(
      appBar: AppBar(title: Text("选择")),
      body: ListView(
        children: <Widget>[
          ListTile(
            title: Text("是否加载数据,用来测试状态页和重新加载数据"),
            trailing: ValueListenableBuilder(
              valueListenable: vm.isLoadData,
              builder: (_, value, __) => Switch(
                value: value,
                onChanged: (value) => vm.isLoadData.value = value,
              ),
            ),
          ),
          ListTile(
            title: Text("是否单独配置状态页,用来测试状态页和重新加载数据"),
            trailing: ValueListenableBuilder(
              valueListenable: vm.isConfigState,
              builder: (_, value, __) => Switch(
                value: value,
                onChanged: (value) => vm.isConfigState.value = value,
              ),
            ),
          ),
          ListTile(
            title: Text("根布局刷新"),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (_) => ArticlePage(
                    true,
                    configState: vm.isConfigState.value,
                    loadData: vm.isLoadData.value,
                  ),
                ),
              );
            },
          ),
          ListTile(
            title: Text("根布局不刷新"),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (_) => ArticlePage(
                    false,
                    configState: vm.isConfigState.value,
                    loadData: vm.isLoadData.value,
                  ),
                ),
              );
            },
          ),
        ],
      ),
    );
  }
}

ArticleEntity #

模拟接口返回的数据实体类:

class ArticleEntity extends BaseEntity {
  List<ArticleItem> list;

  ArticleEntity(this.list);
}

class ArticleItem {
  String id;
  String title;
  String content;
  String time;

  ArticleItem(this.id, this.title, this.content, this.time);
}

ArticleVM #

UserModel 继承 BaseModel,ArticleEntity继承BaseEntity,ArticleItem 暂没要求。 是否首次加载数据测试空状态页的效果 创建vnTime 用来监听List第一个ArticleItem的时间刷新


class ArticleVM
    extends BaseListViewModel<UserModel, ArticleEntity, ArticleItem> {
  ArticleVM(this.isLoadData);
  bool isLoadData = true;

  /// 首次加载
  bool firstLoad = true;
  ValueNotifier<String> vnTime = ValueNotifier("暂无");

  @override
  void jointList(ArticleEntity newEntity) => entity.list.addAll(newEntity.list);

  @override
  List<ArticleItem> get list => entity.list;
  @override
  Future<DataResponse<ArticleEntity>> requestHttp(
      {bool isLoad, int page, params}) {
    /// 判断是否加载数据, 测试状态页用
    if (!isLoadData && firstLoad) {
      firstLoad = false;
      return null;
    }
    return model.getArticleList();
  }

  @override
  void initResultData() {
    vnTime.value = list[0].time;
  }

  /// 修改第一个数据的时间
  void modifyFistTime() {
    list[0].time = DateTime.now().toString();
    vnTime.value = list[0].time;
    notifyListeners();
  }
}

ArticlePage View #

文章具体页面,显示一个列表,下方显示第一个item对应的时间,和根布局刷新的时间。

class ArticlePage extends StatelessWidget with BaseView<ArticleVM> {
  const ArticlePage(
    this.rootRefresh, {
    Key key,
    this.configState = false,
    this.loadData = true,
  }) : super(key: key);

  /// 是否全局刷新
  final bool rootRefresh;
  final bool configState;
  final bool loadData;

  @override
  ViewConfig<ArticleVM> initConfig(BuildContext context) {
    var _empty = configState ? (vm) => Center(child: Text("单独配置:empty")) : null;
    return rootRefresh
        ? ViewConfig<ArticleVM>(vm: ArticleVM(loadData), empty: _empty)
        : ViewConfig<ArticleVM>.noRoot(vm: ArticleVM(loadData), empty: _empty);
  }

  @override
  Widget vmBuild(
      BuildContext context, ArticleVM vm, Widget child, Widget state) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(title: Text("文章")),
      bottomNavigationBar: state != null
          ? SizedBox()
          : Container(
              color: Colors.amber,
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  MaterialButton(
                    onPressed: vm.modifyFistTime,
                    color: Colors.white,
                    child: Text("修改第一个Item时间,测试全局刷新"),
                  ),
                  ValueListenableBuilder<String>(
                    valueListenable: vm.vnTime,
                    builder: (_, value, __) {
                      return Text("第一个Item时间:$value");
                    },
                  ),
                  Text("根布局刷新时间:${DateTime.now().toString()}"),
                ],
              ),
            ),
      body: state ??
          EasyRefresh(
            controller: vm.refreshController,
            onLoad: vm.loadMore,
            onRefresh: vm.pullRefresh,
            child: ListView.builder(
              itemCount: vm.list.length,
              itemBuilder: (ctx, index) {
                return Selector<ArticleVM, ArticleItem>(
                  selector: (_, aVM) => aVM.list[index],
                  shouldRebuild: (pre, next) => pre == next,
                  builder: (_, ArticleItem value, __) => _item(value),
                );
              },
            ),
          ),
    );
  }

  Widget _item(ArticleItem item) {
    return Container(
      color: Colors.lightGreen,
      margin: EdgeInsets.all(8),
      padding: EdgeInsets.all(4),
      child: Column(
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              Text(item.title),
              Text(item.time),
            ],
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(item.content),
          ),
        ],
      ),
    );
  }
}

数据刷新 #

通过ValueListenableBuilder 监听在ArticleVM创建的ValueNotifier 对象,实现局部刷新。 通过Selector 监听ArticleEntityListArticleItem 的变化

ValueListenableBuilder<String>(
                  valueListenable: vm.vnTime,
                  builder: (_, value, __) {
                    return Text("第一个Item时间:$value");
                  },
                )
Selector<ArticleVM, ArticleItem>(
                  selector: (_, aVM) => aVM.list[index],
                  shouldRebuild: (pre, next) => pre == next,
                  builder: (_, ArticleItem value, __) => _item(value),
                )

更多帮助 #

博客讲解:https://blog.csdn.net/q948182974/article/details/106613565

掘金讲解:https://juejin.im/post/5ee86c9b51882543313a0de7

一个MVVM框架附带简单的demo,会一直更新,希望支持一下.有问题可以反馈QQ 275918180。

1.1.9+4 #

修改 部分代码和注释与注解。

1.1.9+3 #

修改 BaseViewState build 兼容。 新增 BaseViewState 类中的 mixinBuild 方法

1.1.9+2 #

修改 BaseViewState build 兼容。 新增 ViewConfig.gListDataEmpty 配置列表页面->广告数据非空,列表数据空时的空视图,

1.1.9+1 #

修改 _disposeAdd 异常 兼容处理 null 和 StreamSubscription

1.1.9 #

修改 _disposeAdd 异常 导致page页面错误

1.1.8 #

修复 从列表页进入下一页面,返回列表页上拉失效。

ListOrGridEmpty 新增 listWidget 方法

initMVVM 新增 height 和 width 设置

1.1.7 #

优化 ViewConfig 配置

1.1.6 #

修复 The class doesn't have a concrete implementation of the super-invoked member 'build'. 错误

优化 ViewConfig 配置 checkEmpty 应用到BaeViewModel上面; 优化 刷新页面便捷方法; 新增 initPage 功能; 新增 ListOrGridEmpty;

1.1.5 #

新增公共方法getVM(),得到ViewModel全局调用的语法糖。 initMVVM 初始化新增参数 DataFromNetworkOrDatabase

1.1.4 #

BaseView新增获取对应ViewModel方法 VM vm(BuildContext context);

BaseViewOfState新增获取对应ViewModel方法 VM vm();

1.1.3 #

优化根布局刷新,修复当开启根布局不刷新后,页面为空或者数据错误,点击刷新没有效果。 增加demo状态页配置案例。

1.1.2 #

修复单独页面配置状态页类型错误。

1.1.1 #

修复不规范配置 上拉刷新下拉加载 造成的空异常。

1.1.0 #

优化刷新的空判断。

1.0.9 #

去掉 flutter_easyrefresh 依赖,initMVVM增加上拉刷新下拉加载全局配置

1.0.8 #

优化的刷新控制,子类可以自己实现控制

BaseListViewModel

BaseListViewModel({params, refreshController}) : super(defaultOfParams: params) { _refreshController = refreshController; }

resetRefreshState|finishRefresh|resetLoadState|finishLoad

1.0.7 #

优化代码和注释。

1.0.6 #

修复开启根布局不刷新,下拉刷新获取数据状态控制失效 新增下拉刷新方法 pullRefresh()

1.0.5 #

Demo 增加关于数据刷新的展示。

1.0.4 #

修复http跟本地组装数据

1.0.3 #

initMVVM 初始化提供全局状态页配置

1.0.2 #

更正说明

1.0.1 #

更新一下说明 和案例

0.0.1 #

首次上传 有简单的demo 暂时处理的不完善

example/lib/main.dart

import 'package:flutter/material.dart';

import 'package:fast_mvvm/fast_mvvm.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';

import 'article.dart';

void main() {
  runApp(App());
}

class UserModel extends BaseModel {
  Future<bool> login(String account, String psd) async {
    await Future.delayed(Duration(seconds: 3));
    return true;
  }

  Future<DataResponse<ArticleEntity>> getArticleList() async {
    await Future.delayed(Duration(seconds: 1));

    var entity = ArticleEntity([
      ArticleItem("1", "好的", "内容内容内容内容内容", DateTime.now().toString()),
      ArticleItem("1", "好的", "内容内容内容内容内容", DateTime.now().toString()),
    ]);

    DataResponse dataResponse =
        DataResponse<ArticleEntity>(entity: entity, totalPageNum: 3);
    return dataResponse;
  }
}

class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  @override
  void initState() {
    initMVVM<BaseViewModel>(
      [UserModel()],
      controllerBuild: () => EasyRefreshController(),
      resetRefreshState: (c) =>
          (c as EasyRefreshController)?.resetRefreshState(),
      finishRefresh: (c, {bool success, bool noMore}) =>
          (c as EasyRefreshController)
              ?.finishRefresh(success: success, noMore: noMore),
      resetLoadState: (c) => (c as EasyRefreshController)?.resetLoadState(),
      finishLoad: (c, {bool success, bool noMore}) =>
          (c as EasyRefreshController)
              ?.finishLoad(success: success, noMore: noMore),
    );
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SelectPage(),
    );
  }
}

class SelectVM extends BaseViewModel {
  ValueNotifier<bool> isLoadData = ValueNotifier(true);
  ValueNotifier<bool> isConfigState = ValueNotifier(false);
}

class SelectPage extends StatelessWidget with BaseView<SelectVM> {
  @override
  ViewConfig<SelectVM> initConfig(BuildContext context) =>
      ViewConfig(vm: SelectVM());

  @override
  Widget vmBuild(
      BuildContext context, SelectVM vm, Widget child, Widget state) {
    return Scaffold(
      appBar: AppBar(title: Text("选择")),
      body: ListView(
        children: <Widget>[
          ListTile(
            title: Text("是否加载数据,用来测试状态页和重新加载数据"),
            trailing: ValueListenableBuilder(
              valueListenable: vm.isLoadData,
              builder: (_, value, __) => Switch(
                value: value,
                onChanged: (value) => vm.isLoadData.value = value,
              ),
            ),
          ),
          ListTile(
            title: Text("是否单独配置状态页,用来测试状态页和重新加载数据"),
            trailing: ValueListenableBuilder(
              valueListenable: vm.isConfigState,
              builder: (_, value, __) => Switch(
                value: value,
                onChanged: (value) => vm.isConfigState.value = value,
              ),
            ),
          ),
          ListTile(
            title: Text("根布局刷新"),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (_) => ArticlePage(
                    true,
                    configState: vm.isConfigState.value,
                    loadData: vm.isLoadData.value,
                  ),
                ),
              );
            },
          ),
          ListTile(
            title: Text("根布局不刷新"),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (_) => ArticlePage(
                    false,
                    configState: vm.isConfigState.value,
                    loadData: vm.isLoadData.value,
                  ),
                ),
              );
            },
          ),
        ],
      ),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  fast_mvvm: ^1.1.9+4

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:fast_mvvm/fast_mvvm.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
30
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
65
Learn more about scoring.

We analyzed this package on Jul 14, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.15
  • Flutter: 1.17.5

Analysis suggestions

Package does not support Flutter platform Linux

Because:

  • package:fast_mvvm/fast_mvvm.dart that declares support for platforms: Android, iOS

Package does not support Flutter platform Web

Because:

  • package:fast_mvvm/fast_mvvm.dart that declares support for platforms: Android, iOS

Package does not support Flutter platform Windows

Because:

  • package:fast_mvvm/fast_mvvm.dart that declares support for platforms: Android, iOS

Package does not support Flutter platform macOS

Because:

  • package:fast_mvvm/fast_mvvm.dart that declares support for platforms: Android, iOS

Package not compatible with SDK dart

Because:

  • fast_mvvm that is a package requiring null.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
dio ^3.0.9 3.0.9
fast_event_bus ^1.0.1 1.0.1
flutter 0.0.0
provider ^4.1.3 4.3.1
Transitive dependencies
charcode 1.1.3
collection 1.14.12 1.14.13
http_parser 3.1.4
meta 1.1.8 1.2.2
nested 0.0.4
path 1.7.0
sky_engine 0.0.99
source_span 1.7.0
string_scanner 1.0.5
term_glyph 1.1.0
typed_data 1.1.6 1.2.0
vector_math 2.0.8 2.1.0-nullsafety
Dev dependencies
flutter_test