Flutter 中Widget 多種多樣,有UI的,固然也有功能型的組件InheritedWidget 組件就是Flutter 中的一個功能組件,它能夠實現Flutter 組件之間的數據共享,他的數據傳遞方向在Widget樹傳遞是從上到下的。android
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/15 0015
/// email: maoqitian068@163.com
/// des: InheritedWidget是Flutter中很是重要的一個功能型組件,它提供了一種數據在widget樹中從上到下傳遞、共享的方式
import 'package:flutter/material.dart';
class ShareDataWidget extends InheritedWidget {
final int data; //須要在子樹中共享的數據,保存點擊次數
ShareDataWidget( {@required this.data,Widget child})
:super(child:child);
// 子樹中的widget經過該方法獲取ShareDataWidget,從而獲取共享數據
static ShareDataWidget of(BuildContext context){
return context.inheritFromWidgetOfExactType(ShareDataWidget);
}
//繼承 InheritedWidget 實現的方法 返回值 決定當data發生變化時,是否通知子樹中依賴data的Widget 更新數據
@override
bool updateShouldNotify(ShareDataWidget oldWidget) {
//若是返回true,則子樹中依賴(build函數中有調用)本widget的子widget的`state.didChangeDependencies`會被調用
return oldWidget.data != data;
}
}
複製代碼
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/15 0015
/// email: maoqitian068@163.com
/// des: 測試 ShareDataWidget
import 'package:flutter/material.dart';
import 'package:flutter_hellow_world/InheritedWidget/ShareDataWidget.dart';
class TestShareDataWidget extends StatefulWidget {
@override
_TestShareDataWidgetState createState() => _TestShareDataWidgetState();
}
class _TestShareDataWidgetState extends State<TestShareDataWidget> {
@override
void didChangeDependencies() {
super.didChangeDependencies();
//上層 widget中的InheritedWidget改變(updateShouldNotify返回true)時會被調用。
//若是build中沒有依賴InheritedWidget,則此回調不會被調用。
print("didChangeDependencies");
}
@override
Widget build(BuildContext context) {
//顯示 ShareDataWidget 數據變化,若是build中沒有依賴InheritedWidget,則此回調不會被調用。
return Text(ShareDataWidget.of(context).data.toString());
}
}
複製代碼
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/15 0015
/// email: maoqitian068@163.com
/// des: 建立一個按鈕,每點擊一次,就將ShareDataWidget的值自增
import 'package:flutter/material.dart';
import 'package:flutter_hellow_world/InheritedWidget/ShareDataWidget.dart';
import 'package:flutter_hellow_world/InheritedWidget/TestShareDataWidget.dart';
class InheritedWidgetTest extends StatefulWidget {
@override
_InheritedWidgetTestState createState() => _InheritedWidgetTestState();
}
class _InheritedWidgetTestState extends State<InheritedWidgetTest> {
int count = 0;
@override
Widget build(BuildContext context) {
return Center(
child: ShareDataWidget(
data: count, //共享數據 data
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: TestShareDataWidget()//子widget中依賴ShareDataWidget
),
RaisedButton(
child: Text("計數增長"),
onPressed: (){
setState(() {
++ count;
});
},
)
],
),
),
);
}
}
複製代碼
代碼很簡單,建立一個按鈕,每點擊一次,就將ShareDataWidget的data值加一,而前面建立的TestShareDataWidget中依賴了ShareDataWidget的data值,若是數據共享則它的值就會跟隨變化。git
運行效果github
I/flutter ( 7082): didChangeDependencies
複製代碼
// 子樹中的widget獲取共享數據 方法
static ShareDataWidget of(BuildContext context){
//return context.inheritFromWidgetOfExactType(ShareDataWidget);
//使用 ancestorInheritedElementForWidgetOfExactType 方法當數據變化則不會調用 子widget 的didChangeDependencies 方法
return context.ancestorInheritedElementForWidgetOfExactType(ShareDataWidget).widget;
}
複製代碼
/**
* framework.dart inheritFromWidgetOfExactType和ancestorInheritedElementForWidgetOfExactType方法源碼
*/
@override
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return inheritFromElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
@override
InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
return ancestor;
}
複製代碼
/**
* framework.dart inheritFromElement方法源碼
*/
@override
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
複製代碼
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019-11-17
/// email: maoqitian068@163.com
/// des: 實現InheritedWidget 保存須要共享的數據InheritedWidget
import 'package:flutter/material.dart';
class InheritedProvider<T> extends InheritedWidget{
//共享數據 外部傳入
final T data;
InheritedProvider({@required this.data, Widget child}):super(child:child);
@override
bool updateShouldNotify(InheritedProvider<T> oldWidget) {
///返回true,則每次更新都會調用依賴其的子孫節點的`didChangeDependencies`方法。
return true;
}
}
複製代碼
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019-11-17
/// email: maoqitian068@163.com
/// des: 訂閱者
import 'package:flutter/material.dart';
import 'package:flutter_theme_change/provider/InheritedProvider.dart';
// 該方法用於在Dart中獲取模板類型
Type _typeOf<T>(){
return T;
}
class ChangeNotifierProvider<T extends ChangeNotifier> extends StatefulWidget{
final Widget child;
final T data;
ChangeNotifierProvider({Key key,this.child,this.data});
//方便子樹中的widget獲取共享數據
static T of<T> (BuildContext context,{bool listen = true}){ //listen 是否註冊依賴關係 默認註冊
final type = _typeOf<InheritedProvider<T>>();
final provider = listen ? context.inheritFromWidgetOfExactType(type) as InheritedProvider<T> :
context.ancestorInheritedElementForWidgetOfExactType(type)?.widget as InheritedProvider<T>;
return provider.data;
}
@override
State<StatefulWidget> createState() {
return _ChangeNotifierProviderState<T>();
}
}
class _ChangeNotifierProviderState<T extends ChangeNotifier> extends State<ChangeNotifierProvider<T>>{
@override
Widget build(BuildContext context) {
//構建 InheritedProvider
return InheritedProvider<T>(
data: widget.data,
child: widget.child,
);
}
}
複製代碼
class _ChangeNotifierProviderState<T extends ChangeNotifier> extends State<ChangeNotifierProvider<T>>{
@override
void initState() {
// 給model添加監聽器
widget.data.addListener(update);
super.initState();
}
@override
void didUpdateWidget(ChangeNotifierProvider<T> oldWidget) {
//當Provider更新時,若是新舊數據不"==",則解綁舊數據監聽,同時添加新數據監聽
if(widget.data != oldWidget.data){
oldWidget.data.removeListener(update);
widget.data.addListener(update);
}
super.didUpdateWidget(oldWidget);
}
// build方法 省略
........
@override
void dispose() {
// 移除model監聽器
widget.data.removeListener(update);
super.dispose();
}
void update() {
//若是數據發生變化(model類調用了notifyListeners),從新構建InheritedProvider
setState(() => {
});
}
複製代碼
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/18 0018
/// email: maoqitian068@163.com
/// des: 事件 消費者 得到當前context和指定數據類型的Provider
import 'package:flutter/material.dart';
import 'package:flutter_theme_change/provider/ChangeNotifierProvider.dart';
class Consumer<T> extends StatelessWidget{
final Widget child;
//得到當前context
final Widget Function(BuildContext context, T value) builder;
Consumer({Key key,@required this.builder,this.child}):assert(builder !=null),super(key:key);
@override
Widget build(BuildContext context) { //默認綁定 註冊依賴關係
return builder(context,ChangeNotifierProvider.of<T>(context)); //自動獲取Model 獲取更新的數據
}
}
複製代碼
上一節中手寫了一個很是簡單基於InheritedWidget的Provider數據共享組件,接下來經過一個切換主題的例子來使用剛剛寫好的ChangeNotifierProvider。數組
主題切換這裏簡單的改變主題顏色,因此共享數據就是顏色值,Demo 思路爲使用Dialog,提供可選擇的主題顏色,而後點擊對應顏色則切換應用主題顏色,接下來一塊兒實現。bash
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/18 0018
/// email: maoqitian068@163.com
/// des: 主題 model
import 'package:flutter/material.dart';
class ThemeModel extends ChangeNotifier {
int settingThemeColor ;
ThemeModel(this.settingThemeColor);
void changeTheme (int themeColor){
this.settingThemeColor = themeColor;
// 通知監聽器(訂閱者),從新構建InheritedProvider, 更新狀態。
notifyListeners();
}
}
複製代碼
class _MyHomePageState extends State<MyHomePage> {
int themeColor =0;
@override
void initState() {
super.initState();
themeColor = sp.getInt(SharedPreferencesKeys.themeColor);
if(themeColor == null ){
themeColor = 0xFF3391EA;//默認藍色
}
}
@override
Widget build(BuildContext context) {
return Center(
child: ChangeNotifierProvider<ThemeModel>(
data: ThemeModel(themeColor),
child: Consumer<ThemeModel>(
builder: (BuildContext context,themeModel){
return MaterialApp(
theme: ThemeData(
primaryColor: Color(themeModel.settingThemeColor),
),
home: Scaffold(
appBar: AppBar(
title: Text("Flutter Theme Change"),
actions: <Widget>[
Builder(builder: (context){
return IconButton(icon: new Icon(Icons.color_lens), onPressed: (){
_changeColor(context);
});
},)
// onPressed 點擊事件
],
),
body: Center(
child: Text("主題變化測試"),
)
),
);
},
),
),
);
}
void _changeColor(BuildContext context) {
buildSimpleDialog(context);
}
複製代碼
class SingleThemeColor extends StatelessWidget {
final int themeColor;
final String colorName;
const SingleThemeColor({Key key,this.themeColor, this.colorName}):
super(key:key);
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () async{
print("點擊了改變主題");
//改變主題
ChangeNotifierProvider.of<ThemeModel>(context,listen: false).changeTheme(this.themeColor);
await SpUtil.getInstance()..putInt(SharedPreferencesKeys.themeColor, this.themeColor);
Navigator.pop(context);
},
child: new Column( // 豎直佈局
children: <Widget>[
Container(
width: 50,
height: 50,
margin: const EdgeInsets.all(5.0),
decoration: BoxDecoration( //圓形背景裝飾
borderRadius:BorderRadius.all(
Radius.circular(50)
),
color: Color(this.themeColor)
),
),
Text(
colorName,
style: TextStyle(
color: Color(this.themeColor),
fontSize: 14.0),
),
],
),
);
}
}
複製代碼