父组件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
相关产品推荐
相关产品推荐

