Flutter用状态管理起源类InheritedWidget来定义自己的provider

提笔缘由
状态管理伴随着Flutter的发展日益丰富,如果说响应式是Flutter的特色,那么状态管理的就是实现方式,从最初的provide再到官方认可的provider,还有scoped_model,event_bus,get,riverpod,BloC,redux等等太多。作为开发者,对这些状态管理实现方式可以了解一二,当然因为学疏才浅,我只能略懂。Stream和InheritedWidget两种不同方式实现状态管理,项目中常用的Provider核心就是InheritedWidget,所以通过探究该类来侧面理解provider,在实践中很少用到InheritedWidget,但他是个幕后英雄。
关联知识点
- InheritedWidget
- ValueNotifier
- ValueListenBuilder
- Builder
- ChangeNotifier
- ValueListenable
类InheritedWidget
构造函数很简单,需要必传一个Widget,因为只有他的child,才能获取到存放在树梢的数据。
InheritedWidget({Key key,required Widget child})
继承InheritedWidget后必须override的方法,作用是你来决定通知变化的条件,以便节省资源。
updateShouldNotify(
covariant InheritedWidget oldWidget
) → bool
继承的新类需要一个静态方法 of(context),也是核心的一步,child就是通过of获取在树梢的数据,借助context.dependOnInheritedWidgetOfExactType方法来获取。
最简单示例
用最简单的例子演示InheritedWidget使用步骤,定义类ColorInherited继承InheritedWidget,里面存放颜色color和大小size,ColorInherited用于下面的child获取。ColorInherited可以放在Scaffold上下都可以,注意点是需要类Builder促使生成context,否则会因为context问题而报错,第二个注意点是dependOnInheritedWidgetOfExactType方法是泛型,要传入具体类。示例代码如下:
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: ColorInheritedPage()));
class ColorInheritedPage extends StatelessWidget {
const ColorInheritedPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('InheritedWidget演示')),
body: ColorInherited(
size: const Size(300, 100),
color: Colors.brown,
child: Builder(builder: (context) {
var provider = ColorInherited.of(context);
return Container(
height: provider.size.height,
width: provider.size.width,
color: provider.color,
);
}),
),
);
}
}
class ColorInherited extends InheritedWidget {
final Color color;
final Size size;
const ColorInherited({
super.key,
required super.child,
required this.color,
required this.size,
});
static ColorInherited of(BuildContext context) {
var inherited = context.dependOnInheritedWidgetOfExactType<ColorInherited>();
assert(inherited != null, 'error:No ColorInherited ');
return inherited!;
}
@override
bool updateShouldNotify(covariant ColorInherited oldWidget) {
return oldWidget.size != size || oldWidget.color != color;
}
}
计数器示例
在理解InheritedWidget如何使用后,我们可继续探究。上面例子中是静态数据,页面没有任何变化,现在用他来做个计数器的页面,是一种有变化的状态,需要及时去响应,就可以结合ChangeNotifier类,这个是不是熟悉的味道,在provider中定义数据类时需要继承他,借助ChangeNotifier定义自己的数据,存放一个变量,是需要ValueNotifier类,
//存放需要监听的变量
class CounterNotify extends ChangeNotifier {
final countState = ValueNotifier<int>(0);
void add() => countState.value++;
}
而ValueListenableBuilder负责在页面上响应变化做到局部刷新,ValueNotifier和ValueListenableBuilder配套使用。
有数据源之后,需要借助InheritedWidget来放在树上,其下child随时取用,这个状态管理类CounterInherited里有个变量是CounterNotify类型的,也就是数据源。
//类似于Provider类
class CounterInherited extends InheritedWidget {
const CounterInherited({
super.key,
required super.child,
required this.counterNotify,
});
final CounterNotify counterNotify;
static CounterInherited of(BuildContext context) {
var inherited = context.dependOnInheritedWidgetOfExactType<CounterInherited>();
assert(inherited != null, '在context中未找到CounterInherited类');
return inherited!;
}
@override
bool updateShouldNotify(covariant CounterInherited oldWidget) {
return oldWidget.counterNotify != counterNotify;
}
}
万事具备,只欠显示在页面上,ValueNotifier和ValueListenableBuilder配套使用,一个负责页面端显示,一个负责状态变化及通知。其实更严格地说是ValueListenable和ValueListenableBuilder配套使用,ValueNotifier只是抽象类ValueListenable的继承者,还有熟悉的类Animation也是属于ValueListenable,
//构造函数
ValueListenableBuilder({
Key? key,
required ValueListenable<T> valueListenable, //对应数据源的变量
required ValueWidgetBuilder<T> builder,
Widget? child
})
//抽象类ValueListenable的继承者有: ValueNotifier、Animation、RouteInformationProvider、SelectionHandler。
用InheritedWidget和ValueListenable组装的计数器完成,代码如下:
//空安全,Flutter3.3.2
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: CounterInheritedPage()));
class CounterInheritedPage extends StatelessWidget {
const CounterInheritedPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return CounterInherited(
counterNotify: CounterNotify(),
child: Scaffold(
appBar: AppBar(title: const Text('InheritedWidget计数器')),
//需要Builder促使context实例,否则无context
body: Builder(builder: (context) {
var provider = CounterInherited.of(context).counterNotify;
return Center(
child: GestureDetector(
onTap: provider.add,
//局部刷新
child: ValueListenableBuilder(
valueListenable: provider.countState,
builder: (context, value, child) {
return Text('${provider.countState.value}');
},
),
),
);
}),
),
);
}
}
//类似于Provider类
class CounterInherited extends InheritedWidget {
const CounterInherited({
super.key,
required super.child,
required this.counterNotify,
});
final CounterNotify counterNotify;
static CounterInherited of(BuildContext context) {
var inherited = context.dependOnInheritedWidgetOfExactType<CounterInherited>();
assert(inherited != null, '在context中未找到CounterInherited类');
return inherited!;
}
@override
bool updateShouldNotify(covariant CounterInherited oldWidget) {
return oldWidget.counterNotify != counterNotify;
}
}
//存放需要监听的变量
class CounterNotify extends ChangeNotifier {
final countState = ValueNotifier<int>(0);
void add() => countState.value++;
}
转载自:https://juejin.cn/post/7154242747719745572