1// Copyright 2014 The Flutter 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';
6
7/// Flutter code sample for [FocusTraversalGroup].
8
9void main() => runApp(const FocusTraversalGroupExampleApp());
10
11class FocusTraversalGroupExampleApp extends StatelessWidget {
12 const FocusTraversalGroupExampleApp({super.key});
13
14 @override
15 Widget build(BuildContext context) {
16 return const MaterialApp(
17 home: FocusTraversalGroupExample(),
18 );
19 }
20}
21
22/// A button wrapper that adds either a numerical or lexical order, depending on
23/// the type of T.
24class OrderedButton<T> extends StatefulWidget {
25 const OrderedButton({
26 super.key,
27 required this.name,
28 this.canRequestFocus = true,
29 this.autofocus = false,
30 required this.order,
31 });
32
33 final String name;
34 final bool canRequestFocus;
35 final bool autofocus;
36 final T order;
37
38 @override
39 State<OrderedButton<T>> createState() => _OrderedButtonState<T>();
40}
41
42class _OrderedButtonState<T> extends State<OrderedButton<T>> {
43 late FocusNode focusNode;
44
45 @override
46 void initState() {
47 super.initState();
48 focusNode = FocusNode(
49 debugLabel: widget.name,
50 canRequestFocus: widget.canRequestFocus,
51 );
52 }
53
54 @override
55 void dispose() {
56 focusNode.dispose();
57 super.dispose();
58 }
59
60 @override
61 void didUpdateWidget(OrderedButton<T> oldWidget) {
62 super.didUpdateWidget(oldWidget);
63 focusNode.canRequestFocus = widget.canRequestFocus;
64 }
65
66 void _handleOnPressed() {
67 focusNode.requestFocus();
68 debugPrint('Button ${widget.name} pressed.');
69 debugDumpFocusTree();
70 }
71
72 @override
73 Widget build(BuildContext context) {
74 final FocusOrder order = switch (widget.order) {
75 final num number => NumericFocusOrder(number.toDouble()),
76 final Object? object => LexicalFocusOrder(object.toString()),
77 };
78
79 return FocusTraversalOrder(
80 order: order,
81 child: Padding(
82 padding: const EdgeInsets.all(8.0),
83 child: OutlinedButton(
84 focusNode: focusNode,
85 autofocus: widget.autofocus,
86 style: const ButtonStyle(
87 overlayColor: WidgetStateProperty<Color?>.fromMap(
88 // If neither of these states is active, the property will
89 // resolve to null, deferring to the default overlay color.
90 <WidgetState, Color>{
91 WidgetState.focused: Colors.red,
92 WidgetState.hovered: Colors.blue,
93 },
94 ),
95 foregroundColor: WidgetStateProperty<Color?>.fromMap(
96 // "WidgetState.focused | WidgetState.hovered" could be used
97 // instead of separate map keys, but this setup allows setting
98 // the button style to a constant value for improved efficiency.
99 <WidgetState, Color>{
100 WidgetState.focused: Colors.white,
101 WidgetState.hovered: Colors.white,
102 },
103 ),
104 ),
105 onPressed: () => _handleOnPressed(),
106 child: Text(widget.name),
107 ),
108 ),
109 );
110 }
111}
112
113class FocusTraversalGroupExample extends StatelessWidget {
114 const FocusTraversalGroupExample({super.key});
115
116 @override
117 Widget build(BuildContext context) {
118 return ColoredBox(
119 color: Colors.white,
120 child: FocusTraversalGroup(
121 policy: OrderedTraversalPolicy(),
122 child: Column(
123 mainAxisAlignment: MainAxisAlignment.center,
124 children: <Widget>[
125 // A group that is ordered with a numerical order, from left to right.
126 FocusTraversalGroup(
127 policy: OrderedTraversalPolicy(),
128 child: Row(
129 mainAxisAlignment: MainAxisAlignment.center,
130 children: List<Widget>.generate(3, (int index) {
131 return OrderedButton<num>(
132 name: 'num: $index',
133 // TRY THIS: change this to "3 - index" and see how the order changes.
134 order: index,
135 );
136 }),
137 ),
138 ),
139 // A group that is ordered with a lexical order, from right to left.
140 FocusTraversalGroup(
141 policy: OrderedTraversalPolicy(),
142 child: Row(
143 mainAxisAlignment: MainAxisAlignment.center,
144 children: List<Widget>.generate(3, (int index) {
145 // Order as "C" "B", "A".
146 final String order = String.fromCharCode('A'.codeUnitAt(0) + (2 - index));
147 return OrderedButton<String>(
148 name: 'String: $order',
149 order: order,
150 );
151 }),
152 ),
153 ),
154 // A group that orders in widget order, regardless of what the order is set to.
155 FocusTraversalGroup(
156 // Because this is NOT an OrderedTraversalPolicy, the
157 // assigned order of these OrderedButtons is ignored, and they
158 // are traversed in widget order. TRY THIS: change this to
159 // "OrderedTraversalPolicy()" and see that it now follows the
160 // numeric order set on them instead of the widget order.
161 policy: WidgetOrderTraversalPolicy(),
162 child: Row(
163 mainAxisAlignment: MainAxisAlignment.center,
164 children: List<Widget>.generate(3, (int index) {
165 return OrderedButton<num>(
166 name: 'ignored num: ${3 - index}',
167 order: 3 - index,
168 );
169 }),
170 ),
171 ),
172 ],
173 ),
174 ),
175 );
176 }
177}
178

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com