Keys And GlobalKey

All widgets should have a Key key as optional parameter or their constructor. Key is something used by flutter engine at the step of recognizing which widget in a list as changed.

"relatively expensive. So don't use it if you don't need it". "Better avoided". There are many usages of GlobalKey that can be achieved using something different.

Keys for Form

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Input Boxes',
      theme: ThemeData(
        primarySwatch: Colors.indigo,
      ),
      home: InputBox(),
    );
  }
}

class InputBox extends StatefulWidget {
  
  InputBoxState createState() => InputBoxState();
}

class InputBoxState extends State<InputBox> {
  bool loggedIn = false;
  String _email, _username, _password;

  final formKey = GlobalKey<FormState>();
  final mainKey = GlobalKey<ScaffoldState>();

  
  Widget build(BuildContext context) {
    return Scaffold(
      key: mainKey,
      appBar: AppBar(title: Text("Form Example")),
      body: Padding(
          padding: EdgeInsets.all(10.0),
          child: loggedIn == false
              ? Form(
                  key: formKey,
                  child: Column(
                    children: <Widget>[
                      TextFormField(
                        autocorrect: false,
                        decoration: InputDecoration(
                          labelText: "Email:",
                        ),
                        validator: (str) =>
                            !str.contains('@') ? "Not a Valid Email!" : null,
                        onSaved: (str) => _email = str,
                      ),
                      TextFormField(
                        autocorrect: false,
                        decoration: InputDecoration(
                          labelText: "Username:",
                        ),
                        validator: (str) =>
                            str.length <= 5 ? "Not a Valid Username!" : null,
                        onSaved: (str) => _username = str,
                      ),
                      TextFormField(
                        autocorrect: false,
                        decoration: InputDecoration(
                          labelText: "Password:",
                        ),
                        validator: (str) =>
                            str.length <= 7 ? "Not a Valid Password!" : null,
                        onSaved: (str) => _password = str,
                        obscureText: true,
                      ),
                      RaisedButton(
                        child: Text("Submit"),
                        onPressed: onPressed,
                      ),
                    ],
                  ),
                )
              : Center(
                  child: Column(
                    children: <Widget>[
                      Text("Welcome $_username"),
                      RaisedButton(
                        child: Text("Log Out"),
                        onPressed: () {
                          setState(() {
                            loggedIn = false;
                          });
                        },
                      )
                    ],
                  ),
                )),
    );
  }

  void onPressed() {
    //使用formkey获得form
    var form = formKey.currentState;

    if (form.validate()) {
      //form.save会调用children每个组件的save方法
      //这里form是FormState
      //see :https://docs.flutter.io/flutter/widgets/FormState/save.html
      form.save();
      setState(() {
        loggedIn = true;
      });

      var snackbar = SnackBar(
        content:
            Text('Username: $_username, Email: $_email, Password: $_password'),
        duration: Duration(milliseconds: 5000),
      );
      //这其实不是一个好的使用GLOBALKEY的方法
      //把StatefulWidget包在一个Scaffold中,然后使用Scaffold.of(context)比使用_scaffoldKey更好
        //see:https://stackoverflow.com/questions/40579879/display-snackbar-in-flutter
      mainKey.currentState.showSnackBar(snackbar);
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

Keys for dismiss

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

// MyApp is a StatefulWidget. This allows us to update the state of the
// Widget whenever an item is removed.
class MyApp extends StatefulWidget {
  MyApp({Key key}) : super(key: key);

  
  MyAppState createState() {
    return MyAppState();
  }
}

class MyAppState extends State<MyApp> {
  final items = List<String>.generate(3, (i) => "Item ${i + 1}");

  
  Widget build(BuildContext context) {
    final title = 'Dismissing Items';

    return MaterialApp(
      title: title,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index) {
            final item = items[index];

            return Dismissible(
              // Each Dismissible must contain a Key. Keys allow Flutter to
              // uniquely identify Widgets.
              // 如果这里使用相同的KEY,将导致重新build时候,在rendertree中无法找到对应的widget,因为widgt的key值一样,无法知道哪个是要被删除的。
              //如果不使用KEY,则报错key == null,使用dismissible必须有key
              key: Key(item),
              // We also need to provide a function that tells our app
              // what to do after an item has been swiped away.
              onDismissed: (direction) {
                // Remove the item from our data source.
                setState(() {
                  items.removeAt(index);
                });

                // Then show a snackbar!
                Scaffold.of(context)
                    .showSnackBar(SnackBar(content: Text("$item dismissed")));
              },
              // Show a red background as the item is swiped away
              background: Container(color: Colors.red),
              child: ListTile(title: Text('$item')),
            );
          },
        ),
      ),
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

SnackBar

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
 final GlobalKey<ScaffoldState> _scafKey = GlobalKey<ScaffoldState>();
 
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'SnackBar',
     home: Scaffold(
       key: _scafKey,
       backgroundColor: Colors.greenAccent,
       appBar: AppBar(
         title: Text('SnackBar'),
       ),
       body: Center(
         child: Container(
           child: Column(
             mainAxisAlignment: MainAxisAlignment.center,
             children: <Widget>[
               Text(
                 'Hit below Button',
                 style: TextStyle(fontSize: 25.0),
               ),
               RaisedButton(
                 color: Colors.blue,
                 child: Text('Hit Me'),
                 onPressed: () {
                   _scafKey.currentState.showSnackBar(SnackBar(
                     content: Text('This is SnackBar'),
                     duration: Duration(seconds: 3),
                     backgroundColor: Colors.purpleAccent,
                   ));
                 },
               ),
               //rButton(),
             ],
           ),
         ),
       ),
     ),
   );
 }
}

class rButton extends StatelessWidget {
 
 Widget build(BuildContext context) {
   return RaisedButton(
     color: Colors.blue,
     child: Text('Hit Me'),
     onPressed: () {
       Scaffold.of(context).showSnackBar(SnackBar(
             content: Text('This is SnackBar'),
             duration: Duration(seconds: 3),
             backgroundColor: Colors.purpleAccent,
           ));
     },
   );
 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

Snackbar 2

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(),
      routes: {
        TabsPage.routeName: (context) => new TabsPage(),
      },
    );
  }
}

/// HOME
class MyHomePage extends StatefulWidget {
  
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Home Page"),
      ),
      body: new ListView(
        children: <Widget>[
          new ListTile(
            title: new Text("Home Body"),
          ),
          new ListTile(
            title: new RaisedButton(
                child: new Text("Route to Tabs".toUpperCase()),
                onPressed: () {
                  Navigator.of(context).pushNamed(TabsPage.routeName);
                }),
          ),
        ],
      ),
    );
  }
}

/// Tabs
class TabsPage extends StatefulWidget {
  static String routeName = "/tabsPage";

  
  _TabsPageState createState() => new _TabsPageState();
}

class _TabsPageState extends State<TabsPage> {
  var _scaffoldKey = new GlobalKey<ScaffoldState>();

  
  Widget build(BuildContext context) {
    return new DefaultTabController(
      length: 2,
      child: new Scaffold(
        key: _scaffoldKey,
        appBar: new AppBar(
          bottom: new TabBar(
            tabs: [
              new Tab(icon: new Icon(Icons.directions_car), text: "Cars"),
              new Tab(
                  icon: new Icon(Icons.directions_transit), text: "Transit"),
            ],
          ),
          title: new Text('Tabs'),
        ),
        body: new TabBarView(
          children: [
            new CarsTab(scaffoldKey: _scaffoldKey,),
            new TransitTab(scaffoldKey: _scaffoldKey,),
          ],
        ),
      ),
    );
  }
}

/// Cars
class CarsTab extends StatefulWidget {
  final GlobalKey<ScaffoldState> scaffoldKey;

  CarsTab({Key key, this.scaffoldKey}) : super(key: key);

  
  _CarsTabState createState() => new _CarsTabState();
}

class _CarsTabState extends State<CarsTab> {
  bool _enabled = false;

  
  Widget build(BuildContext context) {
    return new ListView(
      children: <Widget>[
        new SwitchListTile(
            title: new Text("Enable Cars Route"),
            value: _enabled,
            onChanged: (bool value) {
              setState(() {
                _enabled = value;
              });

              if (value) {
                var snackbar = new SnackBar(content: new Text("Cars enabled"));
                widget.scaffoldKey.currentState.showSnackBar(snackbar);
              }
            }),
      ],
    );
  }
}

/// Transit
class TransitTab extends StatefulWidget {
  final GlobalKey<ScaffoldState> scaffoldKey;

  TransitTab({Key key, this.scaffoldKey}) : super(key: key);

  
  _TransitTabState createState() => new _TransitTabState();
}

class _TransitTabState extends State<TransitTab> {
  bool _enabled = false;

  
  Widget build(BuildContext context) {
    return new ListView(
      children: <Widget>[
        new SwitchListTile(
            title: new Text("Enable Transit Route"),
            value: _enabled,
            onChanged: (bool value) {
              setState(() {
                _enabled = value;
              });

              if (value) {
                var snackbar = new SnackBar(content: new Text("Transit enabled"));
                widget.scaffoldKey.currentState.showSnackBar(snackbar);
              }
            }),
      ],
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158