【问题标题】:How to move the focus smoothly from a TextField to a newly created TextField?如何将焦点从 TextField 平滑地移动到新创建的 TextField?
【发布时间】:2023-01-30 02:49:59
【问题描述】:

我想创建一个屏幕,其中许多 TextFIelds 垂直排列,当在编辑底部的 TextField 时按下 Enter 键时,一个新的 TextField 也会添加到它下面,同时移动焦点。我在 FocusNode 的文档中创建了一个引用 the example 的演示应用程序,它基本上可以工作,但是当将焦点移动到新创建的 TextField 时键盘会弹跳(参见下面的 gif)。我该如何解决这种不良行为?

The gif of the demo app

演示应用程序的代码在这里:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: _title,
      home: MyStatefulWidget(),
    );
  }
}

class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({super.key});

  @override
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  int focusedChild = 0;
  List<Widget> children = <Widget>[];
  List<FocusNode> childFocusNodes = <FocusNode>[];

  @override
  void initState() {
    super.initState();
    // Add the first child.
    _addChild();
  }

  @override
  void dispose() {
    for (final FocusNode node in childFocusNodes) {
      node.dispose();
    }
    super.dispose();
  }

  void _addChild() {
    // Calling requestFocus here creates a deferred request for focus, since the
    // node is not yet part of the focus tree.
    childFocusNodes
        .add(FocusNode(debugLabel: 'Child ${children.length}')..requestFocus());

    children.add(
      TextField(
        focusNode: childFocusNodes.last,
        textInputAction: TextInputAction.unspecified,
        minLines: 1,
        onSubmitted: (value) {
          setState(() {
            focusedChild = children.length;
            _addChild();
          });
        },
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: SingleChildScrollView(
          child: Column(
            children: children,
          ),
        ),
      ),
    );
  }
}

【问题讨论】:

    标签: flutter dart


    【解决方案1】:

    Screenshot

    你需要使用TextInputAction

    TextInputAction.next:将焦点移动到下一个可聚焦的项目。

    TextInputAction.done:关闭键盘。


    class TestWidget extends StatelessWidget {
      const TestWidget({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: SafeArea(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.end,
              children: const [
                TextField(
                  decoration: InputDecoration(hintText: 'TextField #1 with next'),
                  textInputAction: TextInputAction.next, // Moves the focus to the next focusable item.
                ),
                TextField(
                  decoration: InputDecoration(hintText: 'TextField #2 with next'),
                  textInputAction: TextInputAction.next, // Moves the focus to the next focusable item.
                ),
                TextField(
                  decoration: InputDecoration(hintText: 'TextField #3 with done'),
                  textInputAction: TextInputAction.done, // Close the keyboard.
                ),
              ],
            ),
          ),
        );
      }
    }
    

    更新

    在编辑完成时创建一个新的文本字段并将焦点更改为新文本字段。

    1. 您需要使用onEditingComplete函数,而不是onSubmitted。因为onEditingComplete 不会关闭(dismiss)键盘。
    2. 我重写了您的代码,并删除了生成小部件列表的操作。并将其替换为TextEditingController,因为将 UI(小部件)保留在变量中是一种糟糕的体验。所以文本字段由TextEditingController长度生成。
    3. 我删除了FocusNode 的列表,并使用FocusScope.of(context).nextFocus() 来更改焦点。
    4. 在创建新的TextEditingController 函数调用延迟nextFocus() 后,有时间重新创建新的 UI。

      这是完整的代码:

      import 'package:flutter/material.dart';
      
      void main() => runApp(const MyApp());
      
      class MyApp extends StatelessWidget {
        const MyApp({super.key});
      
        @override
        Widget build(BuildContext context) {
          return const MaterialApp(home: MyStatefulWidget());
        }
      }
      
      class MyStatefulWidget extends StatefulWidget {
        const MyStatefulWidget({super.key});
      
        @override
        State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
      }
      
      class _MyStatefulWidgetState extends State<MyStatefulWidget> {
        final List<TextEditingController> _controllers = [
          TextEditingController()
        ]; // with first controller
      
        _addController() {
          setState(() {
            _controllers.add(TextEditingController());
          });
      
          Future.delayed(const Duration(milliseconds: 100), () {
            // Add delay for recreate UI after setState
            FocusScope.of(context).nextFocus();
          });
        }
      
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            body: SafeArea(
              child: SingleChildScrollView(
                child: Column(
                  children: _controllers
                      .map((e) => TextField(
                            textInputAction: TextInputAction.next,
                            controller: e,
                            onEditingComplete: _addController,
                          ))
                      .toList(),
                ),
              ),
            ),
          );
        }
      }
      

    【讨论】:

    • 感谢你的回答。但我想做的是动态添加一个新的TextField到列表底部,同时移动焦点。 TextInputAction.next 不适用于这种情况,因为在按下 Next 键时新的 TextField 尚未附加到小部件树。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-23
    • 2017-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-30
    • 2020-05-27
    相关资源
    最近更新 更多