一,概述
flutter一個重要的特性就是組件化。組件分為兩種狀態,一種是StatefulWidget有狀態組件,一種是StatelessWidget無狀態組件。 無狀態組件不能更新狀態,有狀態組件具有類似刷新的機制,可更改狀態。
功能模塊都可以通過繼承兩種狀態組件實現功能模塊封裝。組件間通信,一般存在一下兩種關系。
-
-
- 父子組件通信
- 兄弟組件通信
-
二, 通信實現方式
- 回調通信
- 需求“點擊子組件,修改父組件的背景顏色與子組件背景顏色一致”
- 代碼實現
//父組件 class ParentWidget extends StatefulWidget { final String title; ParentWidget({Key key,this.title}):super(key:key); @override State<StatefulWidget> createState() { return new ParentWidgetState(); } } class ParentWidgetState extends State<ParentWidget> { Color containerBg = Colors.orange;
//回調函數 void changeBackgroundColor(Color newColor){ setState(() { containerBg = newColor;//修改狀態 }); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(widget.title), ), body: new Center( child: new GestureDetector( onTap: (){ changeBackgroundColor(Colors.orange); }, child: new Container( width: 300, height: 300, color: containerBg, alignment: Alignment.center, child: new Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ new childrenA(childrenACallBack: changeBackgroundColor), new childrenB(childrenBCallBack: changeBackgroundColor), ], ), ), ) ), ); } } //子組件(組件A) class childrenA extends StatelessWidget {
//定義接收父類回調函數的指針final ValueChanged<Color> childrenACallBack; childrenA({Key key,this.childrenACallBack}):super(key:key);
@override Widget build(BuildContext context) { return new GestureDetector( onTap: (){
//調用回調函數傳值 childrenACallBack(Colors.green); }, child: new Container( width: 80, height: 80, color: Colors.green, child: new Text('ChildrenA'), ), ); } } //子組件(組件B) class childrenB extends StatelessWidget { final ValueChanged<Color> childrenBCallBack; childrenB({Key key,this.childrenBCallBack}):super(key:key); @override Widget build(BuildContext context) { return new GestureDetector( onTap:(){ childrenBCallBack(Colors.red); }, child: new Container( width: 80, height: 80, color: Colors.red, child: new Text('ChildredB'), ), ); } } -
功能實現
- 使用場景:一般用於子組件對父組件傳值。
-
InheritedWidget 數據共享
- 場景:業務開發中經常會碰到這樣的情況,多個Widget需要同步同一份全局數據,比如點贊數、評論數、夜間模式等等。
- 代碼實現:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: new InheritedWidgetTestContainer(), ); } } //模型數據 class InheritedTestModel { final int count; const InheritedTestModel(this.count); } //哨所(自定義InheritedWidget類) class InheritedContext extends InheritedWidget { //構造函數 InheritedContext({ Key key, @required this.inheritedTestModel, @required this.increment, @required this.reduce, @required Widget child }):super(key:key,child:child); //變量 final InheritedTestModel inheritedTestModel; final Function() increment; final Function() reduce; //靜態方法 static InheritedContext of(BuildContext context){ InheritedContext contexts = context.inheritFromWidgetOfExactType(InheritedContext); return context.inheritFromWidgetOfExactType(InheritedContext); } //是否重建取決於Widget組件是否相同 @override bool updateShouldNotify(InheritedContext oldWidget) { return inheritedTestModel != oldWidget.inheritedTestModel; } } class TestWidgetA extends StatelessWidget { @override Widget build(BuildContext context) { final inheritedContext = InheritedContext.of(context); return new Padding( padding: const EdgeInsets.only(left: 10.0,top: 10.0,right: 10.0), child: new RaisedButton( textColor: Colors.black, child: new Text('+'), onPressed:inheritedContext.increment ), ); } } class TestWidgetB extends StatelessWidget { @override Widget build(BuildContext context) { final inheritedContext = InheritedContext.of(context); return new Padding( padding: const EdgeInsets.only(left: 10,top: 10,right: 10.0), child: new RaisedButton( textColor: Colors.black, child: new Text('-'), onPressed: inheritedContext.reduce ), ); } } class TestWidgetC extends StatelessWidget { @override Widget build(BuildContext context) { final inheritedContext = InheritedContext.of(context); final inheritedTestModel = inheritedContext.inheritedTestModel; return new Padding( padding: const EdgeInsets.only(left: 10.0,top: 10.0,right: 10.0), child: new RaisedButton( textColor: Colors.black, child: new Text('${inheritedTestModel.count}'), onPressed: (){ }, ), ); } } class InheritedWidgetTestContainer extends StatefulWidget { @override State<StatefulWidget> createState() { return new InheritedWidgetTestContainerState(); } } class InheritedWidgetTestContainerState extends State<InheritedWidgetTestContainer> { InheritedTestModel _inheritedTestModel; _initData(){ _inheritedTestModel = new InheritedTestModel(0); } @override void initState() { _initData(); super.initState(); } _incrementCount(){ setState(() { _inheritedTestModel = new InheritedTestModel(_inheritedTestModel.count + 1); }); } _reduceCount(){ setState(() { _inheritedTestModel = new InheritedTestModel(_inheritedTestModel.count - 1); }); } @override Widget build(BuildContext context) { return new InheritedContext( inheritedTestModel: _inheritedTestModel, increment: _incrementCount, reduce: _reduceCount, child: new Scaffold( appBar: new AppBar( title: new Text('InheritedWidgetTest'), ), body: new Center( child: new Column( children: <Widget>[ new TestWidgetA(), new TestWidgetB(), new TestWidgetC(), ], ), ) ), ); } }
- 功能實現
-
使用場景
一般用於父組件對子組件的跨組件傳值。
- Global Key通信
GlobalKey能夠跨Widget訪問狀態。
- 需求“點擊A子組件,修改B子組件的背景顏色為指定的‘藍色”
- 代碼實現
//父組件 class ParentWidget extends StatefulWidget { @override State<StatefulWidget> createState() { return new ParentWidgetState(); } } class ParentWidgetState extends State<ParentWidget> { @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text('組件化'), ), body: new Center( child: new Container( color: Colors.grey, width: 200, height: 200, child: new Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ new SubWidgetA(key: subAkey), new SubWidgetB(key: subBkey) ], ), ), ), ); } } //子組件A class SubWidgetA extends StatefulWidget { SubWidgetA({Key key}):super(key:key); @override State<StatefulWidget> createState() { return new SubWidgetAState(); } } class SubWidgetAState extends State <SubWidgetA> { Color _backgroundColors = Colors.red;//紅色 void updateBackGroundColors(Color colos){ setState(() { _backgroundColors = colos; }); } @override Widget build(BuildContext context) { return new GestureDetector( onTap: (){ subBkey.currentState.updateBackGroundColors(Colors.blue); setState(() { _backgroundColors = Colors.red; }); }, child: new Container( width: 80, height: 80, color:_backgroundColors, alignment: Alignment.center, child: new Text('SubWidgetA'), ), ); } } //子組件B class SubWidgetB extends StatefulWidget { SubWidgetB({Key key}):super(key:key); @override State<StatefulWidget> createState() { // TODO: implement createState return new SubWidgetBState(); } } class SubWidgetBState extends State<SubWidgetB> { Color _backgroundColors = Colors.green;//綠色 void updateBackGroundColors(Color colos){ setState(() { _backgroundColors = colos; }); } @override Widget build(BuildContext context) { return new GestureDetector( onTap: (){ subAkey.currentState.updateBackGroundColors(Colors.blue); setState(() { _backgroundColors = Colors.green; }); }, child: new Container( width: 80, height: 80, color: _backgroundColors, alignment: Alignment.center, child: new Text('SubWidgetB'), ), ); } }
- 功能實現
- 使用場景:一般用於跨組件訪問狀態
- ValueNotifier通信
ValueNotifier是一個包含單個值的變更通知器,當它的值改變的時候,會通知它的監聽。
- 定義ValueNotifierData類,繼承ValueNotifier
class ValueNotifierData extends ValueNotifier<String> { ValueNotifierData(value) : super(value); }
- 定義
_WidgetOne
,包含一個ValueNotifierData的實例。
class _WidgetOne extends StatefulWidget { _WidgetOne({this.data}); final ValueNotifierData data; @override _WidgetOneState createState() => _WidgetOneState(); }
_WidgetOneState
中給ValueNotifierData實例添加監聽。
@override initState() { super.initState(); widget.data.addListener(_handleValueChanged); info = 'Initial mesage: ' + widget.data.value; } void _handleValueChanged() { setState(() { info = 'Message changed to: ' + widget.data.value; });
- 在
ValueNotifierCommunication
組件中實例化_WidgetOne
,可以通過改變ValueNotifierData
實例的value來觸發_WidgetOneState
的更新。
@override Widget build(BuildContext context) { ValueNotifierData vd = ValueNotifierData('Hello World'); return Scaffold( appBar: AppBar(title: Text('Value Notifier Communication'),), body: _WidgetOne(data: vd), floatingActionButton: FloatingActionButton(child: Icon(Icons.refresh),onPressed: () { vd.value = 'Yes'; }), ); }
- 定義ValueNotifierData類,繼承ValueNotifier
- 第三方插件
在這里運用event_bus來實現傳值,用於組件與組件之間的傳值。
- event_bus
- 引入插件
import 'package:event_bus/event_bus.dart';
-
event_bus用法。
-
新建消息監測類
import 'package:event_bus/event_bus.dart'; EventBus eventBus = new EventBus(); class TransEvent{ String text; TransEvent(this.text); }
-
監測類變化
eventBus.on<TransEvent>().listen((TransEvent data) => show(data.text)); void show(String val) { setState(() { data = val; }); }
-
觸發消息變化
eventBus.fire(new TransEvent('$inputText'));
-
- 使用場景:這樣我們就可以根據這些來實現組件之間的傳值。
- 引入插件
- event_bus