Tab order in flutter
Asked Answered
U

1

9

On my Windows App ( The interface looks like this: https://i.sstatic.net/SibAC.png )

When I focused to Input-1, and I press Tab key on keyboard, the primary focus will focus to Input-3. Is there any way to re-arrange my focus order: Input-1 -> Input-2 -> Input-3 -> Input-4 -> Input-5.

Attention: This is not Tab bar/tab controller in flutter's docs example.

This is my code:

3 files is in same root

main.dart

import 'package:flutter/material.dart';
import 'tab1.dart';
import 'tab2.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Tab oder',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Tab order'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final TextEditingController _controller = TextEditingController();
  final FocusNode _focusNode = FocusNode();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Form(
        key: GlobalKey<FormState>(),
        onChanged: () {
          Form.of(primaryFocus!.context!)!.save();
        },
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.end,
          children: [
            Container(
              constraints: const BoxConstraints.tightFor(width: 200.0, height: 200.0),
              child: TextFormField(
                controller: _controller,
                focusNode: _focusNode,
                decoration: const InputDecoration(
                  filled: true,
                  fillColor: Colors.yellow,
                ),
              ),
            ),
            const SizedBox(width: 150.0),
            Column(
              mainAxisAlignment: MainAxisAlignment.end,
              children: <Widget>[
                Container(
                  constraints: const BoxConstraints.tightFor(width: 200.0, height: 120.0),
                  child: Tab1(),
                ),
                Container(
                  constraints: const BoxConstraints.tightFor(width: 200.0, height: 120.0),
                  child: Tab2(),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

tab1.dart

import 'package:flutter/material.dart';

class Tab1 extends StatefulWidget {
  @override
  _Tab1State createState() => _Tab1State();
}

class _Tab1State extends State<Tab1> {
  final TextEditingController _controller1 = TextEditingController();
  final TextEditingController _controller2 = TextEditingController();
  final FocusNode _focusNode1 = FocusNode();
  final FocusNode _focusNode2 = FocusNode();

  @override
  build(BuildContext context) {
    return Column(
      children: [
        TextFormField(
          controller: _controller1,
          focusNode: _focusNode1,
          decoration: const InputDecoration(
            filled: true,
            fillColor: Colors.greenAccent,
          ),
        ),
        TextFormField(
          controller: _controller2,
          focusNode: _focusNode2,
          decoration: const InputDecoration(
            filled: true,
            fillColor: Colors.greenAccent,
          ),
        ),
      ],
    );
  }
}

tab2.dart

import 'package:flutter/material.dart';

class Tab2 extends StatefulWidget {
  @override
  _Tab2State createState() => _Tab2State();
}

class _Tab2State extends State<Tab2> {
  final TextEditingController _controller1 = TextEditingController();
  final TextEditingController _controller2 = TextEditingController();
  final FocusNode _focusNode1 = FocusNode();
  final FocusNode _focusNode2 = FocusNode();

  @override
  build(BuildContext context) {
    return Column(
      children: [
        TextFormField(
          controller: _controller1,
          focusNode: _focusNode1,
          decoration: const InputDecoration(
            filled: true,
            fillColor: Colors.greenAccent,
          ),
        ),
        TextFormField(
          controller: _controller2,
          focusNode: _focusNode2,
          decoration: const InputDecoration(
            filled: true,
            fillColor: Colors.greenAccent,
          ),
        ),
      ],
    );
  }
}
Unnecessarily answered 20/7, 2021 at 5:16 Comment(2)
Is possible if I switch display of tab1 and tab2 (show tab1 hide tab2, or show tab2 hide tab1)Unnecessarily
Thanks for the easy reproducible example, it makes answering a breeze.Krems
K
14

You can achieve the behavior you want via a FocusTraversalGroup, with this widget you specify the order in which you want the traversal to be performed.

This widgets takes a traversal policy as a parameter, I used a OrderedTraversalPolicy, but there are others like:

WidgetOrderTraversalPolicy, a policy that relies on the widget creation order to describe the order of traversal.

ReadingOrderTraversalPolicy, a policy that describes the order as the natural "reading order" for the current Directionality.

NumericFocusOrder, a focus order that assigns a numeric traversal order.

LexicalFocusOrder a focus order that assigns a string-based lexical traversal order.

Also there is an really interesting section about building adaptive apps in which focus and traversal order in different platforms is part of, you can check it out here

class Tab1 extends StatefulWidget {
  @override
  _Tab1State createState() => _Tab1State();
}

class _Tab1State extends State<Tab1> {
  final TextEditingController _controller1 = TextEditingController();
  final TextEditingController _controller2 = TextEditingController();
  final FocusNode _focusNode1 = FocusNode();
  final FocusNode _focusNode2 = FocusNode();

  @override
  build(BuildContext context) {
    return FocusTraversalGroup(
      policy: OrderedTraversalPolicy(),

If you need to change the traversal order of other groups(like tab2) simply wrap them in another FocusTraversalGroup and specify the policy you want them to follow.

Krems answered 20/7, 2021 at 6:0 Comment(1)
Can you pls elaborate above example with textfields included, currently i'm trying with textfields in child of column below FocusTraversalGroup, nothing happening. But the problem is nothing works even trying to changing focus using tab.Femoral

© 2022 - 2024 — McMap. All rights reserved.