Flutter不重载Widget实现状态更新的问题咨询
问题:Flutter添加任务后ToDoList无法自动刷新
我在导航栏的initState中调用API获取任务数据,避免切换屏幕时重复请求,但在其他页面通过POST新增任务后,展示任务的ToDoList Widget不会自动更新,只有重启APP才能显示新数据。
导航栏代码(API调用位置)
class CustomBottomNavigationState extends State<CustomBottomNavigation> { bool isLoading = true; int index = 2; final screens = [ MenuScreen(), LeaveScreen(), // TaskList(), HomeScreen(), // PaySlipScreen(), TaskList(), Claimz_category(), // ClaimzScreen() ]; @override void initState() { Provider.of<LeaveRequestViewModel>(context, listen: false) .getLeaveRequest() .then((value) { Provider.of<AnnouncementViewModel>(context, listen: false) .getAllAnouncements() .then((value) { Provider.of<TodaysTaskList>(context, listen: false) .getTodaysTasks() // 目标API调用 .then((value) { setState(() { isLoading = false; }); }); }); }); super.initState(); } @override Widget build(BuildContext context) { final items = ['The icons are stored here']; return SafeArea( child: Scaffold( body: isLoading ? const Center( child: CircularProgressIndicator(), ) : screens[index], extendBody: true, bottomNavigationBar: Container( decoration: const BoxDecoration( borderRadius: BorderRadius.only( topLeft: Radius.circular(200), topRight: Radius.circular(200)), boxShadow: [ BoxShadow( color: Colors.transparent, blurRadius: 10, offset: Offset(1, 2)) ]), child: CurvedNavigationBar( items: items, index: index, height: 60, color: const Color.fromARGB(255, 70, 70, 70), backgroundColor: Colors.transparent, onTap: (index) => setState(() { this.index = index; })), ), ), ); } }
ToDoList Widget(无法自动更新的Widget)
class ToDoListState extends State<ToDoList> { @override Widget build(BuildContext context) { final toDoList = Provider.of<TodaysTaskList>(context).getToDoList; // 获取API返回的数据 return ContainerStyle( height: SizeVariables.getHeight(context) * 0.35, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ Padding( padding: EdgeInsets.only( top: SizeVariables.getHeight(context) * 0.015, left: SizeVariables.getWidth(context) * 0.04), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Container( child: FittedBox( fit: BoxFit.contain, child: Text( 'To do list', style: Theme.of(context).textTheme.caption, ), ), ), ], ), ), SizedBox(height: SizeVariables.getHeight(context) * 0.01), Padding( padding: EdgeInsets.only( left: SizeVariables.getWidth(context) * 0.04, top: SizeVariables.getHeight(context) * 0.005, right: SizeVariables.getWidth(context) * 0.04), child: SizedBox( height: SizeVariables.getHeight(context) * 0.25, child: Container( child: toDoList['today'].isEmpty ? Center( child: Lottie.asset('assets/json/ToDo.json'), ) : ListView.separated( physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) => Row( children: [ Icon(Icons.circle, color: Colors.white, size: SizeVariables.getWidth(context) * 0.03), SizedBox( width: SizeVariables.getWidth(context) * 0.02), FittedBox( fit: BoxFit.contain, child: Text( toDoList['today'][index]['task_name'], // 使用数据渲染任务 overflow: TextOverflow.ellipsis, style: Theme.of(context) .textTheme .bodyText1), ) ], ), separatorBuilder: (context, index) => Divider( height: SizeVariables.getHeight(context) * 0.045, color: Colors.white, thickness: 0.5, ), itemCount: toDoList['today'].length > 4 ? 4 : toDoList['today'].length), ), ), ) ], ), ); } }
添加数据的Widget
class _TaskListState extends State<TaskList> { @override Widget build(BuildContext context) { var floatingActionButton; return Scaffold( backgroundColor: Colors.black, floatingActionButton: Container( .... ...., child: FloatingActionButton( backgroundColor: Color.fromARGB(255, 70, 69, 69), onPressed: openDialog, // 打开添加任务弹窗 child: Icon(Icons.add), ), ), ), body: Container( .... .... .... ), ); } Future<dynamic> openDialog() => showDialog( context: context, builder: (context) => AlertDialog( backgroundColor: Color.fromARGB(255, 87, 83, 83), content: Form( key: _key, child: TextFormField( controller: taskController, maxLines: 5, style: Theme.of(context).textTheme.bodyText1, decoration: InputDecoration( border: InputBorder.none, ), validator: (value) { if (value!.isEmpty || value == '') { return 'Please Enter Task'; } else { input = value; } }, ), ), actions: [ InkWell( onTap: () async { showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2010), lastDate: DateTime.now().add(const Duration(days: 365))) .then((date) { setState(() { _dateTime = date; }); print('Date Time: ${dateFormat.format(_dateTime!)}'); }); }, child: const Icon(Icons.calendar_month, color: Colors.white)), TextButton( child: Text( "Add", style: Theme.of(context).textTheme.bodyText1, ), onPressed: () async { Map<String, dynamic> _data = { 'task': taskController.text, 'task_date': dateFormat.format(_dateTime!).toString() }; print(_data); if (_key.currentState!.validate()) { await Provider.of<ToDoViewModel>(context, listen: false) .addToDo(_data, context) // 提交任务的POST请求 .then((_) { Navigator.of(context).pop(); Provider.of<TodaysTaskList>(context, listen: false) .getTodaysTasks(); // 尝试重新获取数据 }); } }, ), ], ), ); void add() { Navigator.of(context).pop(); } }
Get API调用代码
class TodaysTaskList with ChangeNotifier { Map<String, dynamic> _getToDoList = {}; Map<String, dynamic> get getToDoList { return {..._getToDoList}; } Future<void> getTodaysTasks() async { SharedPreferences localStorage = await SharedPreferences.getInstance(); var response = await http.get(Uri.parse(AppUrl.toDoList), headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ${localStorage.getString('token')}' }); if (response.statusCode == 200) { _getToDoList = json.decode(response.body); } else { _getToDoList = {}; } print('TO DO LIST: $_getToDoList'); notifyListeners(); } }
解决方案
你的核心逻辑没问题,但有几个细节导致刷新失效,按优先级尝试以下方法:
1. 调换刷新与弹窗关闭的顺序
当前代码是先关闭弹窗再刷新数据,可能导致context失效,无法正确通知监听。改为先刷新再关闭:
if (_key.currentState!.validate()) { // 先完成POST请求 await Provider.of<ToDoViewModel>(context, listen: false).addToDo(_data, context); // 等待数据刷新完成 await Provider.of<TodaysTaskList>(context, listen: false).getTodaysTasks(); // 再关闭弹窗回到主页 Navigator.of(context).pop(); }
2. 用Consumer替代直接Provider.of
确保ToDoList能正确监听数据变化,替换原build方法:
class ToDoListState extends State<ToDoList> { @override Widget build(BuildContext context) { return Consumer<TodaysTaskList>( builder: (context, taskModel, child) { final toDoList = taskModel.getToDoList; // 原Widget代码完全移到这里 return ContainerStyle( height: SizeVariables.getHeight(context) * 0.35, child: Column( // ... 原代码内容不变 ), ); }, ); } }
3. 切换到主页时强制刷新
在导航栏切换到HomeScreen(index=2)时,主动触发数据刷新:
// 导航栏的CurvedNavigationBar onTap回调 onTap: (newIndex) { setState(() { index = newIndex; }); // 切换到主页时刷新任务数据 if (newIndex == 2) { Provider.of<TodaysTaskList>(context, listen: false).getTodaysTasks(); } }
4. 验证POST请求是否成功
检查addToDo方法的响应,确保任务确实添加到数据库:
// 示例addToDo实现,添加日志 Future<void> addToDo(Map<String, dynamic> data, BuildContext context) async { SharedPreferences localStorage = await SharedPreferences.getInstance(); final response = await http.post( Uri.parse(AppUrl.addTask), headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ${localStorage.getString('token')}' }, body: json.encode(data), ); print('Add Task Status: ${response.statusCode}, Body: ${response.body}'); if (response.statusCode != 200 && response.statusCode != 201) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('任务添加失败')) ); } }
内容的提问来源于stack exchange,提问作者coolhack7
相关产品推荐
相关产品推荐

