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

调用setState时FutureBuilder反复重建,星星状态无法更新

解决Flutter Dashboard中星星按钮状态无法即时更新的问题

问题根源

  • 使用单个全局isGolden变量存储所有GridView项的收藏状态,每个项在itemBuilder中都会覆盖该变量的值
  • 调用setState后FutureBuilder重建,itemBuilder会重新从API返回的旧数据中赋值isGolden,导致手动切换的状态被覆盖,无法即时显示

解决方案

改用**Map<String, bool>**(以故事ID为键,更稳定)单独存储每个项的收藏状态,避免全局变量被覆盖;同时处理API请求的成功/失败,失败时回滚本地状态,保证数据一致性。

修改后的关键代码部分

  1. 替换状态变量
// 替换原来的单个bool isGolden
Map<String, bool> favoriteStates = {}; // 用故事ID作为键,确保状态与对应项绑定
  1. 初始化状态Map
    在FutureBuilder获取到数据后,从API返回的内容中填充状态Map:
if(snapshot.hasData){
  // 初始化每个故事的收藏状态
  favoriteStates = {
    for(var story in snapshot.data!.data!)
      story.id.toString(): story.favoriteStories!.length > 0
  };
  // 后续GridView代码...
}
  1. 在itemBuilder中获取对应项的状态
itemBuilder: (BuildContext context, index){
  final story = snapshot.data!.data![index];
  final storyId = story.id.toString();
  // 从Map中获取当前项的状态,不再覆盖全局变量
  final isGolden = favoriteStates[storyId] ?? false;
  // 后续Card代码...
}
  1. 修改星星按钮的点击逻辑
    先切换本地状态实现即时反馈,再处理API请求,失败时回滚状态:
onPressed: (){
  final storyId = snapshot.data!.data![index].id.toString();
  // 先切换本地状态,即时更新UI
  setState(() {
    favoriteStates[storyId] = !(favoriteStates[storyId] ?? false);
  });
  // 发起API请求
  futureMessage = PostStoryFavoriteCreateDelete(storyId);
  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
    elevation: 20,
    content: FutureBuilder(
      future: futureMessage,
      builder: (context, snackSnapshot){
        if (snackSnapshot.hasData) {
          return const Text("SuccessFully Updated");
        } else if (snackSnapshot.hasError) {
          // API请求失败,回滚本地状态
          setState(() {
            favoriteStates[storyId] = !(favoriteStates[storyId] ?? false);
          });
          return const Text("Failed To Update");
        }
        return const Text("Processing...");
      },
    ),
  ));
}

完整修改后的_DashBoardState代码

class _DashBoardState extends State<DashBoard> {
  final drawerWidget = const DrawerWidget();
  late Future<StoriesMobilePrivate> futureStoriesMobilePrivate;
  late Future<StoryFavoriteCreateDelete> futureMessage;
  // 替换单个isGolden为存储每个故事状态的Map
  Map<String, bool> favoriteStates = {};

  @override
  void initState() {
    super.initState();
    futureStoriesMobilePrivate = fetchFavoriteStoriesPrivate();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: true,
      extendBodyBehindAppBar: true,
      appBar: AppBar(
        backgroundColor: Colors.transparent,
        centerTitle: true,
        elevation: 0.0,
        title: const Text(
          "My Talez",
          style: TextStyle(
              color: Colors.black87, fontWeight: FontWeight.bold, fontSize: 23),
        ),
        iconTheme: const IconThemeData(color: Colors.black87),
        actions: [
          IconButton(onPressed: (){},
              icon: Image.asset("assets/images/Frame.png"))
        ],
      ),
      drawer: drawerWidget,
      body: Container(
        width: MediaQuery.of(context).size.width,
        decoration: const BoxDecoration(
          image: DecorationImage(
            image: AssetImage("assets/images/dashboard.png"),
            fit: BoxFit.cover,
          )
        ),
        child: Padding(
          padding: const EdgeInsets.only(top: 100),
          child: Column(
            children: [
              FutureBuilder(
                future: futureStoriesMobilePrivate,
                builder: (context, snapshot) {
                  if(snapshot.hasData){
                    // 初始化收藏状态Map
                    favoriteStates = {
                      for(var story in snapshot.data!.data!)
                        story.id.toString(): story.favoriteStories!.length > 0
                    };
                    return SingleChildScrollView(
                      child: SizedBox(
                        height: MediaQuery.of(context).size.height*.8,
                        child: GridView.builder(
                          padding: const EdgeInsets.all(20),
                          itemCount: snapshot.data!.data!.length,
                          gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                            childAspectRatio: .85,
                            crossAxisCount: 2,
                            crossAxisSpacing: 8,
                            mainAxisSpacing: 8,
                          ),
                          itemBuilder: (BuildContext context, index){
                            final story = snapshot.data!.data![index];
                            final storyId = story.id.toString();
                            final isGolden = favoriteStates[storyId] ?? false;
                            return Card(
                              elevation: 7,
                              shape: RoundedRectangleBorder(
                                borderRadius: BorderRadius.circular(10),
                              ),
                              color: const Color(0xFF609FFB),
                              child: Container(
                                child: Column(
                                  children: [
                                    Stack(
                                      children: [
                                        story.userStoryPages![0].mergedImage == null?
                                        Container(
                                          height: 160,
                                          width: 160,
                                          margin: const EdgeInsets.only(top: 8, right: 8, left: 8, bottom: 1),
                                          decoration: const BoxDecoration(
                                            color: Colors.white,
                                              image: DecorationImage(
                                                fit: BoxFit.fitHeight,
                                                image: AssetImage("assets/images/image 14.png"),
                                              )
                                          ),
                                        ):Container(
                                          height: 160,
                                          width: 160,
                                          margin: const EdgeInsets.only(top: 8, right: 8, left: 8, bottom: 1),
                                          decoration: BoxDecoration(
                                              image: DecorationImage(
                                                  fit: BoxFit.cover,
                                                  image: NetworkImage(story.userStoryPages![0].mergedImage.toString())
                                              )
                                          ),
                                        ),
                                        Positioned(
                                          top: 12,
                                          right: 12,
                                          child: SizedBox(
                                            height: 25,
                                            width: 25,
                                            child: CircleAvatar(
                                              backgroundColor: isGolden? Colors.amber :Colors.black87,
                                              child: IconButton(
                                                padding: EdgeInsets.zero,
                                                icon: const Icon(Icons.star),
                                                color: Color(0xFFFFFFD5),
                                                onPressed: (){
                                                  // 先切换本地状态,即时更新UI
                                                  setState(() {
                                                    favoriteStates[storyId] = !(favoriteStates[storyId] ?? false);
                                                  });
                                                  // 发起API请求
                                                  futureMessage = PostStoryFavoriteCreateDelete(storyId);
                                                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                                                    elevation: 20,
                                                    content: FutureBuilder(
                                                      future: futureMessage,
                                                      builder: (context, snackSnapshot){
                                                        if (snackSnapshot.hasData) {
                                                          return const Text("SuccessFully Updated");
                                                        } else if (snackSnapshot.hasError) {
                                                          // API失败,回滚状态
                                                          setState(() {
                                                            favoriteStates[storyId] = !(favoriteStates[storyId] ?? false);
                                                          });
                                                          return const Text("Failed To Update");
                                                        }
                                                        return const Text("Processing...");
                                                      },
                                                    ),
                                                  ));
                                                },
                                              ),
                                            ),
                                          ),
                                        )
                                      ],
                                    ),
                                    Padding(
                                      padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
                                      child: Row(
                                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                        children: [
                                          Row(
                                            children: [
                                              Text(story.title.toString(),
                                                style: const TextStyle(color: Colors.white),
                                              ),
                                            ],
                                          ),
                                          Row(
                                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                            children: [
                                              const Icon(Icons.share,
                                                size: 18,
                                                color: Colors.white,),
                                              const SizedBox(width: 5,),
                                              Image.asset("assets/images/delete 6.png",
                                                height: 22,
                                              )
                                            ],
                                          ),
                                        ],
                                      ),
                                    ),
                                  ],
                                ),
                              ),
                            );
                          },
                        ),
                      ),
                    );
                  } else if (snapshot.hasError){
                    return Text(snapshot.error.toString());
                  }
                  return const CircularProgressIndicator();
                }
              ),
              Card(
                color: const Color(0xFFF96162),
                elevation: 8,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(4),
                ),
                child: SizedBox(
                  height: 55,
                  width: MediaQuery.of(context).size.width*.84,
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Image.asset("assets/images/drawer icons/script 1.png"),
                      const SizedBox(width: 7,),
                      const Text("Create My Talez",
                        style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
                      ),
                    ],
                  )
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 23:28:08