【问题标题】:How do i change a boolean in a StatefullWidget from another one?如何从另一个有状态小部件中更改布尔值?
【发布时间】:2019-08-19 01:45:48
【问题描述】:

我是 Flutter 的新手。

我正在尝试按一个按钮打开一个面板,而不是通过按该面板上的一个按钮来关闭它。

我已经设法通过在同一页面中编写代码来做到这一点。

我不能做的是拆分代码并保持一切正常。

我实际上在做的是在初始化为 False 的小部件的 State 中调用一个变量,然后使用我正在调用的 if 语句:或者一个空容器或我想要的面板。

当我按下按钮时,我调用 SetState(){} 并且变量更改为 true 以让面板出现,然后在面板中有按钮执行相反的操作。

假设我正在做的事情是正确的。我如何在新页面中重构面板继续执行此操作?

我对流和继承的小部件有所了解,但我还没有完全理解

【问题讨论】:

    标签: flutter widget state setstate


    【解决方案1】:

    您可以像这样从另一个屏幕导航和返回数据:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MaterialApp(
        title: 'Returning Data',
        home: HomeScreen(),
      ));
    }
    
    class HomeScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Returning Data Demo'),
          ),
          body: Center(child: SelectionButton()),
        );
      }
    }
    
    class SelectionButton extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return RaisedButton(
          onPressed: () {
            _navigateAndDisplaySelection(context);
          },
          child: Text('Pick an option, any option!'),
        );
      }
    
      // A method that launches the SelectionScreen and awaits the result from
      // Navigator.pop!
      _navigateAndDisplaySelection(BuildContext context) async {
        // Navigator.push returns a Future that will complete after we call
        // Navigator.pop on the Selection Screen!
        final result = await Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => SelectionScreen()),
        );
    
        // After the Selection Screen returns a result, hide any previous snackbars
        // and show the new result!
        Scaffold.of(context)
          ..removeCurrentSnackBar()
          ..showSnackBar(SnackBar(content: Text("$result")));
      }
    }
    
    class SelectionScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Pick an option'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: RaisedButton(
                    onPressed: () {
                      // Close the screen and return "Yep!" as the result
                      Navigator.pop(context, 'Yep!');
                    },
                    child: Text('Yep!'),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: RaisedButton(
                    onPressed: () {
                      // Close the screen and return "Nope!" as the result
                      Navigator.pop(context, 'Nope.');
                    },
                    child: Text('Nope.'),
                  ),
                )
              ],
            ),
          ),
        );
      }
    }
    

    有关导航的更多详细信息: https://flutter.dev/docs/cookbook/navigation/returning-data

    【讨论】:

      【解决方案2】:

      如果我理解正确,您想从另一个StatefullWidget 通知StatefullWidget。这个有几种方法,但既然你提到了Streams,我将尝试发布一个示例并解释一下这种情况。

      因此,基本上,您可以将流视为一端连接到水龙头的管道,另一端将其添加到杯子中(该端可以分成多个端并放入多个杯子中,“广播流”) .

      现在,杯子是听者(订阅者)并等待水从管道中滴落。

      水龙头是发射器,打开水龙头会喷出水珠。

      当另一端放入杯子时可以打开水龙头,这是一个带有一些很酷的传感器的智能水龙头,(当“检测到订阅者”时,发射器将开始发射事件)。

      小滴是应用程序中发生的实际事件。

      此外,您还必须记住关闭水龙头,以免杯子大量泄漏到厨房地板上。 (您必须在处理完事件后取消订阅者以避免泄漏)。

      现在,对于您的特殊情况,这里的代码 sn-p 可以说明上述隐喻:

      class ThePannel extends StatefulWidget { // this is the cup
        final Stream<bool> closeMeStream; // this is the pipe 
      
        const ThePannel({Key key, this.closeMeStream}) : super(key: key);
      
        @override
        _ThePannelState createState() => _ThePannelState(closeMeStream);
      }
      
      class _ThePannelState extends State<ThePannel> {
        bool _closeMe = false;
        final Stream<bool> closeMeStream;
        StreamSubscription _streamSubscription;
      
        _ThePannelState(this.closeMeStream);
      
        @override
        void initState() {
          super.initState();
          _streamSubscription = closeMeStream.listen((shouldClose) { // here we listen for new events coming down the pipe
            setState(() {
              _closeMe = shouldClose; // we got a new "droplet" 
            });
          });
        }
      
        @override
        void dispose() {
          _streamSubscription.cancel(); // THIS IS QUITE IMPORTANT, we have to close the faucet 
          super.dispose();
        }
      
        @override
        Widget build(BuildContext context) {
          return Stack(
            children: <Widget>[
              SomeWidgetHere(shouldClose: _closeMe), 
              RaisedButton(
                onPressed: () {
                  setState(() {
                    _closeMe = true;
                  });
                },
              )
            ],
          );
        }
      }
      
      class SomeWidgetThatUseThePreviousOne extends StatefulWidget { // this one is the faucet, it will emit droplets 
        @override
        _SomeWidgetThatUseThePreviousOneState createState() =>
            _SomeWidgetThatUseThePreviousOneState();
      }
      
      class _SomeWidgetThatUseThePreviousOneState
          extends State<SomeWidgetThatUseThePreviousOne> {
        final StreamController<bool> thisStreamWillEmitEvents = StreamController(); // this is the end of the pipe linked to the faucet
      
        @override
        Widget build(BuildContext context) {
          return Stack(
            children: <Widget>[
              ThePannel(closeMeStream: thisStreamWillEmitEvents.stream), // we send the other end of the pipe to the cup
              RaisedButton(
                child: Text("THIS SHOULD CLOSE THE PANNEL"),
                onPressed: () {
                  thisStreamWillEmitEvents.add(true); // we will emit one droplet here
                },
              ),
              RaisedButton(
                child: Text("THIS SHOULD OPEN THE PANNEL"),
                onPressed: () {
                  thisStreamWillEmitEvents.add(false); // we will emit another droplet here
                },
              )
            ],
          );
        }
      
        @override
        void dispose() {
           thisStreamWillEmitEvents.close(); // close the faucet from this end.
           super.dispose();
        }
      }
      

      我希望我的类比能帮助你稍微理解流的概念。

      【讨论】:

      • 我花了一段时间才明白,我仍然不确定我是否完全明白,但这是我想要实现的。这是最简单和/或最有效的方法吗?谢谢你的比喻。
      • 我不能建议最简单和/或最有效的部分,这取决于具体情况以及您对概念的熟悉程度,可以使用 @ 实现相同的方法987654325@ 模式,所以我认为你应该多花一点时间在这些概念上,然后决定哪个更适合你。
      【解决方案3】:

      如果您想打开一个对话框(而不是您所谓的“面板”),您只需在再次关闭对话框时返回所选数据即可。 你可以在这里找到一个很好的教程:https://medium.com/@nils.backe/flutter-alert-dialogs-9b0bb9b01d28

      【讨论】:

        猜你喜欢
        • 2021-12-26
        • 1970-01-01
        • 2021-12-26
        • 2019-12-17
        • 2021-07-20
        • 1970-01-01
        • 2019-10-06
        • 2023-01-13
        • 2021-01-01
        相关资源
        最近更新 更多