文章系列
Flutter Provider狀態(tài)管理---介紹、類圖分析、基本使用
Flutter Provider狀態(tài)管理---八種提供者使用分析
Flutter Provider狀態(tài)管理---四種消費(fèi)者使用分析
Flutter Provider狀態(tài)管理---MVVM架構(gòu)實戰(zhàn)
視頻系列
Flutter Provider狀態(tài)管理---介紹砸琅、類圖分析涕俗、基本使用
Flutter Provider狀態(tài)管理---八種提供者使用分析
Flutter Provider狀態(tài)管理---四種消費(fèi)者使用分析
Flutter Provider狀態(tài)管理---MVVM架構(gòu)實戰(zhàn)
源碼倉庫地址
MVVM介紹
MVVM架構(gòu)分為M(Model)材鹦、V(View)碟狞、VM(ViewModel)三個部分可都,他們分別處理自己的分工缓待,在View
和Model
之間使用ViewModel
作為中介者,使View
和Model
不受業(yè)務(wù)邏輯影響渠牲。
Model: 模型層旋炒,處理Api數(shù)據(jù)、模型相關(guān)業(yè)務(wù)
View: 視圖層签杈,UI呈現(xiàn)瘫镇、使用者互動等。
ViewModel: 視圖模型答姥,處理邏輯铣除、將數(shù)據(jù)綁定給View展示。
MVVM運(yùn)行原理
當(dāng)界面需要展示數(shù)據(jù)時鹦付,View
跟ViewModel
綁定尚粘,ViewModel
向Model
取得數(shù)據(jù)后,在ViewModel
處理對應(yīng)的業(yè)務(wù)邏輯敲长,然后將數(shù)據(jù)處理背苦,最后通過View
更新并展示互捌。
MVVM優(yōu)點(diǎn)
- 易于變更需求,降低耦合
- 權(quán)責(zé)分工明確
- 方便測試
MVVM缺點(diǎn)
- 文件數(shù)量增加
- bug定位較為不易
- 數(shù)據(jù)綁定消耗資源
MVVM實戰(zhàn)
下面這個項目實戰(zhàn)是用Provider
和MVVM
搭建的架構(gòu)行剂,是一個笑話段子列表秕噪。
它包含了5主要類:
- Service: 網(wǎng)絡(luò)請求類
- Model: 主要負(fù)責(zé)轉(zhuǎn)換模型
- View: 主要負(fù)責(zé)呈現(xiàn)UI,通過ViewModel獲取數(shù)據(jù)并展示
- Widgets: 單獨(dú)的UI模塊分離
- ViewModel: 處理業(yè)務(wù)邏輯厚宰,將數(shù)據(jù)綁定給View展示
定義模型
將網(wǎng)絡(luò)請求回來的數(shù)據(jù)轉(zhuǎn)換為對應(yīng)的模型
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<Joke>? data;
factory JokeModel.fromJson(Map<String, dynamic> json) => JokeModel(
data: List<Joke>.from(json["data"].map((x) => Joke.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"data": List<dynamic>.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<String, dynamic> json) => Joke(
content: json["content"],
hashId: json["hashId"],
unixtime: json["unixtime"],
updatetime: json["updatetime"],
);
Map<String, dynamic> toJson() => {
"content": content,
"hashId": hashId,
"unixtime": unixtime,
"updatetime": updatetime,
};
}
定義網(wǎng)絡(luò)請求類
網(wǎng)絡(luò)請求用到第三方網(wǎng)路請求庫Dio ^4.0.0
腌巾,將請求回來的數(shù)據(jù)轉(zhuǎn)換為模型,并更新ViewModel
數(shù)據(jù)铲觉。
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<void> 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) {
// 轉(zhuǎn)換模型
JokeModel jokeModel = jokeModelFromJson(json.encode(response.data["result"]));
// 更新數(shù)據(jù)
jokeViewModel.setJokeList(jokeModel);
}
}
}
定義ViewModel
這個ViewModel
主要負(fù)責(zé)把請求回來的數(shù)據(jù)進(jìn)行處理澈蝙,并通知View
層更新數(shù)據(jù)
import 'package:flutter/material.dart';
import 'package:flutter_provider_example/provider_mvvm_example/model/joke_model.dart';
class JokeViewModel with ChangeNotifier {
List<Joke>? _jokeList = [];
late Joke _joke;
bool loading = true;
setJokeList(JokeModel jokeModel) {
_jokeList = [];
_jokeList = jokeModel.data;
loading = false;
notifyListeners();
}
setJoke(Joke joke) {
_joke = joke;
}
List<Joke>? get jokeList => _jokeList;
Joke get joke => _joke;
}
定義View
我們在頁面剛進(jìn)入時進(jìn)行初始化,然后通過Provider
的Consumer
來進(jìn)行監(jiān)聽狀態(tài)的變化撵幽。
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<JokeView> {
@override
void initState() {
// 獲取接口數(shù)據(jù)
JokeService.getJokes(Provider.of<JokeViewModel>(context, listen: false));
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Provider + MVVM"),
),
body: Consumer<JokeViewModel>(
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
把需要單獨(dú)抽離的UI放在widgets
中灯荧,并把ViewModel
傳入進(jìn)來。
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();
}
}
應(yīng)用程序入口設(shè)置
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(),
),
);
}
}
運(yùn)行結(jié)果
[圖片上傳失敗...(image-63254f-1633573555937)]
總結(jié)
以上就是一個很簡單的列表功能MVVM示例盐杂,在實際的情況下也不見得這是最好的方式逗载,MVVM還有很多變種寫法,但核心是一樣的链烈。
最后說一句厉斟,架構(gòu)只是輔助而已,世界沒有最好的架構(gòu)强衡。與其討論這些擦秽,還不如想想這些架構(gòu)為什么會出現(xiàn)?它的前因后果又是什么漩勤?在什么情況下要使用哪種架構(gòu)感挥?