概述
其实本身 flutter 就是一个 mvvm 的架构, 当你加载完数据 ,直接 setState() 就可以了,
MVVM各代表什么
- M:Model class,便是指这里的Repository ,主要负责从本地数据库或者远程服务器来获取数据,Repository统一了数据的入口,获取到数据,将数据发送给 ViewModel
- VM:ViewModel ,即 Flutter 中的ValueNotifier 或者是 ChangeNotifier
- V:View ,即 Flutter 中 Widget,也可以认为 ValueListenableBuilder 或者是 ChangeNotifierProvider
使用MVVM实现一个简单的登录页面
比如我们去请求一个网络的书籍
先建 Model
class BookModel {
String? name; // 书的名字
String? author;// 书的作者
String? detail;
int loadState = 1; // 0 : 加载中 1:加载成功 2:加载失败
}
创建我们的BookRepository
下面的是模拟网络去请求数据,用一个变量 当 count 是偶数的时候模拟网络错误,是奇数的时候模拟网络成功
class BookRepository {
var count = 0;
Future<BookModel> getData() async {
await Future.delayed(const Duration(seconds: 2));
BookModel model = BookModel();
count++;
if (count.isOdd) {
throw Exception("网络错误");
} else {
model.name = "Flutter$count";
model.author = "google$count";
}
return model;
}
}
再写ViewModel层
class BookViewModel extends ValueNotifier<BookModel> {
// 创建 BookRepository
BookRepository repository = BookRepository();
BookViewModel() : super(BookModel());
// 当 已经 dispose 的时候,就不要在发了
bool _dispose = false;
@override
void dispose() {
super.dispose();
_dispose = true;
}
@override
void notifyListeners() {
if (!_dispose) {
super.notifyListeners();
}
}
/// 用来请求数据
void getData() {
// 这里的value 就是 BookModel
// 设置为加载中
value.loadState = 0;
// 通知改变
notifyListeners();
repository.getData().then((book) {
// 设置加载成功
book.loadState = 1;
// 赋值的时候 会调用notifyListeners
value = book;
}).catchError((e) {
// 设置网络错误的码
value.loadState = 2;
notifyListeners();
});
}
}
创建我们的View层
下面有使用 ValueListenableBuilder 以及 ChangeNotifierProvider 两种方式去实现,具体请看代码
class FlutterBook extends StatefulWidget {
FlutterBook({Key? key}) : super(key: key);
@override
_FlutterBookState createState() => _FlutterBookState();
}
class _FlutterBookState extends State<FlutterBook> {
final BookViewModel _viewModel = BookViewModel();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('MVVM的demo'),
),
// body: _useValueListenableBuilderBody(),
body: _useProviderBody(),
);
}
/// 使用 ValueListenableBuilder
Widget _useValueListenableBuilder() {
return ValueListenableBuilder<BookModel>(
valueListenable: _viewModel,
builder: (BuildContext context, BookModel model, Widget? child) {
return _bodyChild(model);
},
);
}
/// 使用 ChangeNotifierProvider
Widget _useProviderBody() {
return ChangeNotifierProvider(
create: (_) => _viewModel,
child: Consumer<BookViewModel>(
builder: (context, BookViewModel viewModel, child) {
BookModel model = viewModel.value;
return _bodyChild(model);
}),
);
}
Widget _bodyChild(BookModel model) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
alignment: Alignment.center,
width: 200,
height: 200,
child: IndexedStack(
alignment: Alignment.center,
// 根据加载状态去显示对应的布局
index: model.loadState,
children: [
const CircularProgressIndicator(),
Text(
"名字是:${model.name ?? ""} , 作者是:${model.author ?? ""}",
),
// 加载失败,点击重试
InkWell(
child: Image.asset("assets/img/net_error.jpg"),
onTap: _viewModel.getData,
)
],
),
),
Container(
margin:
const EdgeInsets.only(top: 50, left: 50, right: 50, bottom: 0),
height: 50,
width: MediaQuery.of(context).size.width,
child: MaterialButton(
elevation: 3,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20))),
color: Theme.of(context).primaryColor,
minWidth: 60,
// 点击加载
onPressed: _viewModel.getData,
child: const Text("请求数据",
style: TextStyle(color: Colors.white, fontSize: 20)),
)),
],
);
}
}