How to use `GlobalKey` to maintain widgets' states when changing parents?

前端 未结 3 550
独厮守ぢ
独厮守ぢ 2020-12-25 15:18

In Emily Fortuna\'s article (and video) she mentions:

GlobalKeys have two uses: they allow widgets to change parents anywhere in your app without lo

3条回答
  •  無奈伤痛
    2020-12-25 15:59

    I would not recommend using GlobalKey for this task.

    You should pass the data around, not the widget, not the widget state. For example, if you want a Switch and a Slider like in the demo, you are better off just pass the actual boolean and double behind those two widgets. For more complex data, you should look into Provider, InheritedWidget or alike.

    Things have changed since that video was released. Saed's answer (which I rewarded 50 bounty points) might be how it was done in the video, but it no longer works in recent Flutter versions. Basically right now there is no good way to easily implement the demo using GlobalKey.

    But...

    If you can guarantee that, the two widgets will never be on the screen at the same time, or more precisely, they will never be simultaneously inserted into the widget tree on the same frame, then you could try to use GlobalKey to have the same widget on different parts of the layout.

    Note this is a very strict limitation. For example, when swiping to another screen, there is usually a transition animation where both screens are rendered at the same time. That is not okay. So for this demo, I inserted a "blank page" to prevent that when swiping.

    How to:

    So, if you want the same widget, appearing on very different screens (that hopefully are far from each other), you can use a GlobalKey to do that, with basically 3 lines of code.

    First, declare a variable that you can access from both screens:

    final _key = GlobalKey();
    

    Then, in your widget, have a constructor that takes in a key and pass it to the parent class:

    Foo(key) : super(key: key);
    

    Lastly, whenever you use the widget, pass the same key variable to it:

    return Container(
      color: Colors.green[100],
      child: Foo(_key),
    );
    

    Full Source:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MaterialApp(home: MyApp()));
    }
    
    class MyApp extends StatelessWidget {
      final _key = GlobalKey();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text("Global Key Demo")),
          body: PageView.builder(
            itemCount: 3,
            itemBuilder: (context, index) {
              switch (index) {
                case 0:
                  return Container(
                    color: Colors.green[100],
                    child: Foo(_key),
                  );
                  break;
                case 1:
                  return Container(
                    color: Colors.blue[100],
                    child: Text("Blank Page"),
                  );
                  break;
                case 2:
                  return Container(
                    color: Colors.red[100],
                    child: Foo(_key),
                  );
                  break;
                default:
                  throw "404";
              }
            },
          ),
        );
      }
    }
    
    class Foo extends StatefulWidget {
      @override
      _FooState createState() => _FooState();
    
      Foo(key) : super(key: key);
    }
    
    class _FooState extends State {
      bool _switchValue = false;
      double _sliderValue = 0.5;
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: [
            Switch(
              value: _switchValue,
              onChanged: (v) {
                setState(() => _switchValue = v);
              },
            ),
            Slider(
              value: _sliderValue,
              onChanged: (v) {
                setState(() => _sliderValue = v);
              },
            )
          ],
        );
      }
    }
    

提交回复
热议问题