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

Flutter启动时加载JSON数据:Provider重建致已加载数据丢失的问题解决咨询

问题根源分析

你遇到的核心问题是创建了两个完全独立的QuestionProvider实例:

  • 在initState里,你实例化了一个临时的QuestionProvider并调用了loadJsonFiles(),数据加载到了这个实例的questionList里;
  • 但在MultiProvider的create方法中,你又新建了一个全新的QuestionProvider,这个实例的questionList是空的,自然就看不到之前加载的数据了。
解决方案:复用同一个Provider实例

我们只需要在initState时创建并初始化好QuestionProvider实例,之后在MultiProvider中直接复用这个实例,而不是重新创建。这样数据就会保存在同一个实例的列表中,不会被清空。

修改后的完整代码

void main() { runApp(const MyApp()); }
class MyApp extends Stateful Widget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late Future<void> _initFuture;
  late QuestionProvider _questionProvider; // 保存Provider实例

  @override
  void initState() {
    super.initState();
    _questionProvider = QuestionProvider(); // 初始化同一个实例
    _initFuture = _questionProvider.loadJsonFiles(); // 用这个实例加载数据
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _initFuture,
      builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const MaterialApp(home: Splash());
        } else {
          return MultiProvider(
            providers: [
              // 直接传入已初始化好的实例,而不是重新创建
              ChangeNotifierProvider.value(value: _questionProvider),
            ],
            child: MaterialApp(
              title: "MyApp",
              theme: ThemeData(
                primarySwatch: Colors.amber,
              ),
              home: const Home(),
            )
          );
        }
      },
    );
  }
}

class QuestionProvider with ChangeNotifier {
  final List<QuestionModel> questionList = <QuestionModel>[];

  Future<void> loadJsonFiles() async {
    final String response = await rootBundle.loadString("assets/questions.json");
    final Map<String, dynamic> data = await jsonDecode(response);
    // 先清空列表(防止重复加载时数据重复)
    questionList.clear();
    for (int i = 0; i < data.length; i++) {
      questionList.add(QuestionModel.fromJson(data[i]));
    }
    // 加载完成后通知监听者(如果后续页面需要响应数据变化的话)
    notifyListeners();
  }
}

关键修改点说明

  1. 保存Provider实例:在_MyAppState中声明_questionProvider变量,在initState中初始化,确保整个生命周期只使用这一个实例。
  2. 使用ChangeNotifierProvider.value:在MultiProvider中不再通过create新建实例,而是用value传入已初始化好的_questionProvider。
  3. 添加notifyListeners():数据加载完成后调用这个方法,确保依赖该Provider的页面能及时获取到最新数据(如果你的Home页面需要监听数据变化的话)。
  4. 清空列表防重复:在loadJsonFiles开头添加questionList.clear(),避免后续如果重新调用该方法时数据重复累积。
另一种思路:在Provider内部处理初始化

如果你不想在State中保存实例,也可以让Provider自己处理初始化逻辑,同时确保初始化只执行一次:

class QuestionProvider with ChangeNotifier {
  final List<QuestionModel> questionList = <QuestionModel>[];
  bool _isInitialized = false;

  Future<void> initialize() async {
    if (_isInitialized) return; // 防止重复初始化
    final String response = await rootBundle.loadString("assets/questions.json");
    final Map<String, dynamic> data = await jsonDecode(response);
    questionList.clear();
    for (int i = 0; i < data.length; i++) {
      questionList.add(QuestionModel.fromJson(data[i]));
    }
    _isInitialized = true;
    notifyListeners();
  }
}

然后在initState中:

@override
void initState() {
  super.initState();
  _questionProvider = QuestionProvider();
  _initFuture = _questionProvider.initialize();
}

这种方式更符合单一职责原则,把数据初始化的逻辑封装在Provider内部。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 19:17:42