You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PageView.builder内Form用GlobalKey致重复Key错误及输入框焦点问题

解决PageView嵌套Form+TextFormField的重复GlobalKey与焦点丢失问题

问题根源

  • 重复GlobalKey错误:你给所有页面的Form共用了同一个_formKey,而PageView默认会预加载相邻页面(比如当前在第0页时,第1页已经被构建),这就导致widget树中出现多个使用相同GlobalKey的Form组件,触发Duplicate GlobalKey detected错误。
  • 焦点丢失问题:移除GlobalKey后,虽然解决了重复key的问题,但切换页面时,Flutter的焦点管理器不会自动将焦点切换到新页面的输入框,因此需要手动处理焦点聚焦逻辑。

修复方案

步骤1:为每个Form分配独立的GlobalKey

将单个_formKey改为GlobalKey列表,让每个页面的Form使用对应索引的key,避免重复冲突。

步骤2:手动管理输入框焦点

为每个TextFormField绑定独立的FocusNode,监听PageView的页面切换事件,在页面切换完成后主动请求当前页面输入框的焦点。

优化后的完整代码

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: PageViewTest(),
    );
  }
}

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

  @override
  State<PageViewTest> createState() => _PageViewTestState();
}

class _PageViewTestState extends State<PageViewTest> {
  final PageController _pageController = PageController(initialPage: 0);
  late List<TextEditingController> _tecList;
  late List<GlobalKey<FormState>> _formKeys;
  late List<FocusNode> _focusNodes;

  final List<String> _signingHintText = [
    'type your domain',
    'type your email',
    'type your password',
  ];
  final List<String> _signingInputLabel = [
    'Domain',
    'Email',
    'Password',
  ];

  @override
  void initState() {
    super.initState();
    // 初始化每个页面的控制器、FormKey和焦点节点
    _tecList = List.generate(3, (index) => TextEditingController());
    _formKeys = List.generate(3, (index) => GlobalKey<FormState>());
    _focusNodes = List.generate(3, (index) => FocusNode());

    // 监听页面切换,自动聚焦当前页面的输入框
    _pageController.addListener(() {
      if (_pageController.page == _pageController.page?.round()) {
        final currentIndex = _pageController.page?.round() ?? 0;
        WidgetsBinding.instance.addPostFrameCallback((_) {
          _focusNodes[currentIndex].requestFocus();
        });
      }
    });

    // 初始页面自动聚焦
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _focusNodes[0].requestFocus();
    });
  }

  @override
  void dispose() {
    // 销毁所有控制器和焦点节点,避免内存泄漏
    for (var controller in _tecList) {
      controller.dispose();
    }
    for (var node in _focusNodes) {
      node.dispose();
    }
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Container(
          margin: const EdgeInsets.all(30),
          child: PageView.builder(
            controller: _pageController,
            itemCount: 3,
            itemBuilder: (context, index) {
              return Column(
                children: [
                  Form(
                    autovalidateMode: AutovalidateMode.onUserInteraction,
                    key: _formKeys[index], // 使用对应索引的FormKey
                    child: TextFormField(
                      focusNode: _focusNodes[index], // 关联焦点节点
                      textAlign: TextAlign.right,
                      textInputAction: TextInputAction.next,
                      style: const TextStyle(color: Color(0xff030303)),
                      cursorColor: const Color(0xff5e6593),
                      controller: _tecList[index],
                      keyboardType: TextInputType.text,
                      decoration: InputDecoration(
                        hintText: _signingHintText[index],
                        focusedBorder: const OutlineInputBorder(
                          borderSide: BorderSide(
                            color: Color(0xff5e6593),
                          ),
                        ),
                        contentPadding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10),
                        labelText: _signingInputLabel[index],
                        labelStyle: const TextStyle(color: Color(0xff5e6593)),
                        floatingLabelBehavior: FloatingLabelBehavior.auto,
                        border: const OutlineInputBorder(),
                        alignLabelWithHint: true,
                        hintStyle: const TextStyle(
                          color: Color(0xff5e6593),
                          fontSize: 15,
                          fontWeight: FontWeight.normal,
                        ),
                      ),
                    ),
                  ),
                  ElevatedButton(
                    onPressed: () {
                      if (index < 2) {
                        _pageController.nextPage(
                          duration: const Duration(milliseconds: 800),
                          curve: Curves.ease,
                        );
                      }
                    },
                    child: const Text('Next'),
                  ),
                ],
              );
            },
          ),
        ),
      ),
    );
  }
}

关键改动说明

  1. 移除全局变量:将原全局的tecList、_formKey等移到State类内部,避免全局状态污染。
  2. 多GlobalKey管理:通过List<GlobalKey<FormState>>为每个Form分配独立的key,彻底解决重复key错误。
  3. 焦点节点控制:用List<FocusNode>给每个输入框绑定焦点节点,监听PageController的页面切换事件,在页面切换完成后自动请求当前页面输入框的焦点。
  4. 资源清理:在dispose方法中销毁所有控制器和焦点节点,防止内存泄漏。

内容的提问来源于stack exchange,提问作者Saied Rahimi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 20:25:29