• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'package:flutter/material.dart';
6import 'package:flutter/services.dart';
7import 'package:flutter/widgets.dart';
8
9void main() {
10  runApp(const MaterialApp(
11    title: 'Focus Demo',
12    home: FocusDemo(),
13  ));
14}
15
16class DemoButton extends StatefulWidget {
17  const DemoButton({this.name, this.canRequestFocus = true, this.autofocus = false});
18
19  final String name;
20  final bool canRequestFocus;
21  final bool autofocus;
22
23  @override
24  _DemoButtonState createState() => _DemoButtonState();
25}
26
27class _DemoButtonState extends State<DemoButton> {
28  FocusNode focusNode;
29
30  @override
31  void initState() {
32    super.initState();
33    focusNode = FocusNode(
34      debugLabel: widget.name,
35      canRequestFocus: widget.canRequestFocus,
36    );
37  }
38
39  @override
40  void dispose() {
41    focusNode?.dispose();
42    super.dispose();
43  }
44
45  @override
46  void didUpdateWidget(DemoButton oldWidget) {
47    super.didUpdateWidget(oldWidget);
48    focusNode.canRequestFocus = widget.canRequestFocus;
49  }
50
51  void _handleOnPressed() {
52    focusNode.requestFocus();
53    print('Button ${widget.name} pressed.');
54    debugDumpFocusTree();
55  }
56
57  @override
58  Widget build(BuildContext context) {
59    return FlatButton(
60      focusNode: focusNode,
61      autofocus: widget.autofocus,
62      focusColor: Colors.red,
63      hoverColor: Colors.blue,
64      onPressed: () => _handleOnPressed(),
65      child: Text(widget.name),
66    );
67  }
68}
69
70class FocusDemo extends StatefulWidget {
71  const FocusDemo({Key key}) : super(key: key);
72
73  @override
74  _FocusDemoState createState() => _FocusDemoState();
75}
76
77class _FocusDemoState extends State<FocusDemo> {
78  FocusNode outlineFocus;
79
80  @override
81  void initState() {
82    super.initState();
83    outlineFocus = FocusNode(debugLabel: 'Demo Focus Node');
84  }
85
86  @override
87  void dispose() {
88    outlineFocus.dispose();
89    super.dispose();
90  }
91
92  bool _handleKeyPress(FocusNode node, RawKeyEvent event) {
93    if (event is RawKeyDownEvent) {
94      print('Scope got key event: ${event.logicalKey}, $node');
95      print('Keys down: ${RawKeyboard.instance.keysPressed}');
96      if (event.logicalKey == LogicalKeyboardKey.tab) {
97        debugDumpFocusTree();
98        if (event.isShiftPressed) {
99          print('Moving to previous.');
100          node.previousFocus();
101          return true;
102        } else {
103          print('Moving to next.');
104          node.nextFocus();
105          return true;
106        }
107      }
108      if (event.logicalKey == LogicalKeyboardKey.arrowLeft) {
109        node.focusInDirection(TraversalDirection.left);
110        return true;
111      }
112      if (event.logicalKey == LogicalKeyboardKey.arrowRight) {
113        node.focusInDirection(TraversalDirection.right);
114        return true;
115      }
116      if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
117        node.focusInDirection(TraversalDirection.up);
118        return true;
119      }
120      if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
121        node.focusInDirection(TraversalDirection.down);
122        return true;
123      }
124    }
125    return false;
126  }
127
128  @override
129  Widget build(BuildContext context) {
130    final TextTheme textTheme = Theme.of(context).textTheme;
131
132    return DefaultFocusTraversal(
133      policy: ReadingOrderTraversalPolicy(),
134      child: FocusScope(
135        debugLabel: 'Scope',
136        onKey: _handleKeyPress,
137        autofocus: true,
138        child: DefaultTextStyle(
139          style: textTheme.display1,
140          child: Scaffold(
141            appBar: AppBar(
142              title: const Text('Focus Demo'),
143            ),
144            floatingActionButton: FloatingActionButton(
145              child: const Text('+'),
146              onPressed: () {},
147            ),
148            body: Center(
149              child: Builder(builder: (BuildContext context) {
150                return Column(
151                  mainAxisAlignment: MainAxisAlignment.center,
152                  children: <Widget>[
153                    Row(
154                      mainAxisAlignment: MainAxisAlignment.center,
155                      children: const <Widget>[
156                        DemoButton(
157                          name: 'One',
158                          autofocus: true,
159                        ),
160                      ],
161                    ),
162                    Row(
163                      mainAxisAlignment: MainAxisAlignment.center,
164                      children: const <Widget>[
165                        DemoButton(name: 'Two'),
166                        DemoButton(
167                          name: 'Three',
168                          canRequestFocus: false,
169                        ),
170                      ],
171                    ),
172                    Row(
173                      mainAxisAlignment: MainAxisAlignment.center,
174                      children: const <Widget>[
175                        DemoButton(name: 'Four'),
176                        DemoButton(name: 'Five'),
177                        DemoButton(name: 'Six'),
178                      ],
179                    ),
180                    OutlineButton(onPressed: () => print('pressed'), child: const Text('PRESS ME')),
181                    const Padding(
182                      padding: EdgeInsets.all(8.0),
183                      child: TextField(
184                        decoration: InputDecoration(labelText: 'Enter Text', filled: true),
185                      ),
186                    ),
187                    Padding(
188                      padding: const EdgeInsets.all(8.0),
189                      child: TextField(
190                        decoration: InputDecoration(
191                          border: OutlineInputBorder(),
192                          labelText: 'Enter Text',
193                          filled: false,
194                        ),
195                      ),
196                    ),
197                  ],
198                );
199              }),
200            ),
201          ),
202        ),
203      ),
204    );
205  }
206}
207