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

Flutter使用Provider获取数据出现无限循环问题求助

问题解决:Flutter Provider 无限触发数据获取的问题

问题原因

这种无限获取的情况完全不正常,核心是你在build方法里调用了会触发notifyListeners()的方法,形成了死循环:

  1. HomeScreen的build方法调用getWorkoutList(),触发_fetchWorkouts()发起网络请求
  2. 请求完成后调用notifyListeners(),通知UI重建
  3. UI重建时再次执行build方法,重复调用getWorkoutList(),循环往复

build方法的设计初衷是纯UI渲染逻辑,绝对不能包含网络请求、状态变更这类有副作用的操作。

解决方案

步骤1:重构ExerciseProvider

把数据获取逻辑和数据读取逻辑分离,避免在读取数据时自动触发请求,同时可以在初始化时自动加载数据:

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:workout_app/utilities/api.dart';

class ExerciseProvider with ChangeNotifier {
  final List list = [];

  // 构造函数初始化时自动加载数据
  ExerciseProvider() {
    _fetchWorkouts();
  }

  // 仅提供数据读取的getter,不触发请求
  List get workoutList => list;

  // 异步获取数据的私有方法
  Future<void> _fetchWorkouts() async {
    Response res = await GetApi(apiUrl: "/workout").getData();
    final result = res.data;

    list.clear();
    list.addAll(result);
    notifyListeners();
  }

  // 提供手动刷新的方法(可选)
  Future<void> refreshWorkouts() async {
    await _fetchWorkouts();
  }
}

步骤2:修改HomeScreen的build逻辑

在build里仅监听数据变化,不再触发请求:

@override
Widget build(BuildContext context) {
  // 仅监听数据,不触发请求
  final workoutList = context.watch<ExerciseProvider>().workoutList;
  print("list: $workoutList");
  return Scaffold(
    // ... 你的UI代码
  );
}

可选:手动触发数据刷新

如果不需要在初始化时自动加载数据,而是想在特定时机(比如下拉刷新、按钮点击)触发,可以在initState或事件回调里调用:

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  void initState() {
    super.initState();
    // 在页面初始化时触发数据获取
    context.read<ExerciseProvider>().refreshWorkouts();
  }

  @override
  Widget build(BuildContext context) {
    final workoutList = context.watch<ExerciseProvider>().workoutList;
    print("list: $workoutList");
    return Scaffold(
      // ... 你的UI代码
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 点击按钮手动刷新数据
          context.read<ExerciseProvider>().refreshWorkouts();
        },
        child: const Icon(Icons.refresh),
      ),
    );
  }
}

关键注意事项

  • build方法必须保持无副作用,只根据状态渲染UI
  • 使用context.watch监听状态变化,使用context.read触发状态变更(不会监听变化)
  • 数据获取逻辑应该放在Provider的初始化方法、页面的initState、或用户交互事件中

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 22:50:00