focus.dart 6.27 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
Ian Hickson's avatar
Ian Hickson committed
4

5 6 7
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

8
void main() {
9 10 11 12 13 14
  runApp(const MaterialApp(
    title: 'Focus Demo',
    home: FocusDemo(),
  ));
}

15
class DemoButton extends StatefulWidget {
16
  const DemoButton({Key key, this.name, this.canRequestFocus = true, this.autofocus = false}) : super(key: key);
17 18

  final String name;
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
  final bool canRequestFocus;
  final bool autofocus;

  @override
  _DemoButtonState createState() => _DemoButtonState();
}

class _DemoButtonState extends State<DemoButton> {
  FocusNode focusNode;

  @override
  void initState() {
    super.initState();
    focusNode = FocusNode(
      debugLabel: widget.name,
      canRequestFocus: widget.canRequestFocus,
    );
  }

  @override
  void dispose() {
    focusNode?.dispose();
    super.dispose();
  }

  @override
  void didUpdateWidget(DemoButton oldWidget) {
    super.didUpdateWidget(oldWidget);
    focusNode.canRequestFocus = widget.canRequestFocus;
  }
49 50

  void _handleOnPressed() {
51 52 53
    focusNode.requestFocus();
    print('Button ${widget.name} pressed.');
    debugDumpFocusTree();
54 55 56 57
  }

  @override
  Widget build(BuildContext context) {
58
    return TextButton(
59 60
      focusNode: focusNode,
      autofocus: widget.autofocus,
61 62 63 64 65 66 67 68 69
      style: ButtonStyle(
        overlayColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) {
          if (states.contains(MaterialState.focused))
            return Colors.red.withOpacity(0.25);
          if (states.contains(MaterialState.hovered))
            return Colors.blue.withOpacity(0.25);
          return null;
        }),
      ),
70
      onPressed: () => _handleOnPressed(),
71
      child: Text(widget.name),
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
    );
  }
}

class FocusDemo extends StatefulWidget {
  const FocusDemo({Key key}) : super(key: key);

  @override
  _FocusDemoState createState() => _FocusDemoState();
}

class _FocusDemoState extends State<FocusDemo> {
  FocusNode outlineFocus;

  @override
  void initState() {
    super.initState();
    outlineFocus = FocusNode(debugLabel: 'Demo Focus Node');
  }

  @override
  void dispose() {
    outlineFocus.dispose();
    super.dispose();
  }

98
  KeyEventResult _handleKeyPress(FocusNode node, RawKeyEvent event) {
99 100 101 102 103 104 105 106
    if (event is RawKeyDownEvent) {
      print('Scope got key event: ${event.logicalKey}, $node');
      print('Keys down: ${RawKeyboard.instance.keysPressed}');
      if (event.logicalKey == LogicalKeyboardKey.tab) {
        debugDumpFocusTree();
        if (event.isShiftPressed) {
          print('Moving to previous.');
          node.previousFocus();
107
          return KeyEventResult.handled;
108 109 110
        } else {
          print('Moving to next.');
          node.nextFocus();
111
          return KeyEventResult.handled;
112 113 114 115
        }
      }
      if (event.logicalKey == LogicalKeyboardKey.arrowLeft) {
        node.focusInDirection(TraversalDirection.left);
116
        return KeyEventResult.handled;
117 118 119
      }
      if (event.logicalKey == LogicalKeyboardKey.arrowRight) {
        node.focusInDirection(TraversalDirection.right);
120
        return KeyEventResult.handled;
121 122 123
      }
      if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
        node.focusInDirection(TraversalDirection.up);
124
        return KeyEventResult.handled;
125 126 127
      }
      if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
        node.focusInDirection(TraversalDirection.down);
128
        return KeyEventResult.handled;
129 130
      }
    }
131
    return KeyEventResult.ignored;
132 133 134 135 136 137
  }

  @override
  Widget build(BuildContext context) {
    final TextTheme textTheme = Theme.of(context).textTheme;

138
    return FocusTraversalGroup(
139 140 141 142 143 144
      policy: ReadingOrderTraversalPolicy(),
      child: FocusScope(
        debugLabel: 'Scope',
        onKey: _handleKeyPress,
        autofocus: true,
        child: DefaultTextStyle(
145
          style: textTheme.headline4,
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
          child: Scaffold(
            appBar: AppBar(
              title: const Text('Focus Demo'),
            ),
            floatingActionButton: FloatingActionButton(
              child: const Text('+'),
              onPressed: () {},
            ),
            body: Center(
              child: Builder(builder: (BuildContext context) {
                return Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: const <Widget>[
162 163 164 165
                        DemoButton(
                          name: 'One',
                          autofocus: true,
                        ),
166 167 168 169 170 171
                      ],
                    ),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: const <Widget>[
                        DemoButton(name: 'Two'),
172 173 174 175
                        DemoButton(
                          name: 'Three',
                          canRequestFocus: false,
                        ),
176 177 178 179 180 181 182 183 184 185
                      ],
                    ),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: const <Widget>[
                        DemoButton(name: 'Four'),
                        DemoButton(name: 'Five'),
                        DemoButton(name: 'Six'),
                      ],
                    ),
186
                    OutlinedButton(onPressed: () => print('pressed'), child: const Text('PRESS ME')),
187 188
                    const Padding(
                      padding: EdgeInsets.all(8.0),
189 190 191 192
                      child: TextField(
                        decoration: InputDecoration(labelText: 'Enter Text', filled: true),
                      ),
                    ),
193 194
                    const Padding(
                      padding: EdgeInsets.all(8.0),
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
                      child: TextField(
                        decoration: InputDecoration(
                          border: OutlineInputBorder(),
                          labelText: 'Enter Text',
                          filled: false,
                        ),
                      ),
                    ),
                  ],
                );
              }),
            ),
          ),
        ),
      ),
    );
  }
}