blob: 7182b67ed5bad007bb88fb1696949edff6649057 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flutter code sample for FocusTraversalGroup
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatelessWidget(),
);
}
}
/// A button wrapper that adds either a numerical or lexical order, depending on
/// the type of T.
class OrderedButton<T> extends StatefulWidget {
const OrderedButton({
Key? key,
required this.name,
this.canRequestFocus = true,
this.autofocus = false,
required this.order,
}) : super(key: key);
final String name;
final bool canRequestFocus;
final bool autofocus;
final T order;
@override
State<OrderedButton<T>> createState() => _OrderedButtonState<T>();
}
class _OrderedButtonState<T> extends State<OrderedButton<T>> {
late 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(OrderedButton<T> oldWidget) {
super.didUpdateWidget(oldWidget);
focusNode.canRequestFocus = widget.canRequestFocus;
}
void _handleOnPressed() {
focusNode.requestFocus();
debugPrint('Button ${widget.name} pressed.');
debugDumpFocusTree();
}
@override
Widget build(BuildContext context) {
FocusOrder order;
if (widget.order is num) {
order = NumericFocusOrder((widget.order as num).toDouble());
} else {
order = LexicalFocusOrder(widget.order.toString());
}
Color? overlayColor(Set<MaterialState> states) {
if (states.contains(MaterialState.focused)) {
return Colors.red;
}
if (states.contains(MaterialState.hovered)) {
return Colors.blue;
}
return null; // defer to the default overlayColor
}
Color? foregroundColor(Set<MaterialState> states) {
if (states.contains(MaterialState.focused) ||
states.contains(MaterialState.hovered)) {
return Colors.white;
}
return null; // defer to the default foregroundColor
}
return FocusTraversalOrder(
order: order,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: OutlinedButton(
focusNode: focusNode,
autofocus: widget.autofocus,
style: ButtonStyle(
overlayColor:
MaterialStateProperty.resolveWith<Color?>(overlayColor),
foregroundColor:
MaterialStateProperty.resolveWith<Color?>(foregroundColor),
),
onPressed: () => _handleOnPressed(),
child: Text(widget.name),
),
),
);
}
}
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: FocusTraversalGroup(
policy: OrderedTraversalPolicy(),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// A group that is ordered with a numerical order, from left to right.
FocusTraversalGroup(
policy: OrderedTraversalPolicy(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List<Widget>.generate(3, (int index) {
return OrderedButton<num>(
name: 'num: $index',
// TRY THIS: change this to "3 - index" and see how the order changes.
order: index,
);
}),
),
),
// A group that is ordered with a lexical order, from right to left.
FocusTraversalGroup(
policy: OrderedTraversalPolicy(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List<Widget>.generate(3, (int index) {
// Order as "C" "B", "A".
final String order =
String.fromCharCode('A'.codeUnitAt(0) + (2 - index));
return OrderedButton<String>(
name: 'String: $order',
order: order,
);
}),
),
),
// A group that orders in widget order, regardless of what the order is set to.
FocusTraversalGroup(
// Note that because this is NOT an OrderedTraversalPolicy, the
// assigned order of these OrderedButtons is ignored, and they
// are traversed in widget order. TRY THIS: change this to
// "OrderedTraversalPolicy()" and see that it now follows the
// numeric order set on them instead of the widget order.
policy: WidgetOrderTraversalPolicy(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List<Widget>.generate(3, (int index) {
return OrderedButton<num>(
name: 'ignored num: ${3 - index}',
order: 3 - index,
);
}),
),
),
],
),
),
);
}
}