【Flutter學習】組件通訊(父子、兄弟)

一,概述 

   flutter一個重要的特性就是組件化。組件分爲兩種狀態,一種是StatefulWidget有狀態組件,一種是StatelessWidget無狀態組件。 無狀態組件不能更新狀態,有狀態組件具備相似刷新的機制,可更改狀態。
  功能模塊均可以經過繼承兩種狀態組件實現功能模塊封裝。組件間通訊,通常存在一下兩種關係。app

      • 父子組件通訊
      • 兄弟組件通訊       

二, 通訊實現方式

  • 回調通訊
    • 需求「點擊子組件,修改父組件的背景顏色與子組件背景顏色一致」
    • 代碼實現
      //父組件
      
      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'), ), ); } }

       

    • 功能實現
      less

    • 使用場景:通常用於子組件對父組件傳值。

  • InheritedWidget 數據共享ide

    • 場景:業務開發中常常會碰到這樣的狀況,多個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是一個包含單個值的變動通知器,當它的值改變的時候,會通知它的監聽。

    1. 定義ValueNotifierData類,繼承ValueNotifier
      class ValueNotifierData extends ValueNotifier<String> {
        ValueNotifierData(value) : super(value);
      }
    1. 定義_WidgetOne,包含一個ValueNotifierData的實例。
      class _WidgetOne extends StatefulWidget {
        _WidgetOne({this.data});
        final ValueNotifierData data;
        @override
        _WidgetOneState createState() => _WidgetOneState();
      }
    1. _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;
          });
    1. 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';
          }),
        );
      }
     
  • 第三方插件
      在這裏運用event_bus來實現傳值,用於組件與組件之間的傳值。
    • event_bus 
      • 引入插件
        import 'package:event_bus/event_bus.dart';
      • event_bus用法。組件化

        • 新建消息監測類ui

          import 'package:event_bus/event_bus.dart';
            EventBus eventBus = new EventBus();
            class TransEvent{
             String text;
             TransEvent(this.text);
            }
        • 監測類變化this

          eventBus.on<TransEvent>().listen((TransEvent data) => show(data.text));
          void show(String val) {
           setState(() {
            data = val;
           });
          }
        • 觸發消息變化spa

          eventBus.fire(new TransEvent('$inputText'));
      • 使用場景:這樣咱們就能夠根據這些來實現組件之間的傳值。
相關文章
相關標籤/搜索