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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 04:30:54