// 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. // Template: dev/snippets/config/templates/stateless_widget_material.tmpl // // Comment lines marked with "▼▼▼" and "▲▲▲" are used for authoring // of samples, and may be ignored if you are just exploring the sample. // Flutter code sample for FocusTraversalGroup // //*************************************************************************** //* ▼▼▼▼▼▼▼▼ description ▼▼▼▼▼▼▼▼ (do not modify or remove section marker) // This sample shows three rows of buttons, each grouped by a // [FocusTraversalGroup], each with different traversal order policies. Use tab // traversal to see the order they are traversed in. The first row follows a // numerical order, the second follows a lexical order (ordered to traverse // right to left), and the third ignores the numerical order assigned to it and // traverses in widget order. //* ▲▲▲▲▲▲▲▲ description ▲▲▲▲▲▲▲▲ (do not modify or remove section marker) //*************************************************************************** import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); /// This is the main application widget. 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(), ); } } //***************************************************************************** //* ▼▼▼▼▼▼▼▼ code-preamble ▼▼▼▼▼▼▼▼ (do not modify or remove section marker) /// A button wrapper that adds either a numerical or lexical order, depending on /// the type of T. class OrderedButton 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> createState() => _OrderedButtonState(); } class _OrderedButtonState extends State> { 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 oldWidget) { super.didUpdateWidget(oldWidget); focusNode.canRequestFocus = widget.canRequestFocus; } void _handleOnPressed() { focusNode.requestFocus(); print('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 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 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(overlayColor), foregroundColor: MaterialStateProperty.resolveWith(foregroundColor), ), onPressed: () => _handleOnPressed(), child: Text(widget.name), ), ), ); } } //* ▲▲▲▲▲▲▲▲ code-preamble ▲▲▲▲▲▲▲▲ (do not modify or remove section marker) //***************************************************************************** /// This is the stateless widget that the main application instantiates. class MyStatelessWidget extends StatelessWidget { const MyStatelessWidget({Key? key}) : super(key: key); @override //******************************************************************** //* ▼▼▼▼▼▼▼▼ code ▼▼▼▼▼▼▼▼ (do not modify or remove section marker) Widget build(BuildContext context) { return Container( color: Colors.white, child: FocusTraversalGroup( policy: OrderedTraversalPolicy(), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // A group that is ordered with a numerical order, from left to right. FocusTraversalGroup( policy: OrderedTraversalPolicy(), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(3, (int index) { return OrderedButton( 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.generate(3, (int index) { // Order as "C" "B", "A". final String order = String.fromCharCode('A'.codeUnitAt(0) + (2 - index)); return OrderedButton( 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.generate(3, (int index) { return OrderedButton( name: 'ignored num: ${3 - index}', order: 3 - index, ); }), ), ), ], ), ), ); } //* ▲▲▲▲▲▲▲▲ code ▲▲▲▲▲▲▲▲ (do not modify or remove section marker) //******************************************************************** }