【问题标题】:Flutter - calling setState() from other widgetFlutter - 从其他小部件调用 setState()
【发布时间】:2019-03-16 15:27:17
【问题描述】:

是否可以从其他小部件 onPressed() 方法调用特定小部件(嵌入在其他小部件中)的 setState() 以便仅重绘 那个 小部件?

我想点击按钮并查看“MyTextWidget”的状态发生变化。其余布局相同,没有任何变化,因此不应重写。

这是我的代码:

import 'package:flutter/material.dart';
void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Timer',
      theme: new ThemeData(
        primaryColor: Colors.grey.shade800,
      ),
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {

  int _seconds = 1;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("title"),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: <Widget>[
          MyTextWidget(), //just update this widget
          Divider(),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.add_circle),
                onPressed: _addPressed,
                iconSize: 150.0,
              ),
              IconButton(
                icon: Icon(Icons.remove_circle),
                onPressed: ()=> print("to be implemented"),
                iconSize: 150.0,
              ),
            ],
          )
        ],
      ),
    );
  }

  void _addPressed() {
    //somehow call _updateSeconds()
  }
}

这是我要更新的有状态的 MyTextWidget。

class MyTextWidget extends StatefulWidget{
  @override
  _MyTextWidgetState createState() => _MyTextWidgetState();
}

class _MyTextWidgetState extends State<MyTextWidget> {

  int secondsToDisplay = 0;

  void _updateSeconds(int newSeconds) {
    setState(() {
      secondsToDisplay = newSeconds;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Text(
      secondsToDisplay.toString(),
      textScaleFactor: 5.0,
    );
  }
}

我想要实现的目标似乎很简单,但我无法弄清楚。想象一下,如果“MyTextWidget”被埋在巨大的布局树中,每次我想更新它时,我都需要重新绘制整个树。

【问题讨论】:

    标签: dart flutter


    【解决方案1】:

    这是使用流的可能解决方案:

    import 'dart:async';
    
    import 'package:flutter/material.dart';
    void main() => runApp(new MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Timer',
          theme: new ThemeData(
            primaryColor: Colors.grey.shade800,
          ),
          home: new MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatelessWidget {
    
      StreamController<int> _controller = StreamController<int>();
    
      int _seconds = 1;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Column(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: <Widget>[
              MyTextWidget(stream: _controller.stream), //just update this widget
              Divider(),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: <Widget>[
                  IconButton(
                    icon: Icon(Icons.add_circle),
                    onPressed: _addPressed,
                    iconSize: 150.0,
                  ),
                  IconButton(
                    icon: Icon(Icons.remove_circle),
                    onPressed: ()=> _controller.add(_seconds++),
                    iconSize: 150.0,
                  ),
                ],
              )
            ],
          ),
        );
      }
    
      void _addPressed() {
        //somehow call _updateSeconds()
      }
    }
    
    class MyTextWidget extends StatefulWidget{
    
      final Stream<int> stream;
    
      MyTextWidget({this.stream});
    
      @override
      _MyTextWidgetState createState() => _MyTextWidgetState();
    }
    
    class _MyTextWidgetState extends State<MyTextWidget> {
    
      int secondsToDisplay = 0;
    
      void _updateSeconds(int newSeconds) {
        setState(() {
          secondsToDisplay = newSeconds;
        });
      }
    
      @override
      void initState() {
        super.initState();
        widget.stream.listen((seconds) {
          _updateSeconds(seconds);
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Text(
          secondsToDisplay.toString(),
          textScaleFactor: 5.0,
        );
      }
    }
    

    【讨论】:

    • 这是一个很好的解决方案,伙计!
    • 哇!太简单。这件事让我很紧张。
    【解决方案2】:

    有不同的方法来实现这一点。真正简单的是InheritedWidget 小部件,就像这样:

    import 'package:flutter/material.dart';
    
    void main() => runApp(new MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Timer',
          theme: new ThemeData(
            primaryColor: Colors.grey.shade800,
          ),
          home: new MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      @override
      MyHomePageState createState() {
        return new MyHomePageState();
      }
    }
    
    class MyHomePageState extends State<MyHomePage> {
      int _seconds = 1;
    
      @override
      Widget build(BuildContext context) {
        return new MyInheritedWidget(
          secondsToDisplay: _seconds,
          child: Scaffold(
    
            appBar: AppBar(
              title: Text("title"),
            ),
            body: Column(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[
                MyTextWidget(), //just update this widget
                Divider(),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: <Widget>[
                    IconButton(
                      icon: Icon(Icons.add_circle),
                      onPressed: _addPressed,
                      iconSize: 150.0,
                    ),
                    IconButton(
                      icon: Icon(Icons.remove_circle),
                      onPressed: () => print("to be implemented"),
                      iconSize: 150.0,
                    ),
                  ],
                )
              ],
            ),
          ),
        );
      }
    
      void _addPressed() {
        setState(() {
          _seconds++;
        });
      }
    }
    
    class MyTextWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final MyInheritedWidget inheritedWidget = MyInheritedWidget.of(context);
        return Text(inheritedWidget.secondsToDisplay.toString(),
          textScaleFactor: 5.0,
        );
      }
    }
    
    class MyInheritedWidget extends InheritedWidget {
      final int secondsToDisplay;
    
      MyInheritedWidget({
        Key key,
        @required this.secondsToDisplay,
        @required Widget child,
      }) : super(key: key, child: child);
    
      static MyInheritedWidget of(BuildContext context) {
        return context.inheritFromWidgetOfExactType(MyInheritedWidget);
      }
    
      @override
      bool updateShouldNotify(MyInheritedWidget oldWidget) =>
          secondsToDisplay != oldWidget.secondsToDisplay;
    }
    

    【讨论】:

    • 也许我没看懂,但是在这段代码中按下按钮会导致整个脚手架重绘,对吗?
    • 不幸的是,这是事实,但为了正确地做事,我们需要更复杂的方法,例如 flutter_reduxreactive extensions。更多信息在这里 - youtu.be/RS36gBEp8OI。我个人的建议是使用flutter_redux
    • 所以InheritedWidget 不能成为仅重绘树中一个特定小部件的解决方案?
    • 通过更深入的研究,我发现了这个很棒的视频,它解释了在 Flutter 中管理状态的不同方法。它涵盖了InheritedWidgetStreams。视频链接:youtube.com/watch?v=RS36gBEp8OIFor。在我的情况下,我猜流答案就是我想要的。
    【解决方案3】:

    我是 Flutter 的初学者。

    这是我的修改版,不知道这样有没有什么问题,但是可以的。

    1. 在 MyTextWidget 类中添加键值
    MyTextWidget({Key key}):super(key:key);
    
    1. 在 MyHomePage 类中声明一个 GlobalKey
    GlobalKey<_MyTextWidgetState> textGlobalKey = new GlobalKey<_MyTextWidgetState>();
    
    1. 添加 MyTextWidget 小部件
    MyTextWidget(key: textGlobalKey);
    
    1. 在 _addPressed() 中调用 _updateSeconds()
    void _addPressed() {
      //somehow call _updateSeconds()
      textGlobalKey.currentState._updateSeconds(_seconds++);
    }
    

    所有代码:

    import 'package:flutter/material.dart';
    void main() => runApp(new MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Timer',
          theme: new ThemeData(
            primaryColor: Colors.grey.shade800,
          ),
          home: new MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatelessWidget {
    
      GlobalKey<_MyTextWidgetState> textGlobalKey = new GlobalKey<_MyTextWidgetState>();
    
      int _seconds = 1;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Column(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: <Widget>[
              MyTextWidget(key: textGlobalKey), //just update this widget
              Divider(),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: <Widget>[
                  IconButton(
                    icon: Icon(Icons.add_circle),
                    onPressed: _addPressed,
                    iconSize: 150.0,
                  ),
                  IconButton(
                    icon: Icon(Icons.remove_circle),
                    onPressed: ()=> print("to be implemented"),
                    iconSize: 150.0,
                  ),
                ],
              )
            ],
          ),
        );
      }
    
      void _addPressed() {
        //somehow call _updateSeconds()
        textGlobalKey.currentState._updateSeconds(_seconds++);
      }
    }
    
    class MyTextWidget extends StatefulWidget{
    
      MyTextWidget({Key key}):super(key:key);
    
      @override
      _MyTextWidgetState createState() => _MyTextWidgetState();
    }
    
    class _MyTextWidgetState extends State<MyTextWidget> {
    
      int secondsToDisplay = 0;
    
      void _updateSeconds(int newSeconds) {
        setState(() {
          secondsToDisplay = newSeconds;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Text(
          secondsToDisplay.toString(),
          textScaleFactor: 5.0,
        );
      }
    }
    

    希望对你有帮助:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-06-13
      • 1970-01-01
      • 1970-01-01
      • 2021-05-01
      • 2020-08-18
      • 1970-01-01
      • 1970-01-01
      • 2020-11-26
      相关资源
      最近更新 更多