Flutter中setState切换页面时键盘自动弹出问题
Flutter AnimatedIndexedStack切换页面后键盘自动弹出问题
问题描述
我用Flutter开发移动应用,主页采用AnimatedIndexedStack实现页面切换,只需修改index即可完成切换。应用初始运行时页面切换无异常,但当切换到包含TextField的页面并编辑内容,关闭键盘后再切换到任意无TextField的页面时,键盘会自动弹出;此后无论起始和目标页面是什么,只要修改index,键盘都会自动弹出。
相关代码
AnimatedIndexedStack实现
import 'package:flutter/material.dart'; class AnimatedIndexedStack extends StatefulWidget { final int index; final List<Widget> children; const AnimatedIndexedStack({ Key? key, required this.index, required this.children, }) : super(key: key); @override _AnimatedIndexedStackState createState() => _AnimatedIndexedStackState(); } class _AnimatedIndexedStackState extends State<AnimatedIndexedStack> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _animation; int _index = 0; @override void initState() { _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 250), ); _animation = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: _controller, curve: Curves.ease, ), ); _index = widget.index; _controller.forward(); super.initState(); } @override void didUpdateWidget(AnimatedIndexedStack oldWidget) { super.didUpdateWidget(oldWidget); if (widget.index != _index) { _controller.reverse().then((_) { setState(() => _index = widget.index); _controller.forward(); }); } } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animation, builder: (context, child) { return Opacity( opacity: _controller.value, child: child, ); }, child: IndexedStack( index: _index, children: widget.children, ), ); } }
主页代码
class Homepage extends StatefulWidget { const Homepage({Key? key}) : super(key: key); @override _HomepageState createState() => _HomepageState(); } class _HomepageState extends State<Homepage> { var _currentIndex = 0; final _homeIndex = 0; final _titles = ['Home', 'New Booking', 'Trips', 'Payment History', 'Support', 'Profile']; final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>(); var _bookingStep = 0; @override void initState() { _currentIndex = _homeIndex; super.initState(); } @override Widget build(BuildContext context) { return WillPopScope( onWillPop: () async => _onWillPop(), child: Scaffold( key: scaffoldKey, extendBody: true, floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, drawer: AppDrawer(selectedIndex: _currentIndex, onItemTap: (index) { setState(() { _currentIndex = index; }); scaffoldKey.currentState!.closeDrawer(); },), body: SafeArea( bottom: false, child: AnimatedIndexedStack( index: _currentIndex, children: [ const BookingsScreen(), NewBookingScreen(onExit: () { setState(() { _currentIndex = 0; }); }, bookingStep: _bookingStep, nextStep: () { setState(() { _bookingStep = _bookingStep + 1; }); },), const Trips(), const PaymentHistoryScreen(), SupportScreen(), const ProfileScreen() ], ) ), ), ); } }
问题原因
IndexedStack的核心特性是保留所有子组件的状态,包括TextField的焦点状态。当你在含TextField的页面编辑后,即使手动关闭键盘,TextField可能仍持有焦点;切换页面时,IndexedStack只是隐藏了原页面,并未释放其状态,在AnimatedIndexedStack的切换动画完成后,原页面的TextField焦点被重新激活,导致键盘自动弹出。
解决方案
最直接有效的方法是在切换页面之前主动清除所有焦点,关闭键盘:
修改主页中所有切换
_currentIndex的逻辑,添加失焦代码:- 抽屉点击事件:
drawer: AppDrawer(selectedIndex: _currentIndex, onItemTap: (index) { // 强制关闭键盘,清除焦点 FocusManager.instance.primaryFocus?.unfocus(); setState(() { _currentIndex = index; }); scaffoldKey.currentState!.closeDrawer(); },), - NewBookingScreen的退出回调:
NewBookingScreen(onExit: () { FocusManager.instance.primaryFocus?.unfocus(); setState(() { _currentIndex = 0; }); }, bookingStep: _bookingStep, nextStep: () { setState(() { _bookingStep = _bookingStep + 1; }); },),
- 抽屉点击事件:
(可选)在含
TextField的页面中,添加焦点监听,确保页面销毁时失焦:
以NewBookingScreen为例:class NewBookingScreen extends StatefulWidget { // ... 原有代码 } class _NewBookingScreenState extends State<NewBookingScreen> { late FocusNode _textFieldFocusNode; @override void initState() { super.initState(); _textFieldFocusNode = FocusNode(); } @override void dispose() { // 页面销毁时主动失焦 _textFieldFocusNode.unfocus(); _textFieldFocusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return TextField( focusNode: _textFieldFocusNode, // ... 其他配置 ); } }
总结
通过在页面切换前强制清除焦点,就能避免IndexedStack保留的TextField焦点导致键盘自动弹出的问题,同时配合页面销毁时的焦点清理,能进一步优化状态管理。
内容的提问来源于stack exchange,提问作者Danish Ajaib
相关产品推荐
相关产品推荐

