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

Flutter页面导航触发setState()/markNeedsBuild()报错的解决案例

解决Flutter中导航到首页时的setState() or markNeedsBuild() called during build错误

问题描述

我的Flutter应用首页功能正常,但每次导航到该页面时都会抛出错误:setState() or markNeedsBuild() called during build。错误提示提到调用MediaQuery时触发构建,但没找到直接关联点。虽然不影响应用运行,但想彻底修复这个报错。

首页核心代码:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'package:gap/gap.dart';
import 'package:ronasapp/providers/providers.dart';
import 'package:ronasapp/screens/screens.dart';
import 'package:ronasapp/utils/extensions.dart';
import '../widgets/widgets.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});
  

  @override
  Widget build(BuildContext context) {
    final movieProvider = Provider.of<Movies>(context, listen: false);
    final directorProvider = Provider.of<Directors>(context, listen: false);
    final genreProvider = Provider.of<Genres>(context, listen: false);

    return Scaffold(
      extendBody: true,
      resizeToAvoidBottomInset: true,
      body: SafeArea(
        child: Stack(
          children: [
            SingleChildScrollView(
              child: Padding(
                padding:
                    const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Padding(
                      padding: const EdgeInsets.symmetric(vertical: 10),
                      child: Text(
                        "Popular directors",
                        style: context.textTheme.titleLarge!.copyWith(
                          fontWeight: FontWeight.w400,
                          fontSize: 26,
                        ),
                      ),
                    ),
                    //directors list
                    SizedBox(
                      height: 140,
                      child: ListView.separated(
                        scrollDirection: Axis.horizontal,
                        itemCount: directorProvider.directors.length,
                        itemBuilder: (context, index) {
                          final director = directorProvider.directors[index];
                          return GestureDetector(
                            onTap: () {
                              Navigator.of(context).pushNamed(
                                  DirectorDetails.routeName,
                                  arguments: director);
                            },
                            child: DirectorAvatar(
                              director: director,
                            ),
                          );
                        },
                        separatorBuilder: ((context, index) => const Gap(5)),
                      ),
                    ),
                    const WhiteLargeText(text: "Trending"),
                    const Gap(10),
                    //new movies
                    FutureBuilder(
                      future: movieProvider.getTrendingMovies(),
                      builder: (context, snapshot) {
                        if (snapshot.connectionState ==
                            ConnectionState.waiting) {
                          return const Center(
                            child: CircularProgressIndicator(),
                          );
                        } else if (snapshot.hasData) {
                          final data = snapshot.data;
                          return MovieCardsList(movies: data!);
                        } else if (snapshot.hasError) {
                          return Text(snapshot.error.toString());
                        } else {
                          return const Text("Error");
                        }
                      },
                    ),
                    const WhiteLargeText(text: "Genres"),
                    const Gap(10),
                    FutureBuilder(
                        future: genreProvider.getGenres(),
                        builder: (context, snapshot) {
                          if (snapshot.connectionState ==
                              ConnectionState.waiting) {
                            return const Center(
                                child: CircularProgressIndicator());
                          } else if (snapshot.hasData) {
                            return SizedBox(
                              height: 110,
                              child: ListView.separated(
                                scrollDirection: Axis.horizontal,
                                itemBuilder: (context, index) {
                                  final genre = genreProvider.genres[index];
                                  final moviesInGenre =
                                      movieProvider.getMoviesByGenre(genre.id);
                                  return GestureDetector(
                                      onTap: () {
                                        Navigator.of(context).pushNamed(
                                          SpecificCategoryScreen.routeName,
                                          arguments: [
                                            genre.name,
                                            moviesInGenre
                                          ],
                                        );
                                      },
                                      child: GenreCard(genre: genre));
                                },
                                separatorBuilder: (context, index) =>
                                    const Gap(10),
                                itemCount: genreProvider.genres.length,
                              ),
                            );
                          } else {
                            return const Text("Error");
                          }
                        }),

                    const Gap(20),
                    const WhiteLargeText(text: "Coming soon"),
                    const Gap(10),
                    const ComingSoonMovieCard(),
                    // const Gap(70),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

已验证的解决方案

在MoviesProvider中重写notifyListeners方法,用WidgetsBinding.instance.addPostFrameCallback延迟通知时机:

@override
void notifyListeners() {
  WidgetsBinding.instance.addPostFrameCallback(
    (_) {
      super.notifyListeners();
    },
  );
}

如果Directors和Genres这两个Provider也存在类似的状态更新逻辑,建议对它们也做同样修改,避免同类报错。

问题根源

这个错误本质是在Widget构建过程中触发了状态更新(调用notifyListeners),Flutter框架不允许在build流程中修改状态,否则会引发渲染冲突。

从首页代码看,问题大概率出在两个FutureBuilder调用的方法里:

  • movieProvider.getTrendingMovies()
  • genreProvider.getGenres()

这两个方法获取数据后,可能直接调用了notifyListeners(),而此时HomeScreen正处于build阶段,状态更新触发的UI重建请求和当前build流程冲突,于是抛出警告。

用addPostFrameCallback把notifyListeners()的执行延迟到当前帧构建完成后,就能避开构建期间的状态变更冲突,彻底解决这个警告。

内容的提问来源于stack exchange,提问作者MehdiDx

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 03:12:43