慕课网站开发与实现网站提交百度了经常修改网站
news/
2025/9/25 19:57:53/
文章来源:
慕课网站开发与实现,网站提交百度了经常修改网站,wordpress自定义百度分享,健康陕西app管理端最新版状态提升#xff08;Lifting-state-up#xff09;
把子组件的状态#xff0c;提升到上级组件中#xff0c;从而实现在多个组件之间共享和同步数据的效果
以 flutter counter demo#xff0c;那个按按钮1 的来说#xff0c;现在的 count 是几#xff0c;不是存在页面显…状态提升Lifting-state-up
把子组件的状态提升到上级组件中从而实现在多个组件之间共享和同步数据的效果
以 flutter counter demo那个按按钮1 的来说现在的 count 是几不是存在页面显示几的地方而是作为 HomePage 的一个 state这样就提到了上级子组件那个按钮的 press 事件也不是说找到页面显示几的 Text 元素然后改那个元素而是改 state
子组件获取和控制父组件的状态
父组件传给子组件只需要直接传参数进去即可
class Child extends StatelessWidget {final int stateFromParent;const Child({Key? key, required this.stateFromParent}): super(key: key);
}子组件想要修改父组件的变量需要使用 callback。在 dart 中函数也可以作为参数传递
class Child extends StatelessWidget {final void Function() changeStateInParent;const Child({Key? key, required this.changeStateInParent}): super(key: key);overrideWidget build(BuildContext context) {return ElevatedButton(onPressed: changeStateInParent);}
}关于 Funtion 类型
final void Function() changeStateInParent;void 是代表这个函数的返回值 ()是代表这个函数没有参数
为什么是这个顺序呢因为我们平时写函数的时候也是这样
int fun(int x, int y){...
}那么这个 fun 函数的函数类型就是 int Function(int x, int y)
void Function()可以缩写为 VoidCallback因为 flutter 里面有如下定义
typedef VoidCallback void Function();关于 BuildContext
BuildContext 在 flutter 中是用于定位当前 Widget 在 Widget 树中位置的对象用于访问父 widget 和其他相关信息在构建 UI 时调用
比如用 Nevigator 进行页面导航的时候需要使用 BuildContext 来获取当前 Scaffold页面基本元素布局如 appBar 之类的或 MaterialApp以执行页面跳转操作
BuildContext 还可用于查找和访问在 widget 树中的其他 widget
onPressed: () {Scaffold scaffold Scaffold.of(context);scaffold.showSnackBar(SnackBar(content: Text(Button Pressed)))
}控制器父组件控制子组件的状态
第一个思路状态提升将子组件的状态提升到父组件上可以但是有一些问题
子组件不是我们自己写的而是用的别人的库这样就没办法要求它提升到我们的父组件了而且我们封装子组件的目的就是为了提高性能结果提升到父组件了又要整体进行重绘如拆如果这个状态是基础数据类型那么父组件给子组件传递的是值是一个副本子组件去修改这个值的时候修改不到父组件的版本
解决状态提升的基础数据类型问题
基础数据类型下父组件给子组件传递的是值不是引用父组件控制子组件的功能是好的但是如果子组件想正常的管理自己的 state就通知不到父组件
解决方法是将 state 从基础数据类型转换为一个复杂的结构这样传的就是引用而不是值了
class IntHolder {int value;IntHolder(this.value);
}class _ParentState extends StateParent {IntHolder ih IntHolder(1);Widget build(BuildContext context) {return Child(ih);}
}class Child extends StatefulWidget {final IntHolder ih;const Child({Key? key, required this.ih});
}class _ChildState extends StateChild {overrideWidget build(BuildContext context){return Column(children: [Text(widget.ih.value);ElevatedButton(onPressed: () {setState(() {widget.ih.value 2; });});]);}
}解决整体重绘的问题
子组件可以监听一个 stream当 stream 发生变化的时候这个子组件也可以发生变化这个解决方案有点太“重”了我们这个假如就是传递一个数结果整了一个 stream没必要
如果父组件有这么一个功能当某个 state 变化的时候能够“通知”相对应的子组件让它变化这个过程中父组件自身不变就可以完美解决这个问题了
这就要求 state 不是直接以 state 形式被提升在父组件里而是被封装起来。即使里面的值发生变化从父组件的角度看这个被封装起来的块没有发生变化就不会引发父组件重绘同时里面的值发生变化后又要能够通知子组件让子组件进行重绘
flutter 有一个 ChangeNotifier 类可以有这样的效果当 object 更新时通知子组件更新
下面的例子是官方文档举的例子其中 CounterModel 就混入了 ChangeNotifier当_count 变化时会通知这个 notifier 所在的 ListenableBuilder然后 ListenableBuilder 重新使用 builder 进行 build
// 这里的 with 相当于 mixin直接混入进去如 class Dove extends Bird with Walker, Flyer {}
class CounterModel with ChangeNotifier {int _count 0;int get count _count;void increment() {_count 1;notifyListeners(); // 在 _count 变化后通知}// 下面还能再加一些其他的更新 _count 的逻辑
}class ListenableBuilderExample extends StatefulWidget {const ListenableBuilderExample({super.key});overrideStateListenableBuilderExample createState() _ListenableBuilderExampleState();
}class _ListenableBuilderExampleState extends StateListenableBuilderExample {final CounterModel _counter CounterModel();overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: const Text(ListenableBuilder Example)),body: CounterBody(counterNotifier: _counter),floatingActionButton: FloatingActionButton(onPressed: _counter.increment,child: const Icon(Icons.add),),),);}b
}class CounterBody extends StatelessWidget {const CounterBody({super.key, required this.counterNotifier});final CounterModel counterNotifier;overrideWidget build(BuildContext context) {return Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: Widget[const Text(Current counter value:),// Thanks to the ListenableBuilder, only the widget displaying the// current count is rebuilt when counterValueNotifier notifies its// listeners. The Text widget above and CounterBody itself arent// rebuilt.ListenableBuilder(listenable: counterNotifier, // 每当 counterNotifier 里 notifyListeners 后builder: (BuildContext context, Widget? child) { // 重新 buildreturn Text(${counterNotifier.count});},),],),);}
}这个 CounterModel 就是一个控制器可以直接把它重新命名为 CounterController 如果是例子中这样只为了一个变量实现一个类的 ChangeNotifier有点繁琐。Flutter 为这种单变量值发生变化的 Notifier 提供了一个专门的类ValueNotifier。CounterController 可以直接写成
class CounterController {ValueNotifier count ValueNotifier(0); // ValueNotifier 本身继承了 ChangeNotifier
}注意这样修改之后count 变成了一个 Notifier所以使用 count 值的时候要写成 count.value ListenableBuilder(listenable: widget.controller.count, // 每当 ValueNotifier count 的value 变化builder: (BuildContext context, Widget? child) { // 重新 buildreturn Text(${widget.controller.count.value});},),如果同时需要监听多个 Notifier 的变化使用 Listenable.merge ListenableBuilder(listenable: Listenable.merge([widget.controller.count,widget.controller.fontSize])builder: (BuildContext context, Widget? child) { // 重新 buildreturn Text(${widget.controller.count.value},style: TextStyle(fontSize: widget.controller.fontSize.value),);},),如果是这样多个 Notifier可以统一放在一起提升到父组件中这就是控制器类Controller
组件在开发的时候一般都遵循这个规范使用 Controller。所以我们用别人写的组件的时候只需要用他们写好的 Controller 就能实现父组件控制子组件的状态且不影响子组件自己控制自己状态了
继承式组件 InheritedWidget
如果 state 被提升到顶部就要一层一层传这样每一层组件的构造函数都有大量的参数
InheritedWidget 可以解决这一问题
class MyColor extends InteritedWidget {final Color color;MyColor({super.key, required super.child, required this.color});
}// 在组件树较高的位置用 MyColor 包裹
// 这样里面的所有组件都能访问到 color 这个属性
class MyApp extends StatelessWidget {const MyApp({Key? key}): super(key: key);overrideWidget build(BuildContext context) {return MyColor(child: MaterialApp(....),color: Color.red);}
}dependOnInheritedWidgetOfExactType依赖于 继承式组件 of 特定的 Type 因为在某个组件所在的那一支上可能有其他的继承式组件我们要找那个特定的继承式组件这里 MyColor
具体的寻找方法就是从这个组件向上向组件树的根部去找直到找到 ExactType 如果有多个同样的组件MyColor选最近的那一个
组件如何访问到这个继承式组件的属性呢
Widget build(BuildContext context) {final myColor context.dependOnInheritedWidgetOfExactTypeMyColor();return Container(color: myColor.color,...);
}关于 updateShouldNotify
InteritedWidget 有一个函数 updateShouldNotify是指当 InheritedWidget 发生变化的时候需不需要通知相关的子组件进行重绘
按理说变了肯定要通知要不然不白变了吗
但是有的时候InteritedWidget 的属性是被 setState 变掉的setState 本身就会让子组件刷新所以不用通知子组件本来就是新的
class MyColor extends InteritedWidget {final Color color;// color 是父组件传进来的是父组件的 state。父组件 setState 后子组件也重绘了MyColor({super.key, required super.child, required this.color});overridebool updateShouldNotify(covariant Inherited oldWidget) {// return true;return false; // 颜色也会变化不过不是 updateShouldNotify 导致的}
}虽然能够成功更新颜色但这种一 setState全局组件就要重绘不是我们想看到的所以要多加 const
return const Child();这样的话父组件里的 state 发生变化子组件就不会自动重绘了 这样 updateShouldNotify 的重要性就凸显出来了
override
bool updateShouldNotify(covariant Inherited oldWidget) {return color ! oldWidget.color;
}当新的 color 不等于旧的 color 时告诉子组件刷新 如果是希望获取一次 color 后就不管了不再监听之后 color 的更新可以使用 getInheritedWidgetOfExactType final myColor context.getInheritedWidgetOfExactTypeMyColor();在 color 更新后如果 updateShouldNotify 为 true且子组件是 dependOnInheritedWidgetOfExactType也会调用 didChangeDependencies 这个生命周期函数
关于 of
如果能将 dependOnInheritedWidgetOfExactType 封装起来不用每次子组件使用的时候都写这么长一串那就更好了。直接将这个封装到 MyColor 这个继承式组件中
有两种of 和 maybeOf of 指一定能从这个 context 组件树往上找中找到 MyColor maybeOf 指有可能能找到可能为空
class MyColor extends InteritedWidget {final Color color;MyColor({super.key, required super.child, required this.color});static MyColor of(BuildContext context) {return context.dependOnInheritedWidgetOfExactTypeMyColor()!;// !代表确信不为空}// 或static MyColor maybeOf(BuildContext context) {return context.dependOnInheritedWidgetOfExactTypeMyColor();}overridebool updateShouldNotify(covariant Inherited oldWidget) {return color ! oldWidget.color;}
}通过这种写法子组件找到继承式组件就能更加简洁了
color: MyColor.of(context).color;
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/917452.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!