Flutter中GraphQL查询无法返回最新数据问题求助
Flutter GraphQL查询数据不更新问题解决
在Flutter中使用graphql_flutter执行列表查询时,新增条目后调用setState()重建Widget,界面依然显示旧数据(数据库已确认新条目存在),即使设置fetchPolicy: FetchPolicy.networkOnly也无法解决。
客户端初始化代码:
Future<ValueNotifier<GraphQLClient>> _getClient() async { final HttpLink httpLink = HttpLink( Constants.apiURL, defaultHeaders: { 'X-Parse-Application-Id': Constants.kParseApplicationId, 'X-Parse-Client-Key': Constants.kParseClientKey, }, ); // initialize Hive and wrap the default box in a HiveStore Directory directory = await pathProvider.getApplicationDocumentsDirectory(); final store = await HiveStore.open(path: directory.path); return ValueNotifier( GraphQLClient( cache: GraphQLCache(store: store), link: httpLink, ), ); }
核心问题:创建帖子后仅调用setState(),但Query组件不会自动重新发起查询,导致result.data仍为旧数据。
解决方案
方案1:调用Query的refetch方法
在Query的builder中获取refetch回调,在创建帖子成功后执行它,强制重新拉取最新数据。
修改后的代码:
class _FeedWidgetState extends State<FeedWidget> { final TextEditingController controller = TextEditingController(); VoidCallback? _refetch; // 保存refetch引用 void _createForumPost() async { if (_refetch == null) return; Map<String, dynamic> inputVariables = { "questionText": controller.text, }; GraphQLClient client = GraphQLProvider.of(context).value; await client.query( QueryOptions( document: gql(GraphQLQueries.createForumPost), variables: inputVariables, ), ); controller.text = ""; _refetch!(); // 创建成功后触发重新查询 } @override Widget build(BuildContext context) { return Query( options: QueryOptions( fetchPolicy: FetchPolicy.networkOnly, document: gql(GraphQLQueries.getForumEntries), ), builder: (QueryResult result, {VoidCallback? refetch, FetchMore? fetchMore}) { _refetch = refetch; // 保存refetch引用 if (result.hasException) { return Text(result.exception.toString()); } if (result.isLoading) { return const Center(child: CircularProgressIndicator()); } List<dynamic> forumEntries = result.data?["getForumEntries"] ?? []; return Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Row( children: [ Expanded( child: TextField( controller: controller, keyboardType: TextInputType.multiline, maxLines: null, autocorrect: false, decoration: InputDecoration( fillColor: Theme.of(context).colorScheme.surface, labelText: "Content", filled: true, border: InputBorder.none, ), ), ), const Padding(padding: EdgeInsets.symmetric(horizontal: 3)), CustomIconButton( padding: EdgeInsets.zero, icon: const Icon(Icons.send), onPressed: _createForumPost, ), ], ), const Padding(padding: EdgeInsets.only(bottom: 10)), Expanded( child: ListView.builder( shrinkWrap: true, itemCount: forumEntries.length, itemBuilder: (BuildContext context, int index) { Map<String, dynamic> entry = forumEntries[index]; return ForumEntryWidget(entry); }, ), ), ], ); }, ); } }
方案2:使用Mutation组件(推荐)
用graphql_flutter提供的Mutation组件替代手动调用client.query,在mutation成功后直接触发查询更新,更符合组件化最佳实践。
示例代码:
class _FeedWidgetState extends State<FeedWidget> { final TextEditingController controller = TextEditingController(); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Row( children: [ Expanded( child: TextField( controller: controller, keyboardType: TextInputType.multiline, maxLines: null, autocorrect: false, decoration: InputDecoration( fillColor: Theme.of(context).colorScheme.surface, labelText: "Content", filled: true, border: InputBorder.none, ), ), ), const Padding(padding: EdgeInsets.symmetric(horizontal: 3)), // 使用Mutation组件包裹按钮 Mutation( options: MutationOptions( document: gql(GraphQLQueries.createForumPost), // 成功后触发Query的refetch onCompleted: (_) { controller.text = ""; // 获取Query的refetch方法并调用 final queryResult = QueryResultProvider.of(context); queryResult.refetch?.call(); }, ), builder: (RunMutation runMutation, QueryResult? mutationResult) { return CustomIconButton( padding: EdgeInsets.zero, icon: const Icon(Icons.send), onPressed: () { if (controller.text.isEmpty) return; runMutation({ "questionText": controller.text, }); }, ); }, ), ], ), const Padding(padding: EdgeInsets.only(bottom: 10)), Expanded( child: Query( options: QueryOptions( fetchPolicy: FetchPolicy.networkOnly, document: gql(GraphQLQueries.getForumEntries), ), builder: (QueryResult result, {VoidCallback? refetch, FetchMore? fetchMore}) { if (result.hasException) { return Text(result.exception.toString()); } if (result.isLoading) { return const Center(child: CircularProgressIndicator()); } List<dynamic> forumEntries = result.data?["getForumEntries"] ?? []; return ListView.builder( shrinkWrap: true, itemCount: forumEntries.length, itemBuilder: (BuildContext context, int index) { Map<String, dynamic> entry = forumEntries[index]; return ForumEntryWidget(entry); }, ); }, ), ), ], ); } }
方案3:更新缓存(适合依赖缓存的场景)
如果希望通过缓存自动更新列表,在Mutation中设置update函数,将新创建的条目添加到缓存的列表中:
Mutation( options: MutationOptions( document: gql(GraphQLQueries.createForumPost), update: (Cache cache, QueryResult? result) { if (result?.data == null) return; // 获取缓存中的现有列表 final existingEntries = cache.readQuery( QueryOptions(document: gql(GraphQLQueries.getForumEntries)) )?["getForumEntries"] as List? ?? []; // 获取新创建的帖子 final newPost = result!.data!["createForumPost"]; // 更新缓存 cache.writeQuery( QueryOptions(document: gql(GraphQLQueries.getForumEntries)), data: {"getForumEntries": [...existingEntries, newPost]}, ); }, onCompleted: (_) => controller.text = "", ), // ... builder部分 )
关键原因说明
Query组件的渲染依赖自身内部状态,外层setState()只会重建Widget,但如果QueryOptions没有变化,组件不会自动发起新的网络请求。必须通过refetch、Mutation回调或缓存更新来触发数据刷新。
内容的提问来源于stack exchange,提问作者Wckd_Panda
相关产品推荐
相关产品推荐

