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