效果图
provider的基本使用
暴露创建的对象。
void main() {
runApp(
MultiProvider(
providers: [
Provider(create: (context) => WordRepositoryRemote()),
ChangeNotifierProvider(
create: (context) => WordViewModel(context.read()),
),
],
child: MyPage(),
),
);
}
MultiProvider声明多个provider,Provider暴露一个对象,ChangeNotifierProvider暴露一个继承ChangeNotifier的对象。
如何获取暴露的对象。
context.watch<T>(),widget 能够监听到 T 类型的 provider 发生的改变。
context.read<T>(),直接返回 T,不会监听改变。
context.select<T,R>(R cb(T value)),允许 widget 只监听 T 上的一部分内容的改变。
例子
wordViewModel = context.read<WordViewModel>();
监听viewmodel更新ui。
当WordViewModel数据有变化并调用notifyListeners()时更新ui。
body: Consumer<WordViewModel>(
builder: (context, wordViewModel, child) {
if (wordViewModel.isLoading) {//加载中
........
} else if (wordViewModel.error != null) {//错误
.......
} else {//数据处理与显示
.........
}
},
),
MVVM
model层
模拟一个从网络获取单词数据,延迟2s。
模型
class WordModel {
String random;
String def;
String pron;
String wordAudio;
String word;
WordModel(this.random, this.def, this.pron, this.wordAudio, this.word);
}
从网络获取
import 'package:first_flutter/model/word_model.dart';
class WordRepositoryRemote {
///模拟网络请求
Future<List<WordModel>> getWordList() async {
List<WordModel> wordList = [];
await Future.delayed(Duration(seconds: 2));
WordModel wordModel1 = WordModel(
"0",
"抛弃,遗弃;中止;陷入,沉湎于",
" əˈbændən",
"http://dict.youdao.com/dictvoice?audio=abandon",
"abandon",
);
WordModel wordModel2 = WordModel(
"1",
"抛弃;放纵",
" əˈbændənmənt",
"http://dict.youdao.com/dictvoice?audio=abandonment",
"abandonment",
);
WordModel wordModel3 = WordModel(
"2",
"缩略词,缩写形式;缩略",
" əˌbriːviˈeɪʃn",
"http://dict.youdao.com/dictvoice?audio=abbreviation",
"abbreviation",
);
wordList.add(wordModel1);
wordList.add(wordModel2);
wordList.add(wordModel3);
return wordList;
}
}
viewmodel
创建WordViewModel类继承ChangeNotifier。
import 'package:first_flutter/model/word_repository_remote.dart';
import 'package:flutter/foundation.dart';
import '../model/word_model.dart';
class WordViewModel extends ChangeNotifier {
WordRepositoryRemote wordRepositoryRemote;
List<WordModel> wordList = [];
bool _isLoading = true;
String? _error;
WordViewModel(this.wordRepositoryRemote);
bool get isLoading => _isLoading;
//请求单词数据
void getWordList() async {
_isLoading = true;
_error = null;
notifyListeners();
try {
wordList = await wordRepositoryRemote.getWordList();
} catch (e) {
_error = e.toString();
} finally {
_isLoading = false;
notifyListeners();
}
}
String? get error => _error;
}
上面代码中notifyListeners调用后,就会调用view层的Consumer< WordViewModel >。
view
import 'package:first_flutter/model/word_repository_remote.dart';
import 'package:first_flutter/view_model/word_view_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'model/word_model.dart';
void main() {
runApp(
MultiProvider(
providers: [
Provider(create: (context) => WordRepositoryRemote()),
ChangeNotifierProvider(
create: (context) => WordViewModel(context.read()),
),
],
child: MyPage(),
),
);
}
class MyPage extends StatelessWidget {
const MyPage({super.key});
Widget build(BuildContext context) {
return MaterialApp(theme: ThemeData(), home: WordWidget());
}
}
class MyState extends State {
late WordViewModel wordViewModel;
void initState() {
super.initState();
wordViewModel = context.read<WordViewModel>();
Future.microtask(() {
if(mounted){
wordViewModel.getWordList();
}
},);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("单词 "), centerTitle: true),
body: Consumer<WordViewModel>(
builder: (context, wordViewModel, child) {
if (wordViewModel.isLoading) {
return Container(
color: Colors.white,
child: Center(child: CircularProgressIndicator()),
);
} else if (wordViewModel.error != null) {
return Center(child: Text('错误: ${wordViewModel.error}'));
} else {
if (wordViewModel.wordList.isEmpty) {
return Center(child: Text("暂无数据"));
} else {
List<Widget> wlist = [];
for (int i = 0; i < wordViewModel.wordList.length; i++) {
WordModel wm = wordViewModel.wordList[i];
ListTile listTile = ListTile(
title: Text(wm.word),
subtitle: Text(wm.def),
);
wlist.add(listTile);
}
return ListView(children: wlist);
}
}
},
),
);
}
}
class WordWidget extends StatefulWidget {
State<StatefulWidget> createState() {
return MyState();
}
}
Future.microtask 的作用就是将一个异步任务调度到微任务队列中,使其在当前同步代码执行结束后、事件队列中的下一个任务被执行之前得到执行。mounted 表示当前 State 对象是否仍然与一个有效的 Widget 关联。除了使用Future.microtask之外,还可以使用
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
wordViewModel.getWordList();
});
WidgetsBinding.instance.addPostFrameCallback调用时机,在一帧绘制完成之后、下一帧开始之前立即调用。