这个转自我自己的有道云 想看图片去那里
文档:Day3_23 状态管理.md
链接:http://note.youdao.com/noteshare?id=81a9d9832a1baa68995b45110b101900&sub=82EF5749864544B7B0782C8163EF971E
状态管理是声明式编程里面非常重要的东西
这个东西是更接近原理的东西 它理解它对我们写代码
Flutter, vue, React 声明式编程
OC => swffit 整个前端都在向声明式编程迈近
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cv9Wp8sU-1586077223760)(4C28770B9BAA46E18583B645B85D5A8B)]
状态就是数据 我们在构建flutter的时候我们就需要一些状态
这个官方的图就很好的表示了flutter的情况
我们应用里面有这些状态 我们build方法里面依赖这些状态 最后就会生成我们的页面结构
一般情况下 如果状态改变我们可以 调用setState 然后他就会根据最新的状态来展示我们的页面的
状态被分成两种 短时状态 和 App State 应用状态
某些状态我们只需要在自己的wiget里面使用就可以了
这个就是当前页面会用到的时候它就是一个短时状态
这个状态管理只需要使用StatefulWidget对应的State 来使用就可以了
但是它有一个缺点 就是如果想要在其他的地方访问这个状态其实是并不好访问的
虽然GlobalKey可以做到差不多的事情但是还是希望有一个更方便的形式来使用这个全局状态
有些状态就希望是能在全局进行共享的
这个状态虽然可以在Widget中传递来传递去 虽然可以
但是会造成耦合度过高的问题 如果中间去掉一个 那就会造成很麻烦的问题 你全部结构都要改
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Np0O3zHz-1586077223763)(5CF3B0AF406E49429B2D1E2BFC56ACBB)]
如何选择应用状态 或者 短时状态
针对React使用setState还是Redux中的Store来管理状态哪个更好的问题,Redux的issue上,Redux的作者Dan Abramov
他这样回答的:
The rule of thumb is: Do whatever is less awkward
经验原则就是:选择能够减少麻烦的方式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l7S1wR85-1586077223765)(B22647F6BAB4405480941CDEA6E97D1E)]
本来这些工具就是为了 使用简单
我们主要学习两种状态管理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EUCMpeME-1586077223766)(D83564DE3B1D4396AAE1F02FEB4C5969)]
如果以前我们要做这个我们可能会
将这个状态传递到需要使用的地方 路由的话也是将他作为参数传递过去
这个东西是可以用来共享Widget的
创建一个状态
class HYCounterWidget extends InheritedWidget {
}
它这里有一个抽象方法我们还必须实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n2a1OrLM-1586077223767)(DDB3EA4C823D43489D95EAA7441C88E3)]
我们设置一个状态同时实现这个方法
class HYCounterWidget extends InheritedWidget {
final int counter = 100;
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
// TODO: implement updateShouldNotify
return null;
}
}
同时我们搞一个静态方法
class HYCounterWidget extends InheritedWidget {
final int counter = 100;
static HYCounterWidget of(BuildContext context) {
return null;
}
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
// TODO: implement updateShouldNotify
return null;
}
}
到这里我们的这个InheritedWidget就创建完成了
我们现在就是小组件希望拿到这个widget
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ux6eKX66-1586077223767)(28DF986DCBD54333A6E9BFC907013B57)]
现在我们就创建对应的小组件
然后在小组件的外层包裹一个HYCounterWidget
import "package:flutter/material.dart";
main() => runApp(MyApp());
class HYCounterWidget extends InheritedWidget {
final int counter = 100;
HYCounterWidget({Widget child}): super(child: child);
static HYCounterWidget of(BuildContext context) {
return null;
}
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
// TODO: implement updateShouldNotify
return null;
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
theme: ThemeData(
primarySwatch: Colors.blue,
splashColor: Colors.transparent,
),
home: HYHomePage(),
);
}
}
class HYHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("title"),
),
body: Center(
child: HYCounterWidget (
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
HYShowData01(),
HYShowData02()
],
),
),
),
);
}
}
class HYShowData01 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Card(
color: Colors.red,
child: Text("当前计数: 100")
);
}
}
class HYShowData02 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
child: Text("当前计数: 100"),
);
}
}
所以如果我们要拿到这个couterWidget我们就要
class HYShowData01 extends StatelessWidget {
@override
Widget build(BuildContext context) {
int counter = HYCounterWidget.of(context);
return Card(
color: Colors.red,
child: Text("当前计数: ")
);
}
}
这个context就是Element 他会记录着Widget所在的位置
就像下面这样
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jsmVsktf-1586077223769)(92021157421C448A943494BAAF295C3C)]
所以我们调用这个HYCounterWidget.of(context); 它就开始沿着这个树结构往上找
离我们最近的HYCounterWidget这个对象 而这个HYCounterWidget里面是有一个counter属性的
这个时候我们就可以使用这个couter对象的了
其他地方想用我们也这样把这个参数拿到就可以了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F8Nt3PW4-1586077223770)(F05E29734104475988E8896F84704844)]
class HYShowData01 extends StatelessWidget {
@override
Widget build(BuildContext context) {
int counter = HYCounterWidget.of(context).counter;
return Card(
color: Colors.red,
child: Text("当前计数: ")
);
}
}
class HYShowData02 extends StatelessWidget {
@override
Widget build(BuildContext context) {
int counter = HYCounterWidget.of(context).counter;
return Container(
color: Colors.blue,
child: Text("当前计数: 100"),
);
}
}
当然现在肯定是不行的 因为我们当前of返回的是一个null
所以我们要
class HYCounterWidget extends InheritedWidget {
final int counter = 100;
HYCounterWidget({Widget child}): super(child: child);
static HYCounterWidget of(BuildContext context) {
// 沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
// TODO: implement updateShouldNotify
return null;
}
}
你看这里它是返回的有一个泛型 所以那就是它了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s3DlVojY-1586077223771)(74ED7F9ECB40465280E27BA665CC158C)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uvVLcf1J-1586077223772)(D9161B5B4A6545B1A58F142E17A2990D)]
他会往上找找到对应的对象 找到这个counter
import "package:flutter/material.dart";
main() => runApp(MyApp());
class HYCounterWidget extends InheritedWidget {
final int counter = 100;
HYCounterWidget({Widget child}): super(child: child);
static HYCounterWidget of(BuildContext context) {
// 沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
// TODO: implement updateShouldNotify
return null;
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
theme: ThemeData(
primarySwatch: Colors.blue,
splashColor: Colors.transparent,
),
home: HYHomePage(),
);
}
}
class HYHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("title"),
),
body: Center(
child: HYCounterWidget (
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
HYShowData01(),
HYShowData02()
],
),
),
),
);
}
}
class HYShowData01 extends StatelessWidget {
@override
Widget build(BuildContext context) {
int counter = HYCounterWidget.of(context).counter;
return Card(
color: Colors.red,
child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
);
}
}
class HYShowData02 extends StatelessWidget {
@override
Widget build(BuildContext context) {
int counter = HYCounterWidget.of(context).counter;
return Container(
color: Colors.blue,
child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
);
}
}
然后运行一下 结果报错了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SQkQnnhw-1586077223773)(43473A6E808343F481BC1A98B93E917E)]
原因就是下面这个 这个东西它是要你返回一个bool值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y7rSjssR-1586077223774)(4AAFBC8455E3410DB54B3CF4CEEA88E9)]
class HYCounterWidget extends InheritedWidget {
final int counter = 100;
HYCounterWidget({Widget child}): super(child: child);
static HYCounterWidget of(BuildContext context) {
// 沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
// TODO: implement updateShouldNotify
return true;
}
}
但是这个返回什么用一会再说
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bPxsnqbz-1586077223775)(EABE9034FB50469FBC9330FD9322A393)]
遇到错误我们来读一下就可以了
但是现在我想要一个需求
我们这里有一个按钮然后点击以后 + 1
但是这里注意
我们将这个counter作为参数传递过来
class HYCounterWidget extends InheritedWidget {
final int counter;
HYCounterWidget({this.counter, Widget child}): super(child: child);
static HYCounterWidget of(BuildContext context) {
// 沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
// TODO: implement updateShouldNotify
return true;
}
}
然后在创建的里面弄一个 100 的变量
同样我们的这个数字是要变的所以不能使用statelessWidget
class HYHomePage extends StatefulWidget {
@override
_HYHomePageState createState() => _HYHomePageState();
}
class _HYHomePageState extends State {
int _counter = 100;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("title"),
),
body: Center(
child: HYCounterWidget (
counter: _counter,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
HYShowData01(),
HYShowData02()
],
),
),
),
);
}
}
整个按钮
class HYHomePage extends StatefulWidget {
@override
_HYHomePageState createState() => _HYHomePageState();
}
class _HYHomePageState extends State {
int _counter = 100;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("title"),
),
body: Center(
child: HYCounterWidget (
counter: _counter,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
HYShowData01(),
HYShowData02()
],
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.play_arrow),
onPressed: () {
setState(() {
_counter++;
});
},
),
);
}
}
这样setState的时候这个build的东西都会重新执行
那这个HYCounterWidget 自然就会重新创建
我们点击之后counter改变了 我们保存的_counter就改变
后面的这个就重新构建了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bDQiXJiZ-1586077223776)(40CA46C888F041CF954F92BC9B3402B6)]
这个东西就是我们InheritedWidget最基本的使用
import "package:flutter/material.dart";
main() => runApp(MyApp());
class HYCounterWidget extends InheritedWidget {
final int counter;
HYCounterWidget({this.counter, Widget child}): super(child: child);
static HYCounterWidget of(BuildContext context) {
// 沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
// TODO: implement updateShouldNotify
return true;
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
theme: ThemeData(
primarySwatch: Colors.blue,
splashColor: Colors.transparent,
),
home: HYHomePage(),
);
}
}
class HYHomePage extends StatefulWidget {
@override
_HYHomePageState createState() => _HYHomePageState();
}
class _HYHomePageState extends State {
int _counter = 100;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("title"),
),
body: Center(
child: HYCounterWidget (
counter: _counter,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
HYShowData01(),
HYShowData02()
],
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.play_arrow),
onPressed: () {
setState(() {
_counter++;
});
},
),
);
}
}
class HYShowData01 extends StatelessWidget {
@override
Widget build(BuildContext context) {
int counter = HYCounterWidget.of(context).counter;
return Card(
color: Colors.red,
child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
);
}
}
class HYShowData02 extends StatelessWidget {
@override
Widget build(BuildContext context) {
int counter = HYCounterWidget.of(context).counter;
return Container(
color: Colors.blue,
child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
);
}
}
还有一个问题就是undataShouldNotify 这个函数要求返回一个bool 即true或者false
这个区别是什么呢
可以看到它的名字 当我们更新的时候要不要做一个通知 返回true表示通知 返回false的话 表示不通知
我们改成false看看会有什么不同吗
class HYCounterWidget extends InheritedWidget {
final int counter;
HYCounterWidget({this.counter, Widget child}): super(child: child);
static HYCounterWidget of(BuildContext context) {
// 沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
// TODO: implement updateShouldNotify
return false;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-US7zHuas-1586077223777)(33F880FA7CD641D1B2E49576717B97E0)]
其实这个东西是如果你的使用的widget是fulWidget 这个为true还是为false会影响你是否会调用这个didChangeDependencies
我们之前讲过这个东西 什么时候会回调呢 它依赖我们的某一个InheritedWidget改变的时候它就会执行
import "package:flutter/material.dart";
main() => runApp(MyApp());
class HYCounterWidget extends InheritedWidget {
final int counter;
HYCounterWidget({this.counter, Widget child}): super(child: child);
static HYCounterWidget of(BuildContext context) {
// 沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
// TODO: implement updateShouldNotify
return true;
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
theme: ThemeData(
primarySwatch: Colors.blue,
splashColor: Colors.transparent,
),
home: HYHomePage(),
);
}
}
class HYHomePage extends StatefulWidget {
@override
_HYHomePageState createState() => _HYHomePageState();
}
class _HYHomePageState extends State {
int _counter = 100;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("title"),
),
body: Center(
child: HYCounterWidget (
counter: _counter,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
HYShowData01(),
HYShowData02()
],
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.play_arrow),
onPressed: () {
setState(() {
_counter++;
});
},
),
);
}
}
class HYShowData01 extends StatefulWidget {
@override
_HYShowData01State createState() => _HYShowData01State();
}
class _HYShowData01State extends State {
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
print("是否打印 void didChangeDependencies");
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
int counter = HYCounterWidget.of(context).counter;
return Card(
color: Colors.red,
child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
);
}
}
class HYShowData02 extends StatelessWidget {
@override
Widget build(BuildContext context) {
int counter = HYCounterWidget.of(context).counter;
return Container(
color: Colors.blue,
child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jLirPgTy-1586077223778)(5CD766496A754D6BB1A100B16FBC4078)]
然后改成false它就不会执行了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2DvZXNIU-1586077223780)(DDCD6D96D1BB4FBE9BAB7149E7ABA6FA)]
那我们到底是返回true还是false呢
我们可以比较原来的数据和现在是否是一样的 如果一样我们就返回true
class HYCounterWidget extends InheritedWidget {
final int counter;
HYCounterWidget({this.counter, Widget child}): super(child: child);
static HYCounterWidget of(BuildContext context) {
// 沿着我们Element树, 去找最近的HYCounterElement, 从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
@override
bool updateShouldNotify(HYCounterWidget oldWidget) {
// TODO: implement updateShouldNotify
return oldWidget.counter != counter;
}
}
所以我们来总结一下
我们使用InheritedWidget
所以一般情况下我们的InheritedWidget里面就需要做这些事情
然后我们就要看一下这个
context.dependOnInheritedWidgetOfExactType();
我们这里掉了这么一个方法 这个方法最主要的就是
沿着Element树找最近的HYCounterElement这个对象然后把它返回
然后从Element中取出widget对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yYvnK0PM-1586077223781)(308798937B6542C5A198C2214BBD745E)]
我们点到这个方法里面 它是一个抽象的方法
如果你想看子类的实现的话 ctrl + alt + b
如果有多个实现它会叫你选择 但是这里只有一个 它就会直接调过来
我们来看看它怎么找
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-khpBBubp-1586077223782)(D31C4A67F93D4BA5B717B534A8D3466B)]
它这里有一个 _inheritedWidgets 我们点过去看看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I2Itzu3z-1586077223782)(7B907A02E6A749809192E163E20EC8A9)]
ctrl + 左键 点过来看看 它这里是一个映射
我说我们找到的就是InheritedElement
然后再看过来
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NoDtRS7l-1586077223783)(9902E06923CC4ACEACECD12D59E8BBFF)]
我们这里看它是否为空 然后这里给你放入一个T
这个T是一个泛型
这个地方我们找的是HYCounterWidget 所以这里就意味着这个地方是一个HYCounterWidget这样的一个类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uhi1sgDF-1586077223784)(80FE44F2350647CD8088DDD752855101)]
然后找到之后它就把这个东西赋值给ancester(祖先)
然后它就拿到这个祖先了
他就判断一下你祖先是否为空 就返回空 如果不为空它就给你返回这个dependOnInheritedElement 这个方法
那么我们点进去
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-15btoOKt-1586077223784)(186C36CF915849BD837901D94D524BD5)]
然后这里有一个_dependencies 这个东西的作用就是 到时候数据刷新的时候 要不要执行这个didChangeDependencies
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KmdA9s3p-1586077223785)(5BF54FA6CB2749D4AE67D8D17C06A52B)]
我们这里最主要的还是
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VqJJSDGP-1586077223785)(10CCF606511E445BBE162C85C09205AC)]
这里从祖先拿到了widget 它这里就是把它的widget给返回了
所以我们这里的调用的这个函数 就是去取出我们的widget然后把它返回了
这个虽然是一个第三方的 插件 但是它是由社区作者和flutter团队共同编写的
他们现在也是在共同维护 所以可以放心使用 不用担心它有一天不维护了
这个完全不用担心 只要还有在用flutter 因该就会继续维护下去
因为这个玩意它属于第三方的东西所以我们在使用的 需要去安装它
pub.dev
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pnrDRhsG-1586077223786)(29AE6BFC698F4937B19692923CE3CDEE)]
dependencies:
event_bus: ^1.1.1
provider: ^4.0.4
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
dio: ^3.0.9
然后pub get
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-flPlkyiT-1586077223787)(9E76AAF4467941A68D2AF45E0F6ABFE0)]
我们什么使用使用
像刚刚我们使用InheritedWidget的时候 我们一般是管理一个状态
但是现在这里我们 如果希望是 管理多个状态 我们就需要使用Provider
还有就是我们在使用provider的时候它也是需要嵌套的
那为了不要考虑这个问题 我们一般是直接来到最顶层来嵌套这个provider
看到最上面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jlbulCQM-1586077223787)(5BF3D22E4F91403A8E7FDD11C884A8CC)]
这个MyApp就是最顶层的Widget
现在我们写成 箭头函数就不太好了展开
void main() {
runApp(
MyApp()
);
}
我们一般是使用的ChangeNotifyProvider
void main() {
runApp(
ChangeNotifierProvider(
child: MyApp()
)
);
}
但是还没完 这里还有一个属性create
void main() {
runApp(
ChangeNotifierProvider(
create: ,
child: MyApp()
)
);
}
而这个create就是放到我们以后要共享的数据
怎么使用呢
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-smUUy3tr-1586077223788)(A0921E05D226475DBD24A6BC415F12D7)]
一般我们是在文件夹里面 建立一个文件夹viewmodel
我们建立的这个文件夹 不是model
但是这个东西不是纯粹的model
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-loDTu0sJ-1586077223789)(742885F4022745D4B5590AC6D292279D)]
这样我们就是做的MVVM结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DjyqXJQU-1586077223790)(77BDD97955D34C758DE476B5EAA2B9BB)]
这样做一个东西 然后里面就由东西需要共享
然后这个数据有一天改变的时候我们是不是要通知我们的界面发生一个刷新
所以我们会然他继承至ChangeNotifier
如果我们不想要混入的话我们就可以使用混入
同样使用混入的话我们就会有这个类里面的所有相关的方法
但是这个地方 我们的变量是私有的 外面是访问不了的
所以我们要给它gs方法
我们可以使用 alt + insert 快速生成对应的方法
mac 是 commond + n
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fSlJoZdu-1586077223791)(936294F6098949B1A525A54292F4E160)]
同时为了能通知我们里面的东西进行刷新我们也需要 在set的位置调用对应的方法
import 'package:flutter/material.dart';
class HYCounterViewModel with ChangeNotifier{
int _counter;
int get counter => _counter;
set counter(int value) {
_counter = value;
}
}
调用notifyListeners, 通知所有的监听者 刷新状态
import 'package:flutter/material.dart';
class HYCounterViewModel with ChangeNotifier{
int _counter;
int get counter => _counter;
set counter(int value) {
_counter = value;
notifyListeners();
}
}
然后我们来到引用程序顶层
void main() {
runApp(
ChangeNotifierProvider(
child: MyApp()
)
);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jpe7edkg-1586077223792)(5BB1A3DD20154C5A8A83285B905B5A1C)]
我们看到这个create是一个必传的参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9V0gUwBZ-1586077223793)(2976C2A1EDB94B71857E558AC6FC8206)]
它长成这样 还有一个返回值这个返回的值就是我们刚刚写的HYCunterViewModel的对象
void main() {
runApp(
ChangeNotifierProvider(
create: (BuildContext ctx) {
return HYCounterViewModel();
},
child: MyApp()
)
);
}
那么我们怎么在后面使用这个东西呢
Provider.of(context);
通过这个东西我们就可以取到对应的这个ViewMdeol对象
但是我们怎么才能取到这个ViewModel呢
这个地方的 其实就相当于一个 key
class HYShowData01 extends StatelessWidget {
@override
Widget build(BuildContext context) {
int counter = Provider.of(context).counter;
return Container(
color: Colors.red,
child: Text("当前计数: 100", style: TextStyle(fontSize: 30)),
);
}
}
然后展示
import "package:flutter/material.dart";
import 'package:learn_flutter02/day09Protice/viewmodel/counter_view_model.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (BuildContext ctx) {
return HYCounterViewModel();
},
child: MyApp()
)
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
theme: ThemeData(
primarySwatch: Colors.blue,
splashColor: Colors.transparent,
),
home: HYHomePage(),
);
}
}
class HYHomePage extends StatefulWidget {
@override
_HYHomePageState createState() => _HYHomePageState();
}
class _HYHomePageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("title"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
HYShowData01(),
HYShowData02()
],
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.play_arrow),
onPressed: () {
},
),
);
}
}
class HYShowData01 extends StatelessWidget {
@override
Widget build(BuildContext context) {
int counter = Provider.of(context).counter;
return Container(
color: Colors.red,
child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
);
}
}
class HYShowData02 extends StatelessWidget {
@override
Widget build(BuildContext context) {
int counter = Provider.of(context).counter;
return Container(
color: Colors.blue,
child: Text("当前计数:$counter", style: TextStyle(fontSize: 30)),
);
}
}
但是这样它就是null 因为我们没有赋初值
import 'package:flutter/material.dart';
class HYCounterViewModel with ChangeNotifier{
int _counter = 100;
int get counter => _counter;
set counter(int value) {
_counter = value;
notifyListeners();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LaAhUSFb-1586077223794)(BEDDA5E2BEDC454B93E396B3FF200D2A)]
所以 这个看着和InheritedWidget非常相似
其实这个Provider它的底层就是依赖这个InheritedWidget的
但是它做很多的优化
然后我们同样希望能够在
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A8Oz3QJB-1586077223796)(6F8CBF06B67C42A4AD20C445A524788C)]
改变这个数据
你要想对它做一个修改 你必须这样来做
那我们要怎么来用呢
floatingActionButton: Consumer(
),
我们在对应的地方放一个Consumer 这个东西是消费者的意思
可能这个东西涉及到锁问题吧
它有一个必须实现的方法builder
floatingActionButton: Consumer(
builder:
),
你可以看到它的builder里面有三个参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Y6mLwLu-1586077223797)(48EF4FBD2A8F42D291E584207A8BF2E8)]
一般我们的这个builder都是会有一个context的 Element嘛在树结构中的位置
这个value就是我们的HYCounterViewModel
floatingActionButton: Consumer(
builder: (context, counterVM, child) {
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
},
);
}
),
我们可以通过counterVM取到对应的数据
floatingActionButton: Consumer(
builder: (context, counterVM, child) {
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
counterVM.counter += 1;
},
);
}
),
我们一旦设置这个之后其他的使用的地方就会做监听
HYShowdata01 02
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xYW0HhQC-1586077223798)(4E8C844B36254504B81081622E59DDBF)]
所以我们来总结一下使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r31Zy8Af-1586077223799)(87F7A65B9D8E47C6B30BEF8FB4CF9740)]
为什么说这个是一个MVVM
因为我们出了数据模型意外还多了一个ViewModel
MVVM 就是 Model View ViewModel 的简写
虽然这里没有专门的model对象不过感觉 差不多
我们来到HYShowData02
class HYShowData02 extends StatelessWidget {
@override
Widget build(BuildContext context) {
int counter = Provider.of(context).counter;
return Container(
color: Colors.blue,
child: Consumer(
builder: (ctx, counterVM, child) {
return Text("当前计数:${counterVM.counter}", style: TextStyle(fontSize: 30));
},
),
);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pakMRuaf-1586077223799)(63F3C5B064134065877EAF8F481C9EE6)]
但是从实现的难度上来说 还是直接使用Provider.of(context)简单一点
但是开发中还是Consumer还是用的更多一点
因为我们在使用Provider.of(context).counter 的时候
然后我们来看看
class HYShowData01 extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("data01的build方法");
int counter = Provider.of(context).counter;
return Container(
color: Colors.red,
child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
);
}
}
class HYShowData02 extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("data02的build方法");
return Container(
color: Colors.blue,
child: Consumer(
builder: (ctx, counterVM, child) {
print("data02的builder方法");
return Text("当前计数:${counterVM.counter}", style: TextStyle(fontSize: 30));
},
),
);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H2VUbBMS-1586077223800)(DD9516DB5BDE48A2825544D185B909FF)]
因为consumer里面只会执行builder内部的代码
但是外部的不会执行 所以它的执行效率高
builder方法是会被重新执行的
那么问一个问题我们的整个Provider在那些地方被依赖了
答案是: 三个
但是有些地方我们只是改变了数据 但是我们并不希望它重新执行builder方法
import "package:flutter/material.dart";
import 'package:learn_flutter02/day09Protice/viewmodel/counter_view_model.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (BuildContext ctx) {
return HYCounterViewModel();
},
child: MyApp()
)
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
theme: ThemeData(
primarySwatch: Colors.blue,
splashColor: Colors.transparent,
),
home: HYHomePage(),
);
}
}
class HYHomePage extends StatefulWidget {
@override
_HYHomePageState createState() => _HYHomePageState();
}
class _HYHomePageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("title"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
HYShowData01(),
HYShowData02()
],
),
),
floatingActionButton: Consumer(
builder: (context, counterVM, child) {
print("Consumer 中的builder");
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
counterVM.counter += 1;
},
);
}
),
);
}
}
class HYShowData01 extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("data01的build方法");
int counter = Provider.of(context).counter;
return Container(
color: Colors.red,
child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
);
}
}
class HYShowData02 extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("data02的build方法");
return Container(
color: Colors.blue,
child: Consumer(
builder: (ctx, counterVM, child) {
print("data02的builder方法");
return Text("当前计数:${counterVM.counter}", style: TextStyle(fontSize: 30));
},
),
);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DVXqtZDj-1586077223800)(F494D11B89854035ABEBD179737CCC8B)]
我们的这个FloatingActionButton 没有必要构建
同时我们的Icon也没有必要构建
FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
counterVM.counter += 1;
},
);
我们这里可以有两层优化
floatingActionButton: Consumer(
builder: (context, counterVM, child) {
print("Consumer 中的builder");
return FloatingActionButton(
child: child,
onPressed: () {
counterVM.counter += 1;
},
);
},
child: Icon(Icons.add),
),
这样我们的这个Icon它就不在builder里面了
所以Icon它就不会重新构建了
如果你这里十分的复杂的话它也不会重新构建了
但是它就没有办法使用这个ViewModel里面的数据了
第二部的优化
刚刚的那种优化一般用在HYShowdata01 02 的位置 那些地方有很多不希望重新构建的地方
但是针对我们的floatActionButton 当我们的数据发生变化的时候 floatActionButton也是不需要重新构建的
先创建我们需要共享的数据
在应用程序的顶层 ChangeNotifierProvider
在其他的位置使用这个共享的数据
selector
floatingActionButton: Selector(
)
使用的时候你发现它传一个泛型还不够
它要传两个 这两个怎么用我们一会再说
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L5VLLLTT-1586077223801)(31CEFA30182547C8A8DB7A870CDE7035)]
我们看到他的selector
它传过来一个S返回一个A我们可以通过它 去做ViewModel和Model的转化
但是我们这里共享的是一个普通的数值 所以没有必要使用这个东西
但是还是可以用一下
class _HYHomePageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("title"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
HYShowData01(),
HYShowData02()
],
),
),
floatingActionButton: Selector(
selector: (ctx, counterVM) => counterVM.counter,
builder: (ctx, counter, child) {
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
counter += 1;
},
);
},
)
);
}
}
但是这样没有办法通知到对应的数据了
所以我们还是改回来
class _HYHomePageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("title"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
HYShowData01(),
HYShowData02()
],
),
),
floatingActionButton: Selector(
selector: (ctx, counterVM) => counterVM,
builder: (ctx, counterVM, child) {
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
counterVM.counter += 1;
},
);
},
)
);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GrHes8OY-1586077223801)(6CFE7847B7AF43F3924210826900CC19)]
还是可以用的
floatingActionButton: Selector(
selector: (ctx, counterVM) => counterVM,
shouldRebuild: (prev, next) => false,
builder: (ctx, counterVM, child) {
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
counterVM.counter += 1;
},
);
},
)
Consumer 和 Selector 是搭配使用的
如果有对数据的依赖的话 我们可以使用Consumer
但是没有对数据的依赖或者想要 过滤数据或者改变数据的话我们就可以使用这个Selector
这里我们有一个弊端就是我们如果想要共享多个ViewModel
的话是不能实现的
我们这里共享数据是只能共享一个
但是肯定是想要共享多个 我们这里来创建这样的两个 文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BK8IpWaC-1586077223802)(379732F293EB4EE9BB1466F85823C941)]
user.dart
class UserInfo {
String nickName;
String imageUrl;
int level;
}
user_view_model.dart
import "package:flutter/material.dart";
import 'package:learn_flutter02/day09Protice/model/user.dart';
class UserViewModel extends ChangeNotifier {
UserInfo _user;
UserInfo get user => _user;
set user(UserInfo user) {
_user = user;
notifyListeners();
}
}
现在我们就有两个ViewModel文件了
那你如何进行共享呢
void main() {
runApp(
ChangeNotifierProvider(
create: (BuildContext ctx) {
return HYCounterViewModel();
},
child: MyApp()
)
);
}
其实我们是可以
这样嵌套
void main() {
runApp(
ChangeNotifierProvider(
create: (BuildContext ctx) {
return HYCounterViewModel();
},
child: ChangeNotifierProvider(
create: (BuildContext ctx) {
return HYUserViewModel()
},
child: MyApp(),
)
)
);
}
但是很明显这样嵌套不好
我们可以这样使用
void main() {
runApp(MultiProvider(
providers: [],
child: MyApp(),
));
}
这样我们就不需要使用这个ChangeNotifierProvider的嵌套了
void main() {
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(
create: (ctx) => HYCounterViewModel(),
),
ChangeNotifierProvider(
create: (ctx) => HYUserViewModel(UserInfo()),
)
],
child: MyApp(),
));
}
但是这样 这个provider里面的东西就太多了
同样我们老师的尿性就是这样
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IBgzlPti-1586077223802)(09F0BA41EAE64C97BF608675FC37CA4F)]
创建了一个新的dart 文件
import "package:provider/provider.dart";
import "package:provider/single_child_widget.dart";
import "counter_view_model.dart";
import "user_view_model.dart";
import "../model/user.dart";
List providers = [
ChangeNotifierProvider(
create: (ctx) => HYCounterViewModel(),
),
ChangeNotifierProvider(
create: (ctx) => HYUserViewModel(UserInfo()),
)
];
这样的话我们的这个东西我们就是在这个里面进行维护了
之后如果我们想要添加任何一个东西的 我们就只需要在这里 添加就可以了
所以如果我们有很多Provider我们就可以来这里维护
能不能使用呢
当然能了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JO8R61uT-1586077223803)(61C1FF14E3084C6B9D0FEAC334541BAA)]
import "package:flutter/material.dart";
import 'package:learn_flutter02/day09Protice/viewmodel/counter_view_model.dart';
import 'package:learn_flutter02/day09Protice/viewmodel/initialize_provider.dart';
import 'package:learn_flutter02/day09Protice/viewmodel/user_view_model.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MultiProvider(
providers: providers,
child: MyApp(),
));
// runApp(
// ChangeNotifierProvider(
// create: (BuildContext ctx) {
// return HYCounterViewModel();
// },
// child: ChangeNotifierProvider(
// create: (BuildContext ctx) {
// return HYUserViewModel(UserInfo());
// },
// child: MyApp(),
// )
// )
// );
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
theme: ThemeData(
primarySwatch: Colors.blue,
splashColor: Colors.transparent,
),
home: HYHomePage(),
);
}
}
class HYHomePage extends StatefulWidget {
@override
_HYHomePageState createState() => _HYHomePageState();
}
class _HYHomePageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("title"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
HYShowData01(),
HYShowData02(),
HYShowData03()
],
),
),
floatingActionButton: Selector(
selector: (ctx, counterVM) => counterVM,
shouldRebuild: (prev, next) => false,
builder: (ctx, counterVM, child) {
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
counterVM.counter += 1;
},
);
},
)
);
}
}
class HYShowData01 extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("data01的build方法");
int counter = Provider.of(context).counter;
return Container(
color: Colors.red,
child: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
);
}
}
class HYShowData02 extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("data02的build方法");
return Container(
color: Colors.blue,
child: Consumer(
builder: (ctx, counterVM, child) {
print("data02的builder方法");
return Text("当前计数:${counterVM.counter}", style: TextStyle(fontSize: 30));
},
),
);
}
}
class HYShowData03 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
child: Consumer(
builder: (ctx, userVM, child) {
return Text("user对象的展示${userVM.user.nickName}");
},
),
);
}
}
很容易想到嵌套
但是这个不好
我们知道现在我们的Provider已经有三种用法了
但是现在又要多一些了
我们只说一下2讲了2其他的你就知道了
这个Consumer有两个泛型 分别代表两个ViewModel
同时builder的参数也多了一个
class HYShowData03 extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Provider.of Consumer Selector Consumer23456
return Container(
color: Colors.yellow,
child: Consumer2(
builder: (ctx, userVM, counterVM, child) {
return Text(
"user对象的展示${userVM.user.nickName} counter的展示 ${counterVM.counter}"
, style: TextStyle(fontSize: 30),);
},
),
);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ReXYdWJ6-1586077223803)(2215A52075634235A1FEFB6E82F9C7A7)]
这个东西就是关于我们的Provider的使用
用的时候如果有不懂因该马上去看需要什么东西