Flutter MVVM架构插件fast_mvvm的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter MVVM架构插件fast_mvvm的使用

快速开始

fast_mvvm 是一个基于 MVVM 架构的 Flutter 插件,提供了便捷的开发方式来构建复杂的应用。它可以帮助开发者快速实现数据绑定、状态管理、页面刷新等功能。

Demo 讲解

本示例模拟了一个文章列表页面,展示如何使用 fast_mvvm 来实现 MVVM 架构。

UserModel

首先定义一个 UserModel,用于处理登录和获取文章列表的操作。

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

  /// 获取文章列表
  Future<DataResponse<ArticleEntity>> getArticleList() async {
    await Future.delayed(const Duration(seconds: 1));

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

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

初始化

在应用启动时,初始化 fast_mvvm 并配置相关参数。

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, {success, noMore}) =>
          (c as EasyRefreshController).finishRefresh(success: success, noMore: noMore),
      resetLoadState: (c) => (c as EasyRefreshController).resetLoadState(),
      finishLoad: (c, {success, noMore}) =>
          (c as EasyRefreshController).finishLoad(success: success, noMore: noMore),
    );
    super.initState();
  }

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

SelectPage

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() => ViewConfig.noLoad(SelectVM());

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

ArticleVM 是具体的 ViewModel,用于处理文章列表的逻辑。

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 = false,
    int page = 1,
    Map<String, dynamic>? 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

ArticlePage 是具体的文章页面,显示文章列表和相关信息。

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() {
    var _empty = configState
        ? (vm) => Center(child: Text("单独配置:empty"))
        : null;
    return rootRefresh
        ? ViewConfig<ArticleVM>(vm: ArticleVM(loadData), empty: _empty)
        : ViewConfig.noRoot(vm: ArticleVM(loadData), empty: _empty);
  }

  @override
  Widget vBuild(
      BuildContext context, ArticleVM vm, Widget? child, Widget? state) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(title: const Text("文章")),
      bottomNavigationBar: state != null
          ? SizedBox()
          : Container(
              color: Colors.amber,
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  MaterialButton(
                    onPressed: vm.modifyFistTime,
                    color: Colors.white,
                    child: const 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: const EdgeInsets.all(8),
      padding: const EdgeInsets.all(4),
      child: Column(
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(item.title),
              Text(item.time),
            ],
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(item.content),
          ),
        ],
      ),
    );
  }
}

数据刷新

通过 ValueListenableBuilderSelector 实现局部刷新。

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),
)

更多关于Flutter MVVM架构插件fast_mvvm的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter MVVM架构插件fast_mvvm的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


fast_mvvm 是一个用于 Flutter 应用的 MVVM 架构插件,旨在帮助开发者更轻松地实现 Model-View-ViewModel 架构。它提供了一些工具和类,使得开发者可以更高效地管理状态和业务逻辑。

安装 fast_mvvm

首先,你需要在 pubspec.yaml 文件中添加 fast_mvvm 依赖:

dependencies:
  flutter:
    sdk: flutter
  fast_mvvm: ^latest_version

然后运行 flutter pub get 来安装依赖。

使用 fast_mvvm

1. 创建 ViewModel

ViewModelfast_mvvm 的核心部分,它负责处理业务逻辑和状态管理。你需要继承 BaseViewModel 类来创建你的 ViewModel

import 'package:fast_mvvm/fast_mvvm.dart';

class MyViewModel extends BaseViewModel {
  String _title = "Hello, MVVM!";

  String get title => _title;

  void updateTitle(String newTitle) {
    _title = newTitle;
    notifyListeners(); // 通知视图更新
  }
}

2. 创建 View

View 是用户界面的部分,它通过 ViewModel 来获取数据和监听状态变化。你可以使用 ViewModelProvider 来将 ViewModel 注入到 View 中。

import 'package:flutter/material.dart';
import 'package:fast_mvvm/fast_mvvm.dart';
import 'my_view_model.dart';

class MyView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ViewModelProvider<MyViewModel>(
      create: () => MyViewModel(),
      child: Scaffold(
        appBar: AppBar(
          title: Text("MVVM Example"),
        ),
        body: Center(
          child: ViewModelBuilder<MyViewModel>(
            builder: (context, viewModel) {
              return Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(viewModel.title),
                  ElevatedButton(
                    onPressed: () {
                      viewModel.updateTitle("New Title!");
                    },
                    child: Text("Update Title"),
                  ),
                ],
              );
            },
          ),
        ),
      ),
    );
  }
}

3. 运行应用

在你的 main.dart 文件中,运行 MyView

import 'package:flutter/material.dart';
import 'my_view.dart';

void main() {
  runApp(MaterialApp(
    home: MyView(),
  ));
}
回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!