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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 09:00:58