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

父组件SetState回调返回视图时无法刷新Riverpod驱动的FutureBuilder

问题描述

我正在开发一款社交信息流应用,包含帖子列表页面和发帖页面。期望用户发布帖子后完成以下流程:提交帖子到后端 → 调用帖子列表页的SetState回调 → 关闭发帖页面 → 帖子列表页重建并通过FutureBuilder重新获取最新帖子。但目前功能未正常生效,即使把Riverpod调用单独封装成函数也没用。


相关代码

social_feed.dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:irc/features/social/views/social_post_create.dart';

//Import Providers
import 'package:irc/shared/pocketbase_provider.dart';

//Import Feature Widgets
import 'package:irc/features/social/widgets/social_tile.dart';

class SocialFeedScaffold extends ConsumerStatefulWidget {
  const SocialFeedScaffold({super.key});
  @override
  ConsumerState<SocialFeedScaffold> createState() =>
      _SocialFeedScaffoldConsumerState();
}

class _SocialFeedScaffoldConsumerState
    extends ConsumerState<SocialFeedScaffold> {
  void setStateCallback() => setState(() {});

  @override
  Widget build(BuildContext context) {
    final pocketbase = ref.watch(pocketbaseProvider);
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: SingleChildScrollView(
            child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: Text("What other researchers are talking about...",
                  style: GoogleFonts.cardo(
                      fontSize: 32, fontWeight: FontWeight.bold)),
            ),
            // INLINE SOCIAL FEED
            FutureBuilder(
                future: pocketbase.collection('posts').getList(
                    perPage: 100, sort: '-created', expand: 'posted_by'),
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  if (snapshot.hasData) {
                    List postList = snapshot.data!.toJson()['items'];
                    return ListView.separated(
                        shrinkWrap: true,
                        itemCount: postList.length,
                        separatorBuilder: (context, index) =>
                            const SizedBox(height: 16),
                        itemBuilder: (BuildContext context, int index) {
                          return SocialTile(
                              postContent: postList[index]['post_content'],
                              postedBy: snapshot.data!.toJson()['items'][index]
                                  ['expand']['posted_by']['username']);
                        });
                  } else if (snapshot.connectionState ==
                      ConnectionState.waiting) {
                    return const CircularProgressIndicator();
                  } else if (snapshot.hasError) {
                    return Text(snapshot.error.toString());
                  } else {
                    return const CircularProgressIndicator();
                  }
                }),
          ],
        )),
      ),
      floatingActionButton: FloatingActionButton(
          backgroundColor: Colors.black,
          child: const Icon(Icons.post_add, color: Colors.white),
          onPressed: () {
            Navigator.of(context).push(MaterialPageRoute(
                builder: (context) => NewPostFormView(
                      setStateCallback: setStateCallback,
                    )));
          }),
    );
  }
}

social_post_create.dart

import 'package:flutter/material.dart';
// You're not importing FLUTTER RIVERPOD, just riverpod and that's fricking your code.
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:irc/shared/pocketbase_provider.dart';
import 'package:pocketbase/pocketbase.dart';
import 'dart:ui';

class NewPostFormView extends ConsumerStatefulWidget {
  // Class Constructor
  const NewPostFormView({super.key, required this.setStateCallback});

  // Class Fields
  final void setStateCallback;
  @override
  ConsumerState<NewPostFormView> createState() =>
      _newPostFormViewConsumerState();
}

class _newPostFormViewConsumerState extends ConsumerState<NewPostFormView> {
  // State of the Widget
  final TextEditingController userPostTextEditingController =
      TextEditingController();

  @override
  Widget build(BuildContext context) {
    final pb = ref.watch(pocketbaseProvider);
    return Scaffold(
      appBar: AppBar(title: const Text('New Post')),
      body: Center(
        child: Container(
            child: Padding(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                children: [
                  TextFormField(
                    controller: userPostTextEditingController,
                    maxLines: 10,
                  ),
                  SizedBox(height: 30),
                  Row(mainAxisAlignment: MainAxisAlignment.center, children: [
                    ElevatedButton(
                        child: Text("Submit Post"),
                        onPressed: () {
                          pb.collection('posts').create(body: {
                            'post_content': userPostTextEditingController.text,
                            'posted_by': pb.authStore.record!.id,
                            'likes': 1
                          }).then((value) => widget.setStateCallback);
                          Navigator.of(context).pop();
                        }),
                  ])
                ],
              ),
            ),
            height: MediaQuery.of(context).size.height / 3,
            width: MediaQuery.of(context).size.width / 3,
            color: Colors.grey),
      ),
    );
  }
}

问题修复方案

1. 修复回调函数未执行的问题

在social_post_create.dart中,你仅引用了setStateCallback函数但未调用,同时Navigator.pop需放在异步操作回调内,确保帖子提交成功后再关闭页面:

ElevatedButton(
  child: Text("Submit Post"),
  onPressed: () {
    pb.collection('posts').create(body: {
      'post_content': userPostTextEditingController.text,
      'posted_by': pb.authStore.record!.id,
      'likes': 1
    }).then((value) {
      widget.setStateCallback(); // 调用回调触发列表刷新
      Navigator.of(context).pop(); // 提交成功后关闭页面
    }).catchError((error) {
      // 可选:处理发帖失败场景
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text("发帖失败: ${error.toString()}")),
      );
    });
  },
),

2. 修正回调函数类型定义

在NewPostFormView中,回调字段应使用Flutter内置的VoidCallback类型,避免类型错误:

class NewPostFormView extends ConsumerStatefulWidget {
  const NewPostFormView({super.key, required this.setStateCallback});

  final VoidCallback setStateCallback; // 修正类型

  @override
  ConsumerState<NewPostFormView> createState() => _newPostFormViewConsumerState();
}

3. 优化FutureBuilder避免重复请求

当前FutureBuilder的future参数直接调用API,会导致每次build执行时重复发起请求。可将future存储在state变量中,仅在需要刷新时重新获取:

class _SocialFeedScaffoldConsumerState
    extends ConsumerState<SocialFeedScaffold> {
  late Future _postsFuture;
  late final PocketBase pocketbase;

  @override
  void initState() {
    super.initState();
    pocketbase = ref.read(pocketbaseProvider);
    _postsFuture = fetchPosts();
  }

  Future fetchPosts() {
    return pocketbase.collection('posts').getList(
      perPage: 100,
      sort: '-created',
      expand: 'posted_by',
    );
  }

  void setStateCallback() {
    setState(() {
      _postsFuture = fetchPosts(); // 刷新时重新获取数据
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // ... 其他代码不变
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: SingleChildScrollView(
          child: Column(
            children: [
              // ... 标题部分不变
              FutureBuilder(
                future: _postsFuture, // 使用存储的future
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  // ... 原builder逻辑不变
                },
              ),
            ],
          ),
        ),
      ),
      // ... 悬浮按钮部分不变
    );
  }
}

4. 更优雅的Riverpod状态管理方案(可选)

既然已使用Riverpod,推荐将帖子列表状态交给Riverpod管理,无需手动传递回调:

  • 创建帖子列表Provider:
final postsProvider = FutureProvider<List<dynamic>>((ref) async {
  final pocketbase = ref.watch(pocketbaseProvider);
  final response = await pocketbase.collection('posts').getList(
    perPage: 100,
    sort: '-created',
    expand: 'posted_by',
  );
  return response.toJson()['items'];
});
  • 帖子列表页使用AsyncValue监听状态:
class SocialFeedScaffold extends ConsumerWidget {
  const SocialFeedScaffold({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final postsAsync = ref.watch(postsProvider);
    return Scaffold(
      // ... 其他代码
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: SingleChildScrollView(
          child: Column(
            children: [
              // ... 标题部分
              postsAsync.when(
                loading: () => const CircularProgressIndicator(),
                error: (error, stack) => Text(error.toString()),
                data: (postList) {
                  return ListView.separated(
                    shrinkWrap: true,
                    itemCount: postList.length,
                    separatorBuilder: (context, index) => const SizedBox(height: 16),
                    itemBuilder: (context, index) {
                      return SocialTile(
                        postContent: postList[index]['post_content'],
                        postedBy: postList[index]['expand']['posted_by']['username'],
                      );
                    },
                  );
                },
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        backgroundColor: Colors.black,
        child: const Icon(Icons.post_add, color: Colors.white),
        onPressed: () {
          Navigator.of(context).push(MaterialPageRoute(
            builder: (context) => const NewPostFormView(),
          )).then((_) {
            ref.invalidate(postsProvider); // 发帖完成后刷新Provider
          });
        },
      ),
    );
  }
}
  • 修改发帖页面,移除回调参数:
class NewPostFormView extends ConsumerStatefulWidget {
  const NewPostFormView({super.key});

  @override
  ConsumerState<NewPostFormView> createState() => _newPostFormViewConsumerState();
}

class _newPostFormViewConsumerState extends ConsumerState<NewPostFormView> {
  final TextEditingController userPostTextEditingController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    final pb = ref.watch(pocketbaseProvider);
    return Scaffold(
      appBar: AppBar(title: const Text('New Post')),
      body: Center(
        // ... 原表单代码不变
        child: Container(
          // ... 原容器代码不变
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              children: [
                // ... 输入框不变
                ElevatedButton(
                  child: Text("Submit Post"),
                  onPressed: () {
                    pb.collection('posts').create(body: {
                      'post_content': userPostTextEditingController.text,
                      'posted_by': pb.authStore.record!.id,
                      'likes': 1
                    }).then((value) {
                      Navigator.of(context).pop();
                    }).catchError((error) {
                      ScaffoldMessenger.of(context).showSnackBar(
                        SnackBar(content: Text("发帖失败: ${error.toString()}")),
                      );
                    });
                  },
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:15:55