How do i change a boolean in a StatefullWidget fro

2019-08-17 17:54发布

i'm brand new to Flutter.

I'm trying to open a panel pressing a button and than closing it by pressing a button on that panel.

I've managed to do it by writing the code in the same page.

What i can't do is splitting the code and keep everything working.

What I'm actually doing is calling a variable in the State of a widget that is initialized False and then with an if statement i'm calling: or an empty container or the panel i want.

When i press the button i call SetState(){} and the variable changes to true to let the panel appears, then in the panel there's button that do opposite thing.

Assuming that what i'm doing it is correct. How to i keep doing this with the panel refactored in a new page?

I've red something about streams and inherited widgets but i haven't completely understood

3条回答
再贱就再见
2楼-- · 2019-08-17 18:38

If you want to open an dialog (instead of what you call a "panel") you can simply give the selected data back when you close the dialog again. You can find a good tutorial here: https://medium.com/@nils.backe/flutter-alert-dialogs-9b0bb9b01d28

查看更多
老娘就宠你
3楼-- · 2019-08-17 18:42

If I understand correctly, you want to notify a StatefullWidget from another StatefullWidget. There are several approaches on this one but since you've mentioned Streams, I'll try to post an example and explain a bit this scenario.

So basically, you can consider the streams like a pipe linked to a faucet in one end and the other end it's added into a cup (the end can be split in multiple ends and put in multiple cups, "broadcast streams").

Now, the cup is the listener (subscriber) and waits for water to drop trough the pipe.

The faucet is the emitter, and it will emit water droplets when the faucet is opened.

The faucet can be opened when the other end is put into a cup, this is a smart faucet with some cool sensors, (the emitter will start emitting events when a subscriber is "detected).

The droplets are actual events that are happening in the the app.

Also you must remember to close the faucet in order to avoid a massive leak from your cup into the kitchen floor. (you must cancel the subscribers when you've done handling events to avoid a leak).

Now for your particular case here's the code snippet that kind of illustrate the above metaphor:

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();
  }
}

I hope that my analogy will help you understand a bit the streams concept.

查看更多
贼婆χ
4楼-- · 2019-08-17 18:46

you can navigate and return data from another screen like that :

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.'),
              ),
            )
          ],
        ),
      ),
    );
  }
}

for more details about navigation: https://flutter.dev/docs/cookbook/navigation/returning-data

查看更多
登录 后发表回答