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), ), ], ), ); } } 複製代碼