Flutter Provider状态管理---MVVM架构实战

文章系列 Flutter Provider状态管理---介绍、类图分析、基本使用
Flutter Provider状态管理---八种提供者使用分析
Flutter Provider状态管理---四种消费者使用分析
Flutter Provider状态管理---MVVM架构实战
视频系列 Flutter Provider状态管理---介绍、类图分析、基本使用
Flutter Provider状态管理---八种提供者使用分析
Flutter Provider状态管理---四种消费者使用分析
Flutter Provider状态管理---MVVM架构实战
源码仓库地址 github仓库地址
MVVM介绍 MVVM架构分为M(Model)、V(View)、VM(ViewModel)三个部分,他们分别处理自己的分工,在ViewModel之间使用ViewModel作为中介者,使ViewModel不受业务逻辑影响。
Model: 模型层,处理Api数据、模型相关业务
View: 视图层,UI呈现、使用者互动等。
ViewModel: 视图模型,处理逻辑、将数据绑定给View展示。
MVVM运行原理 当界面需要展示数据时,ViewViewModel绑定,ViewModelModel取得数据后,在ViewModel处理对应的业务逻辑,然后将数据处理,最后通过View更新并展示。
MVVM优点

  • 易于变更需求,降低耦合
  • 权责分工明确
  • 方便测试
MVVM缺点
  • 文件数量增加
  • bug定位较为不易
  • 数据绑定消耗资源
MVVM实战 下面这个项目实战是用ProviderMVVM搭建的架构,是一个笑话段子列表。
【Flutter Provider状态管理---MVVM架构实战】它包含了5主要类:
  • Service: 网络请求类
  • Model: 主要负责转换模型
  • View: 主要负责呈现UI,通过ViewModel获取数据并展示
  • Widgets: 单独的UI模块分离
  • ViewModel: 处理业务逻辑,将数据绑定给View展示
定义模型
将网络请求回来的数据转换为对应的模型
import 'dart:convert'; JokeModel jokeModelFromJson(String str) => JokeModel.fromJson(json.decode(str)); String jokeModelToJson(JokeModel data) => json.encode(data.toJson()); class JokeModel { JokeModel({ this.data, }); final List? data; factory JokeModel.fromJson(Map json) => JokeModel( data: List.from(json["data"].map((x) => Joke.fromJson(x))), ); Map toJson() => { "data": List.from(data!.map((x) => x.toJson())), }; }class Joke { Joke({ this.content, this.hashId, this.unixtime, this.updatetime, }); final String? content; final String? hashId; final int? unixtime; final String? updatetime; factory Joke.fromJson(Map json) => Joke( content: json["content"], hashId: json["hashId"], unixtime: json["unixtime"], updatetime: json["updatetime"], ); Map toJson() => { "content": content, "hashId": hashId, "unixtime": unixtime, "updatetime": updatetime, }; }

定义网络请求类
网络请求用到第三方网路请求库Dio ^4.0.0,将请求回来的数据转换为模型,并更新ViewModel数据。
import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter_provider_example/provider_mvvm_example/model/joke_model.dart'; import 'package:flutter_provider_example/provider_mvvm_example/view_model/joke_view_model.dart'; class JokeService { static Future getJokes(JokeViewModel jokeViewModel) async {var response = await Dio().get("http://v.juhe.cn/joke/content/text.php?page=1&pagesize=20&key=03303e4d34effe095cf6a4257474cda9"); if (response.statusCode == 200) { // 转换模型 JokeModel jokeModel = jokeModelFromJson(json.encode(response.data["result"])); // 更新数据 jokeViewModel.setJokeList(jokeModel); } } }

定义ViewModel
这个ViewModel主要负责把请求回来的数据进行处理,并通知View层更新数据
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/provider_mvvm_example/model/joke_model.dart'; class JokeViewModel with ChangeNotifier {List? _jokeList = []; late Joke _joke; bool loading = true; setJokeList(JokeModel jokeModel) { _jokeList = []; _jokeList = jokeModel.data; loading = false; notifyListeners(); }setJoke(Joke joke) { _joke = joke; }List? get jokeList => _jokeList; Joke get joke => _joke; }

定义View
我们在页面刚进入时进行初始化,然后通过ProviderConsumer来进行监听状态的变化。
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/provider_mvvm_example/service/joke_service.dart'; import 'package:flutter_provider_example/provider_mvvm_example/view_model/joke_view_model.dart'; import 'package:flutter_provider_example/provider_mvvm_example/widgets/joke_item.dart'; import 'package:provider/provider.dart'; class JokeView extends StatefulWidget { @override _JokeViewState createState() => _JokeViewState(); }class _JokeViewState extends State { @override void initState() {// 获取接口数据 JokeService.getJokes(Provider.of(context, listen: false)); super.initState(); }@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Provider + MVVM"), ), body: Consumer( builder: (_, jokeViewModel, child) {JokeViewModel _jokeViewModel = jokeViewModel; if (jokeViewModel.loading) { return Center( child: CircularProgressIndicator(), ); }return ListView.separated( itemBuilder: (_, index) { _jokeViewModel.setJoke(_jokeViewModel.jokeList![index]); return JokeItem(jokeViewModel: _jokeViewModel); }, itemCount: _jokeViewModel.jokeList?.length ?? 0, separatorBuilder: (_, index) { return Divider( height: 1, ); }, ); }, ), ); } }

定义Widgets
把需要单独抽离的UI放在widgets中,并把ViewModel传入进来。
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/provider_mvvm_example/model/joke_model.dart'; import 'package:flutter_provider_example/provider_mvvm_example/view_model/joke_view_model.dart'; class JokeItem extends StatelessWidget {JokeItem({ Key? key, this.jokeViewModel }) : super(key: key); final JokeViewModel? jokeViewModel; @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.only( left: 15, right: 15, top: 10, bottom: 10 ), child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text("${jokeViewModel?.joke?.content ?? ""}", style: TextStyle( color: Colors.black87, letterSpacing: 1.3, wordSpacing: 2 ), ), SizedBox(height: 5,), Text("${jokeViewModel?.joke?.updatetime ?? "--"}") ], ), ); } }

引用View
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/provider_mvvm_example/view/joke_view.dart'; class ProviderMvvmExample extends StatelessWidget { @override Widget build(BuildContext context) { return JokeView(); } }

应用程序入口设置
import 'package:flutter/material.dart'; import 'package:flutter_provider_example/provider_mvvm_example/provider_mvvm_example.dart'; import 'package:flutter_provider_example/provider_mvvm_example/view_model/joke_view_model.dart'; import 'package:provider/provider.dart'; void main() { runApp(MyApp()); }class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return ChangeNotifierProvider( create: (_) => JokeViewModel(), child: MaterialApp( debugShowCheckedModeBanner: false, home: ProviderMvvmExample(), ), ); } }

运行结果

总结 以上就是一个很简单的列表功能MVVM示例,在实际的情况下也不见得这是最好的方式,MVVM还有很多变种写法,但核心是一样的。
最后说一句,架构只是辅助而已,世界没有最好的架构。与其讨论这些,还不如想想这些架构为什么会出现?它的前因后果又是什么?在什么情况下要使用哪种架构?

    推荐阅读