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

Flutter加载服务端JSON前,如何先显示CircularProgressIndicator再启动应用?

我懂你的困扰——现在你在main函数里先await加载JSON数据,导致App启动后先白屏个4、5秒,用户体验很差。你想先显示CircularProgressIndicator,等数据完全加载好再展示完整的App界面对吧?

其实不用等到数据加载完再调用runApp(),反而应该先启动App,在界面里处理加载状态,这样用户一打开就能看到加载反馈,不会盯着白屏发呆。下面给你两种实用的实现方案:


方案一:用FutureBuilder自动管理加载状态

这是最简洁的方式,Flutter的FutureBuilder会帮你监听异步任务的状态,自动切换UI:

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'JSON数据加载示例',
      home: DataLoadingScreen(),
    );
  }
}

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

  @override
  State<DataLoadingScreen> createState() => _DataLoadingScreenState();
}

class _DataLoadingScreenState extends State<DataLoadingScreen> {
  // 定义异步加载任务
  late Future<List<dynamic>> _jsonDataFuture;

  @override
  void initState() {
    super.initState();
    // 页面初始化时启动数据加载
    _jsonDataFuture = _loadTwoJsonFiles();
  }

  // 你的JSON加载逻辑,替换成实际的服务器请求/本地文件读取
  Future<List<dynamic>> _loadTwoJsonFiles() async {
    // 模拟网络延迟,实际开发中删掉这行
    await Future.delayed(const Duration(seconds: 4));
    
    // 加载第一个JSON文件
    final data1Str = await rootBundle.loadString('assets/data1.json');
    final data1 = jsonDecode(data1Str);
    
    // 加载第二个JSON文件
    final data2Str = await rootBundle.loadString('assets/data2.json');
    final data2 = jsonDecode(data2Str);
    
    return [data1, data2];
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _jsonDataFuture,
      builder: (context, snapshot) {
        // 加载中:显示圆形指示器
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Scaffold(
            body: Center(
              child: CircularProgressIndicator(),
            ),
          );
        } 
        // 加载出错:显示错误信息
        else if (snapshot.hasError) {
          return Scaffold(
            body: Center(
              child: Text('加载失败:${snapshot.error}'),
            ),
          );
        } 
        // 加载完成:跳转到主页面并传入数据
        else {
          return MainScreen(loadedData: snapshot.data!);
        }
      },
    );
  }
}

// 你的主页面,接收加载好的数据
class MainScreen extends StatelessWidget {
  final List<dynamic> loadedData;
  const MainScreen({super.key, required this.loadedData});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('主页面')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('第一个JSON数据长度:${(loadedData[0] as List).length}'),
            const SizedBox(height: 16),
            Text('第二个JSON数据长度:${(loadedData[1] as List).length}'),
          ],
        ),
      ),
    );
  }
}

方案二:手动管理加载状态(更灵活)

如果你需要更精细地控制加载过程(比如添加重试按钮),可以手动维护isLoading、error等状态:

class _DataLoadingScreenState extends State<DataLoadingScreen> {
  List<dynamic>? _loadedData;
  bool _isLoading = true;
  String? _errorMsg;

  @override
  void initState() {
    super.initState();
    _loadData();
  }

  Future<void> _loadData() async {
    setState(() {
      _isLoading = true;
      _errorMsg = null;
    });
    
    try {
      // 加载两个JSON文件的逻辑和上面一致
      final data1Str = await rootBundle.loadString('assets/data1.json');
      final data1 = jsonDecode(data1Str);
      final data2Str = await rootBundle.loadString('assets/data2.json');
      final data2 = jsonDecode(data2Str);
      
      setState(() {
        _loadedData = [data1, data2];
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _errorMsg = e.toString();
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) {
      return const Scaffold(body: Center(child: CircularProgressIndicator()));
    } else if (_errorMsg != null) {
      return Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('加载失败:$_errorMsg'),
              ElevatedButton(
                onPressed: _loadData,
                child: const Text('重试'),
              ),
            ],
          ),
        ),
      );
    } else {
      return MainScreen(loadedData: _loadedData!);
    }
  }
}

关键注意点

  • 不要在main函数里await加载数据再调用runApp(),这样会导致App启动后一直白屏,直到数据加载完成。
  • 不管用哪种方案,都要处理加载失败的情况,给用户重试的机会或者明确的错误提示,提升体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:13:46