Flutter中UpdatingTaskPage无法显示widget传递的现有数据求助
问题描述
Flutter新手,从HomePage点击列表项跳转至UpdatingTaskPage时,color、date、startTime、endTime、remind、repeat字段仅显示默认值,而非通过widget传递的Firestore现有数据(例如Firestore中remind为15分钟,但页面显示默认的5分钟)。
相关截图
- HomePage界面:

- Firebase数据(remind值应为15分钟却显示默认5分钟,repeat为Daily而非None):

- UpdatingTaskPage显示默认值而非现有数据:

UpdatingTaskPage代码
class UpdatingTaskPage extends StatefulWidget { final String id, title, note, date, startTime, endTime, repeat; final int color, remind; final bool isCompleted; const UpdatingTaskPage( {super.key, required this.id, required this.title, required this.note, required this.date, required this.startTime, required this.endTime, required this.repeat, required this.color, required this.remind, required this.isCompleted}); @override State<UpdatingTaskPage> createState() => _UpdatingTaskPageState(); } class _UpdatingTaskPageState extends State<UpdatingTaskPage> { static TaskController controller = Get.put(TaskController()); final task = controller.tasks.length; TextEditingController titleController = TextEditingController(); TextEditingController noteController = TextEditingController(); late DateTime _selecteDate = DateTime.now(); late String _startTime = DateFormat('hh:mm a').format(DateTime.now()).toString(); String _endTime = DateFormat('hh:mm a') .format(DateTime.now().add(const Duration(minutes: 60))) .toString(); int _selectedRemind = 5; List<int> remindList = [5, 10, 15, 20]; String _selectedRepeat = 'None'; List<String> repeatList = ['None', 'Daily', 'Weekly', 'Monthly']; int _selectedColor = 0; List<Color> colorList = [ bluishClr, yellowClr, redClr, greenClr, ]; @override Widget build(BuildContext context) { titleController.text = widget.title; noteController.text = widget.note; return Scaffold( appBar: AppBar( title: const Text("Update task!"), centerTitle: true, actions: [ IconButton( onPressed: () { // this icon button is for the user to signout AuthController.instance.logout(); }, icon: const Icon(Icons.logout_rounded)) ], ), body: Container( padding: const EdgeInsets.symmetric(horizontal: 20.0), child: SingleChildScrollView( child: Column( children: [ //Title InputField( title: 'Title', hint: 'Enter your title here...', controller: titleController, ), //Note InputField( title: 'Note', hint: 'Enter your note here...', controller: noteController, ), //Date InputField( title: 'Date', hint: DateFormat.yMd().format(_selecteDate), child: IconButton( onPressed: () => _getDateFromUser(), icon: const Icon( Icons.calendar_today_outlined, color: Colors.grey, ), ), ), //Date Range Row( children: [ Expanded( child: InputField( title: 'Start Time', hint: _startTime, child: IconButton( onPressed: () => _getTimeFromUser(isStartTime: true), icon: const Icon( Icons.access_time_rounded, color: Colors.grey, ), ), ), ), Expanded( child: InputField( title: 'End Time', hint: _endTime, child: IconButton( onPressed: () => _getTimeFromUser(isStartTime: false), icon: const Icon( Icons.access_time_rounded, color: Colors.grey, ), ), ), ), ], ), //Remind InputField( title: 'Alert', hint: '$_selectedRemind minutes early', child: DropdownButton( onChanged: (String? newVal) { setState(() { _selectedRemind = int.parse(newVal!); }); }, items: remindList .map<DropdownMenuItem<String>>( (e) => DropdownMenuItem<String>( value: e.toString(), child: Text('$e minutes'), ), ) .toList(), icon: const Icon( Icons.keyboard_arrow_down, size: 32, color: Colors.grey, ), elevation: 3, underline: Container(height: 0), borderRadius: BorderRadius.circular(15), ), ), //Repeat InputField( title: 'Repeat', hint: _selectedRepeat, child: DropdownButton<String>( onChanged: (String? value) { setState(() { _selectedRepeat = value!; }); }, value: widget.repeat, items: repeatList .map<DropdownMenuItem<String>>( (e) => DropdownMenuItem<String>( value: e, child: Text(e), ), ) .toList(), icon: const Icon( Icons.keyboard_arrow_down, size: 32, color: Colors.grey, ), elevation: 3, underline: Container(height: 0), borderRadius: BorderRadius.circular(15), ), ), //Color Container( margin: const EdgeInsets.symmetric( vertical: 10.0, horizontal: 10.0), alignment: Alignment.centerLeft, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Color'), Row( children: List.generate( colorList.length, (index) => Padding( padding: const EdgeInsets.only(right: 5.0, top: 8), child: InkWell( onTap: () { setState(() { // Value(widget.color); _selectedColor = index; }); }, borderRadius: BorderRadius.circular(50), child: CircleAvatar( backgroundColor: colorList[index], child: widget.color == index ? null : index == _selectedColor ? const Icon( Icons.check, color: Colors.white, ) : null, // child: index == _selectedColor // ? const Icon( // Icons.check, // color: Colors.white, // ) // : null, ), ), ), ), ) ], ), ), ], ), ), ), floatingActionButton: MyButton( label: 'Save', onTap: () { _validateData(); }), ); } _validateData() async { if (titleController.text.isNotEmpty && noteController.text.isNotEmpty) { print(1); _updateTaskToDb(); Get.back(); } else if (titleController.text.isEmpty || noteController.text.isEmpty) { print(2); Get.snackbar( 'Required', 'All fileds are required', backgroundColor: Colors.white, snackPosition: SnackPosition.BOTTOM, colorText: Colors.black, icon: const Icon( Icons.warning_amber_rounded, color: Colors.red, ), ); } else { print('######## Error Here ! ########'); } } _updateTaskToDb() async { await FirestoreDB.updateTask( taskModel: Task( id: FirebaseAuth.instance.currentUser!.uid, title: titleController.text, note: noteController.text, isCompleted: false, date: DateFormat.yMd().format(_selecteDate), startTime: _startTime, endTime: _endTime, color: _selectedColor, remind: _selectedRemind, repeat: _selectedRepeat, ), documentId: widget.id, ); Get.snackbar( 'Updating', 'Successfully update', backgroundColor: whiteClr, colorText: Colors.black, ); } _getDateFromUser() async { DateTime? _pickedDate = await showDatePicker( context: context, initialDate: _selecteDate, firstDate: DateTime(2020), lastDate: DateTime(2030), ); if (_pickedDate != null) { setState(() { Value(widget.date); _selecteDate = _pickedDate; }); } else { print('picked date empty !'); } } _getTimeFromUser({required bool isStartTime}) async { TimeOfDay? _pickedTime = await showTimePicker( context: context, initialTime: TimeOfDay.now(), ); if (_pickedTime != null) { setState(() { if (isStartTime) { _startTime = _pickedTime.format(context); print(_startTime); } else { _endTime = _pickedTime.format(context); print(_endTime); } }); } } }
问题原因及解决方法
核心问题是未在页面初始化阶段将widget传递的Firestore数据赋值给对应状态变量,且部分控件绑定逻辑错误,修复步骤如下:
1. 在initState中初始化状态变量
initState是StatefulWidget初始化时仅执行一次的方法,适合用来将widget传递的数据赋值给状态变量,替换默认值:
@override void initState() { super.initState(); // 初始化文本控制器 titleController.text = widget.title; noteController.text = widget.note; // 将widget中的日期字符串转为DateTime对象 _selecteDate = DateFormat.yMd().parse(widget.date); // 初始化时间 _startTime = widget.startTime; _endTime = widget.endTime; // 初始化提醒时间 _selectedRemind = widget.remind; // 初始化重复选项 _selectedRepeat = widget.repeat; // 初始化颜色索引 _selectedColor = widget.color; }
2. 移除build方法中的控制器赋值
当前在build方法中设置titleController.text = widget.title,build会在每次setState时重新执行,会覆盖用户输入内容,必须将这两行移到initState中。
3. 修复Remind下拉框的value绑定
Remind的DropdownButton未设置value属性,无法显示选中的widget传递值,添加绑定:
DropdownButton( value: widget.remind.toString(), // 绑定widget传递的remind值 onChanged: (String? newVal) { setState(() { _selectedRemind = int.parse(newVal!); }); }, // 其他代码不变 )
4. 修复颜色选择器的选中状态逻辑
当前颜色选择器的选中判断逻辑混乱,直接判断索引是否等于_selectedColor即可(已在initState中赋值为widget.color):
CircleAvatar( backgroundColor: colorList[index], child: index == _selectedColor ? const Icon( Icons.check, color: Colors.white, ) : null, )
5. 修复时间选择器的初始值
当前时间选择器初始值为TimeOfDay.now(),应改为从widget传递的时间字符串解析而来:
_getTimeFromUser({required bool isStartTime}) async { // 解析现有时间字符串为TimeOfDay TimeOfDay initialTime = TimeOfDay.fromDateTime( DateFormat('hh:mm a').parse(isStartTime ? _startTime : _endTime) ); TimeOfDay? _pickedTime = await showTimePicker( context: context, initialTime: initialTime, // 使用解析后的初始时间 ); if (_pickedTime != null) { setState(() { if (isStartTime) { _startTime = _pickedTime.format(context); } else { _endTime = _pickedTime.format(context); } }); } }
6. 清理无用代码
删除_getDateFromUser方法中无用的Value(widget.date);代码,该行无实际作用。
完成以上修改后,UpdatingTaskPage即可正确显示Firestore传递的现有数据。
内容的提问来源于stack exchange,提问作者I Can Done

