介绍:
在Theme.of(context)、 MediaQuery.of(context)等实现代码都能看到InheritedWidget的身影:
- Theme:Theme.of(context)->_InheritedTheme->InheritedTheme->InheritedWidget
- MediaQuery: MediaQuery.of(context)->MediaQuery->InheritedWidget
其实Theme和MediaQuery最终都是通过InheritedWidget实现的
InheritedWidget是Flutter中非常重要的一个功能型组件,它提供了一种数据在widget树中从上到下传递、共享的方式,比如我们在应用的根widget中通过InheritedWidget共享了一个数据,那么我们便可以在任意子widget中来获取该共享的数据!这个特性在一些需要在widget树中共享数据的场景中非常方便!
InheritedWidget使用步骤:
1、首先继承InheritedWidget,并实现自定义of静态方法和重写updateShouldNotify方法,如下:
/*
* <p> 数据共享InheritedWidget</p>
*
* @author 许志新
* @version 1.0 (2020-06-14)
*/
import 'package:flutter/material.dart';
class ShareDataInheritedWidget extends InheritedWidget {
final String name; //用于区分子widget调用的是哪个ShareDataInheritedWidget
//共享数据
final InheritedShareModel shareModel;
//点击+号的方法
final Function() increment;
//点击-号的方法
final Function() reduce;
ShareDataInheritedWidget(
this.name, {
Key key,
@required this.shareModel,
@required this.increment,
@required this.reduce,
@required Widget child,
}) : super(key: key, child: child);
///定义一个便捷方法,方便子树中的widget获取共享数据--InheritedWidget变化时会通知子widget的didChangeDependencies
static ShareDataInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType();
}
///定义一个便捷方法,方便子树中的widget获取共享数据--InheritedWidget变化时不会通知子widget的didChangeDependencies
static ShareDataInheritedWidget ofNotNoticeDependence(BuildContext context) {
return context
.getElementForInheritedWidgetOfExactType<ShareDataInheritedWidget>()
?.widget;
}
///该回调决定当data发生变化时,是否通知子树中依赖data的Widget
@override
bool updateShouldNotify(ShareDataInheritedWidget oldWidget) {
//如果返回true,则子树中依赖(build函数中有调用)本widget
//的子widget的`state.didChangeDependencies`会被调用
return (null != shareModel?.count &&
null != oldWidget?.shareModel?.count &&
shareModel.count != oldWidget.shareModel.count);
}
///用于展示信息
String showInfo() {
return '来自${name ?? ''} InheritedWidget:\ncount值为${shareModel?.count ?? ''}';
}
}
class InheritedShareModel {
final int count;
const InheritedShareModel(this.count);
}
说明:
- 自定义静态of方法:提供给子树中的widget来访问我们自定义InheritedWidget,进而可以访问InheritedWidget共享的数据或方法,如上面共享的shareModel或increment等;
- 重写updateShouldNotify方法:根据自己的业务,决定当共享发生变化时,是否通知子树中依赖共享数据的Widget;如果返回true,则子树中依赖本InheritedWidget的子widget的
state.didChangeDependencies
会被调用;返回false,则不会调用;
2、子widget使用自定义InheritedWidget的共享数据或方法,如下:
class DependenceWidgetA extends StatefulWidget {
@override
_DependenceWidgetAState createState() => new _DependenceWidgetAState();
}
class _DependenceWidgetAState extends State<DependenceWidgetA> {
@override
Widget build(BuildContext context) {
//使用InheritedWidget中的共享数据
final shareDataInheritedWidget = ShareDataInheritedWidget.of(context);
return Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: Text(
'DependenceWidgetA : ${shareDataInheritedWidget?.showInfo() ?? 'null'}',textAlign: TextAlign.center,
style: TextStyle(fontSize: 20.0),
),
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
//如果build中没有依赖InheritedWidget,则此回调不会被调用。
print("DependenceWidgetA didChangeDependencies");
}
}
说明:
- 访问方式:子widget通过InheritedWidget的自定义静态of方法来访问InheritedWidget共享的数据和方法;
- 有效前提条件:子widget必须在InheritedWidget的子树里,才能访问到InheritedWidget共享的数据和方法;否则通过静态of方法获取到的InheritedWidget实例为空;
3、InheritedWidget状态变化通知子widget,如下:
class DependenceWidgetA extends StatefulWidget {
@override
_DependenceWidgetAState createState() => new _DependenceWidgetAState();
}
class _DependenceWidgetAState extends State<DependenceWidgetA> {
@override
Widget build(BuildContext context) {
//使用InheritedWidget中的共享数据
final shareDataInheritedWidget = ShareDataInheritedWidget.of(context);
return Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: Text(
'DependenceWidgetA : ${shareDataInheritedWidget?.showInfo() ?? 'null'}',textAlign: TextAlign.center,
style: TextStyle(fontSize: 20.0),
),
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
//如果build中没有依赖InheritedWidget,则此回调不会被调用。
print("DependenceWidgetA didChangeDependencies");
}
}
说明:
- didChangeDependencies:当子widget依赖的InheritedWidget变化时,将会调用子widget的didChangeDependencies来通知子widget;
- 有效提前条件:
(1) 、InheritedWidget的updateShouldNotify返回true;
(2) 、InheritedWidget自定of静态方法里面实现调用的是dependOnInheritedWidgetOfExactType而不是getElementForInheritedWidgetOfExactType;
InheritedWidget完整使用示例:
自定义InheritedWidget->ShareDataInheritedWidget,用于共享数据和方法,如下:
class ShareDataInheritedWidget extends InheritedWidget {
final String name; //用于区分子widget调用的是哪个ShareDataInheritedWidget
//共享数据
final InheritedShareModel shareModel;
//点击+号的方法
final Function() increment;
//点击-号的方法
final Function() reduce;
ShareDataInheritedWidget(
this.name, {
Key key,
@required this.shareModel,
@required this.increment,
@required this.reduce,
@required Widget child,
}) : super(key: key, child: child);
///定义一个便捷方法,方便子树中的widget获取共享数据--InheritedWidget变化时会通知子widget的didChangeDependencies
static ShareDataInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType();
}
///定义一个便捷方法,方便子树中的widget获取共享数据--InheritedWidget变化时不会通知子widget的didChangeDependencies
static ShareDataInheritedWidget ofNotNoticeDependence(BuildContext context) {
return context
.getElementForInheritedWidgetOfExactType<ShareDataInheritedWidget>()
?.widget;
}
///该回调决定当data发生变化时,是否通知子树中依赖data的Widget
@override
bool updateShouldNotify(ShareDataInheritedWidget oldWidget) {
//如果返回true,则子树中依赖(build函数中有调用)本widget
//的子widget的`state.didChangeDependencies`会被调用
return (null != shareModel?.count &&
null != oldWidget?.shareModel?.count &&
shareModel.count != oldWidget.shareModel.count);
}
///用于展示信息
String showInfo() {
return '来自${name ?? ''} InheritedWidget:\ncount值为${shareModel?.count ?? ''}';
}
}
class InheritedShareModel {
final int count;
const InheritedShareModel(this.count);
}
说明:定义name共享变量主要是为了区别子widget访问到的共享数据来自哪个InheritedWidget
自定义InheritedWidget->TestInheritedWidget,用于理解InheritedWidget在树中的位置对子widget访问InheritedWidget的影响,如下:
class TestInheritedWidget extends InheritedWidget {
final String name; //用于区分子widget调用的是哪个TestInheritedWidget
TestInheritedWidget(
this.name, {
Key key,
@required Widget child,
}) : super(key: key, child: child);
static TestInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType();
}
///是否重建widget就取决于数据是否相同
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
return true;
}
///用于展示信息
String showInfo() {
return '来自${name ?? ''} TestInheritedWidget ';
}
}
各种widget访问自定义InheritedWidget里的共享数据和方法,如下:
class IncreaseWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final shareDataInheritedWidget = ShareDataInheritedWidget.of(context);
// print('IncreaseWidget : ${shareDataInheritedWidget?.showInfo()??'null'}');
return Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: RaisedButton(
textColor: Colors.black,
child: Text(
'+',
textAlign: TextAlign.center,
),
onPressed: shareDataInheritedWidget?.increment),
);
}
}
class DataWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final shareDataInheritedWidget = ShareDataInheritedWidget.of(context);
final inheritedShareModel = shareDataInheritedWidget?.shareModel;
// print('DataWidget : ${shareDataInheritedWidget?.showInfo() ?? ''}');
return Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: Text(
'来自${shareDataInheritedWidget?.name ?? ''} InheritedWidget:\ncount值为${inheritedShareModel?.count ?? ''}',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20.0),
),
);
}
}
class ReduceWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final shareDataInheritedWidget = ShareDataInheritedWidget.of(context);
// print('ReduceWidget : ${shareDataInheritedWidget?.showInfo() ?? ''}');
return Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: RaisedButton(
textColor: Colors.black,
child: Text('-', textAlign: TextAlign.center),
onPressed: shareDataInheritedWidget?.reduce),
);
}
}
class DependenceWidgetA extends StatefulWidget {
@override
_DependenceWidgetAState createState() => new _DependenceWidgetAState();
}
class _DependenceWidgetAState extends State<DependenceWidgetA> {
@override
Widget build(BuildContext context) {
//使用InheritedWidget中的共享数据
final shareDataInheritedWidget = ShareDataInheritedWidget.of(context);
return Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: Text(
'DependenceWidgetA : ${shareDataInheritedWidget?.showInfo() ?? 'null'}',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20.0),
),
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
//如果build中没有依赖InheritedWidget,则此回调不会被调用。
print("DependenceWidgetA didChangeDependencies");
}
}
class DependenceWidgetB extends StatefulWidget {
@override
_DependenceWidgetBState createState() => new _DependenceWidgetBState();
}
class _DependenceWidgetBState extends State<DependenceWidgetB> {
@override
Widget build(BuildContext context) {
//使用InheritedWidget中的共享数据
final shareDataInheritedWidget =
ShareDataInheritedWidget.ofNotNoticeDependence(context);
return Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: Text(
'DependenceWidgetB : ${shareDataInheritedWidget?.showInfo() ?? 'null'}',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20.0),
),
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
//如果build中没有依赖InheritedWidget,则此回调不会被调用。
print("DependenceWidgetB didChangeDependencies");
}
}
在app层使用InheritedWidget,如下:
void main() => runApp(App());
class App extends StatefulWidget {
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
InheritedShareModel _inheritedShareModel;
@override
void initState() {
super.initState();
_initData();
}
_initData() {
_inheritedShareModel = InheritedShareModel(0); //初始为0
}
_incrementCount() {
setState(() {
_inheritedShareModel =
InheritedShareModel(_inheritedShareModel.count + 2); //增幅度为2
});
}
_reduceCount() {
setState(() {
_inheritedShareModel =
InheritedShareModel(_inheritedShareModel.count - 2); //减幅度为2
});
}
@override
Widget build(BuildContext context) {
return ShareDataInheritedWidget(
'app层-在MaterialApp上',
shareModel: _inheritedShareModel,
increment: _incrementCount,
reduce: _reduceCount,
child: MaterialApp(
title: 'InheritedWidget使用示例',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: TestInheritedWidget('app层-在MaterialApp里面', child: HomePage()),
),
/* child: TestInheritedWidget('app层-在MaterialApp上',child: MaterialApp(
title: 'InheritedWidget使用示例',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: TestInheritedWidget('app层-在MaterialApp里面', child: HomePage()),
),),*/
);
}
}
在home页使用InheritedWidget,如下:
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return HomePageWidget();
}
@override
void deactivate() {
super.deactivate();
print('HomePage page deactivate');
}
@override
void dispose() {
super.dispose();
print('HomePage page dispose');
}
}
class HomePageWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _HomePageWidgetState();
}
}
class _HomePageWidgetState extends State<HomePageWidget> {
CiteModel _testCiteModel;
InheritedShareModel _inheritedShareModel;
@override
void initState() {
super.initState();
_initData();
_testCiteModel = CiteModel(1);
}
@override
void deactivate() {
super.deactivate();
print('test page deactivate ${_testCiteModel ?? 'null'}');
}
_initData() {
_inheritedShareModel = InheritedShareModel(1); //初始化为1
}
_incrementCount() {
setState(() {
_inheritedShareModel =
InheritedShareModel(_inheritedShareModel.count + 1); //增幅度为1
});
}
_reduceCount() {
setState(() {
_inheritedShareModel =
InheritedShareModel(_inheritedShareModel.count - 1); //减幅度为1
});
}
@override
Widget build(BuildContext context) {
return ShareDataInheritedWidget('home',
shareModel: _inheritedShareModel,
increment: _incrementCount,
reduce: _reduceCount,
child: Scaffold(
appBar: AppBar(
title: Text('InheritedWidget使用示例'),
),
body: Container(
child: SingleChildScrollView(
child: Container(
/* width: double.infinity,
height: double.infinity,*/
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 20,
),
IncreaseWidget(),
DataWidget(),
ReduceWidget(),
DependenceWidgetA(),
DependenceWidgetB(),
SizedBox(
height: 20,
),
RaisedButton(
child: Text("下一页"),
onPressed: () {
Navigator.push(
//跳转到第二个界面
context,
MaterialPageRoute(builder: (context) {
return NextPage();
}),
);
},
),
SizedBox(
height: 20,
),
RaisedButton(
child: Text("home context"),
onPressed: () {
final ShareDataInheritedWidget
shareDataInheritedWidget =
ShareDataInheritedWidget.of(context); //home context
print(
'home context ShareDataInheritedWidget : ${shareDataInheritedWidget?.showInfo() ?? 'null'}');
final TestInheritedWidget testInheritedWidget =
TestInheritedWidget.of(context);
print(
'home context TestInheritedWidget: ${testInheritedWidget?.showInfo() ?? 'null'}');
},
),
SizedBox(
height: 20,
),
Builder(builder: (BuildContext context) {
//context变成home 子widget context
return RaisedButton(
child: Text("home 子widget context"),
onPressed: () {
final ShareDataInheritedWidget
shareDataInheritedWidget =
ShareDataInheritedWidget.of(
context); //home 子widget context
print(
'home 子widget context ShareDataInheritedWidget: ${shareDataInheritedWidget?.showInfo() ?? 'null'}');
final TestInheritedWidget testInheritedWidget =
TestInheritedWidget.of(context);
print(
'home 子widget context TestInheritedWidget: ${testInheritedWidget?.showInfo() ?? 'null'}');
},
);
}),
SizedBox(
height: 20,
),
RaisedButton(
child: Text("测试type作为map key"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return TestTypePage();
}),
);
},
),
SizedBox(
height: 20,
),
RaisedButton(
child: Text("测试引用计数"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return TestModelCitePage(_testCiteModel);
}),
);
},
),
Padding(
padding: const EdgeInsets.only(
left: 10.0, top: 10.0, right: 10.0),
child: Text(
'Theme.of(context).textTheme\nMediaQuery.of(context).size等\n就是通过InheritedWidget实现的',
style: TextStyle(fontSize: 20.0),
),
),
],
),
),
),
width: double.infinity,
height: double.infinity,
),
));
}
}
从home页用路由跳到下一页使用InheritedWidget,如下:
class NextPage extends StatefulWidget {
@override
_NextPageState createState() => _NextPageState();
}
class _NextPageState extends State<NextPage> {
@override
void initState() {
super.initState();
//直接在iniState使用InheritedWidget会报错
/*ShareDataInheritedWidget shareDataInheritedWidget =
ShareDataInheritedWidget.of(context);
print(
'next page initState shareDataInheritedWidget: ${shareDataInheritedWidget?.showInfo() ?? 'null'}');*/
Future.delayed(Duration.zero, () {
final ShareDataInheritedWidget shareDataInheritedWidget =
ShareDataInheritedWidget.of(context);
print(
'next page initState delayed shareDataInheritedWidget: ${shareDataInheritedWidget?.showInfo() ?? 'null'}');
});
}
@override
Widget build(BuildContext context) {
final TestInheritedWidget testInheritedWidget =
TestInheritedWidget.of(context);
print(
'next page build TestInheritedWidget: ${testInheritedWidget?.showInfo() ?? 'null'}');
final ShareDataInheritedWidget shareDataInheritedWidget =
ShareDataInheritedWidget.of(context);
print(
'next page build ShareDataInheritedWidget: ${shareDataInheritedWidget?.showInfo() ?? 'null'}');
return Scaffold(
appBar: AppBar(
title: Text('下一页'),
),
body: Container(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
IncreaseWidget(),
DataWidget(),
ReduceWidget(),
],
),
),
);
}
}
操作如下:
效果.gif
示例说明:
- 自定义InheritedWidget通过name参数来区分InheritedWidget的类别和相同类别InheritedWidget在widget中的位置;
- <home页>界面展示的是子widget访问到的InheritedWidget是在widget树中离它最近的父InheritedWidget;
+<下一页>界面展示的是路由页面之前的InheritedWidget不能共享,除非InheritedWidget为MaterialApp祖先; - 更多内容请结合代码注释和demo理解;
详细信息请运行demo体验:InheritedWidget_Sample
从示例中可以得出以下结论:
- 子widget通过自定义InheritedWidget提供的of静态方法来访问自定义InheritedWidget的共享数据和方法;
- 子widget和InheritedWidget必须在同一棵树上,并且InheritedWidget为子widget的祖先,这样子widget才能访问到InheritedWidget共享的数据和方法;
- 当widget树中存在多个相同类别的InheritedWidget,子widget访问到的是离它最近的父InheritedWidget;
- InheritedWidget状态变化时,只有当InheritedWidget的of静态方法是dependOnInheritedWidgetOfExactType实现,并且updateShouldNotify返回true,才会通知子widget的didChangeDependencies;
- 用路由切换的页面,由于不在同一棵widget上,所以InheritedWidget在路由页面之间不起作用;
- 只有作为MaterialApp祖先的InheritedWidget,才能在路由页面之间共享;
InheritedWidget内部原理分析:
InheritedWidget原理分析请移步:InheritedWidget内部实现原理浅析
网友评论