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
相关产品推荐
相关产品推荐

