Flutter切换页面时保持TextField输入值不变的实现问题
Flutter页面切换时保留TextField输入内容解决方案
我已经在Flutter中实现了ExpenseScreen和IncomeScreen两个页面,通过切换按钮可以在两个页面间来回跳转。现在需要实现切换页面时,页面中金额输入框TextField的内容保持不变。以下是我的主文件代码和ExpenseScreen代码(IncomeScreen与前者基本一致),要求不使用自定义Widget解决该问题。
原主文件代码
class _MyAppState extends State<MyApp> { bool isexpense=true; void toogleit() { setState(() { isexpense=!isexpense; }); } @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: isexpense==true? ExpenseScreen(ontap: toogleit,txtvalue: '',):IncomeScreen(ontap: toogleit,txtvalue: '',) ); } }
原ExpenseScreen代码
class ExpenseScreen extends StatefulWidget { final VoidCallback ontap; String txtvalue; ExpenseScreen({Key? key, required this.ontap,required this.txtvalue}) : super(key: key); @override State<ExpenseScreen> createState() => _ExpenseScreenState(); } class _ExpenseScreenState extends State<ExpenseScreen> { TextEditingController txtcontroller = TextEditingController(); @override void initState() { super.initState(); txtcontroller.text=widget.txtvalue; } @override void dispose() { txtcontroller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey, body: Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0), child: Container( height: 300, decoration: kboxdecoration1, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ IntrinsicWidth( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 40.0), child: TextField( onChanged: (x) { setState(() { }); }, controller: txtcontroller, keyboardType: TextInputType.number, textAlign: TextAlign.center, style: TextStyle( fontSize: 30, ), decoration: kinputdecoration1.copyWith( hintText: txtcontroller.text == '' ? 'Amount' : null), ), ), ), SizedBox( height: 10, ), ElevatedButton( style: kelevetedbutton1, onPressed: () {}, child: Text('Save Expense')), SizedBox( height: 10, ), TextButton( onPressed: widget.ontap, child: Text('Jump To Income')), ], ), ), ), ), ), ); } }
解决方案
问题根源在于当前切换页面时,每次都会重新创建页面实例,且传入的txtvalue是空字符串,导致输入内容无法保留。我们需要在父组件中维护两个页面的输入值,切换时传递对应的值,并在页面输入变化时同步更新父组件的存储值。
修改后的主文件代码
class _MyAppState extends State<MyApp> { bool isexpense = true; // 分别存储支出和收入页面的输入内容 String expenseInput = ''; String incomeInput = ''; void toggleIt() { setState(() { isexpense = !isexpense; }); } // 更新支出输入内容的回调 void updateExpenseInput(String value) { setState(() { expenseInput = value; }); } // 更新收入输入内容的回调 void updateIncomeInput(String value) { setState(() { incomeInput = value; }); } @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: isexpense ? ExpenseScreen( ontap: toggleIt, txtvalue: expenseInput, onInputChanged: updateExpenseInput, ) : IncomeScreen( ontap: toggleIt, txtvalue: incomeInput, onInputChanged: updateIncomeInput, ), ); } }
修改后的ExpenseScreen代码
class ExpenseScreen extends StatefulWidget { final VoidCallback ontap; final String txtvalue; // 添加输入变化的回调,用于同步到父组件 final Function(String) onInputChanged; ExpenseScreen({ Key? key, required this.ontap, required this.txtvalue, required this.onInputChanged, }) : super(key: key); @override State<ExpenseScreen> createState() => _ExpenseScreenState(); } class _ExpenseScreenState extends State<ExpenseScreen> { late TextEditingController txtcontroller; @override void initState() { super.initState(); txtcontroller = TextEditingController(text: widget.txtvalue); } @override void didUpdateWidget(covariant ExpenseScreen oldWidget) { super.didUpdateWidget(oldWidget); // 当父组件传递的txtvalue变化时,更新控制器内容 if (oldWidget.txtvalue != widget.txtvalue) { txtcontroller.text = widget.txtvalue; } } @override void dispose() { txtcontroller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey, body: Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0), child: Container( height: 300, decoration: kboxdecoration1, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ IntrinsicWidth( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 40.0), child: TextField( onChanged: (x) { // 输入变化时调用回调,同步到父组件 widget.onInputChanged(x); }, controller: txtcontroller, keyboardType: TextInputType.number, textAlign: TextAlign.center, style: const TextStyle( fontSize: 30, ), decoration: kinputdecoration1.copyWith( hintText: txtcontroller.text == '' ? 'Amount' : null, ), ), ), ), const SizedBox(height: 10), ElevatedButton( style: kelevetedbutton1, onPressed: () {}, child: const Text('Save Expense'), ), const SizedBox(height: 10), TextButton( onPressed: widget.ontap, child: const Text('Jump To Income'), ), ], ), ), ), ), ), ); } }
IncomeScreen修改说明
IncomeScreen的修改逻辑和ExpenseScreen完全一致:
- 添加
final Function(String) onInputChanged参数 - 在TextField的
onChanged中调用widget.onInputChanged(x) - 实现
didUpdateWidget方法同步父组件传递的输入值
这样切换页面时,两个页面的输入内容都会被父组件保存,再次切换回来时会自动恢复之前的输入内容。
内容的提问来源于stack exchange,提问作者Irfan Ganatra
相关产品推荐
相关产品推荐

