Flutter中如何将异步读取的JSON文件值赋值给类成员?
问题描述
我想要在Flutter中使用path_provider包异步读取JSON文件中的值,并将这些值赋值给类的成员。我已实现了用于处理文件操作的DataStorage类:
class DataStorage { DataStorage() { initFile(); } Future<String> get docDirPath async { final dir = await getApplicationDocumentsDirectory(); return dir.path; } String fileName = '/lists.json'; Future<File> get listsFile async { final path = await docDirPath; return File(path + fileName); } Future<Map<String, dynamic>> readFile() async { try { final file = await listsFile; final data = jsonDecode(await file.readAsString(encoding: utf8)); return data; } on Exception catch (e) { return {"error": e.toString()}; } } Future<File> writeFile(Map<String, dynamic> data) async { final file = await listsFile; final jsonData = jsonEncode(data); return file.writeAsString(jsonData); } Future<Map<String, dynamic>> loadTemplate() async { return jsonDecode(await rootBundle.loadString('assets/template.json')); } // 创建文件(如果不存在则从模板生成) void initFile() async { if (!(await listsFile).existsSync()) { writeFile(await loadTemplate()); } } }
同时我定义了如下示例类:
class Data { int id; String label; bool state; Data({ required this.id, required this.label, required this.state }); factory Data.fromJson(Map<String, dynamic> json) => Data( id: json['id'] as int, label: json['label'] as String, state: json['state'] as bool); } class Demo { DataStorage storage = DataStorage(); Data data; // 如何用 Data.fromJson(await storage.readFile()) 初始化这个成员? }
我无法创建异步构造函数,也无法在StatefulWidget的initState方法中直接使用异步操作,请问该如何初始化Demo类中的data成员?
解决方案
这里提供几种可行方案,可根据使用场景选择:
1. 异步工厂构造函数
普通构造函数无法异步,但可以用工厂构造函数返回Future<Demo>,在内部处理异步逻辑:
class Demo { DataStorage storage = DataStorage(); late Data data; // 延迟初始化 // 异步工厂构造函数 factory Demo.fromStorage() async { final demo = Demo(); final jsonData = await demo.storage.readFile(); // 处理文件读取可能返回的错误 if (jsonData.containsKey('error')) { throw Exception(jsonData['error']); } demo.data = Data.fromJson(jsonData); return demo; } }
使用方式:
// 在异步上下文(比如async函数、FutureBuilder)中创建实例 Demo demo = await Demo.fromStorage();
2. 单独的异步初始化方法
给Demo类添加初始化方法,创建实例后手动调用:
class Demo { DataStorage storage = DataStorage(); late Data data; // 异步初始化方法 Future<void> init() async { final jsonData = await storage.readFile(); if (jsonData.containsKey('error')) { throw Exception(jsonData['error']); } data = Data.fromJson(jsonData); } }
使用方式:
Demo demo = Demo(); await demo.init(); // 初始化完成后才能使用data成员
3. 在StatefulWidget中处理
如果是在Widget中使用Demo,可以通过以下两种方式处理:
方式一:FutureBuilder自动管理异步状态
class DemoWidget extends StatefulWidget { @override State<DemoWidget> createState() => _DemoWidgetState(); } class _DemoWidgetState extends State<DemoWidget> { final Demo demo = Demo(); @override Widget build(BuildContext context) { return FutureBuilder( future: demo.init(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); // 加载中状态 } else if (snapshot.hasError) { return Text('加载失败: ${snapshot.error}'); // 错误状态 } else { return Text('Label: ${demo.data.label}'); // 初始化完成 } }, ); } }
方式二:initState内调用匿名异步函数
initState不能直接标记为async,但可以在内部创建匿名异步函数执行逻辑,完成后通过setState更新UI:
class DemoWidget extends StatefulWidget { @override State<DemoWidget> createState() => _DemoWidgetState(); } class _DemoWidgetState extends State<DemoWidget> { final Demo demo = Demo(); bool isLoading = true; String? errorMsg; @override void initState() { super.initState(); _initDemoData(); } Future<void> _initDemoData() async { try { await demo.init(); } catch (e) { errorMsg = e.toString(); } finally { setState(() { isLoading = false; }); } } @override Widget build(BuildContext context) { if (isLoading) { return const CircularProgressIndicator(); } else if (errorMsg != null) { return Text('加载失败: $errorMsg'); } else { return Text('Label: ${demo.data.label}'); } } }
额外优化建议
你当前的DataStorage构造函数同步调用异步的initFile(),可能导致文件未初始化完成就调用readFile()。建议修改为:
class DataStorage { DataStorage._private(); // 私有构造函数 factory DataStorage() => DataStorage._private(); // 改为异步的文件初始化方法 Future<void> initFile() async { final file = await listsFile; if (!file.existsSync()) { await writeFile(await loadTemplate()); } } // 读取文件前先确保初始化完成 Future<Map<String, dynamic>> readFile() async { try { await initFile(); final file = await listsFile; final data = jsonDecode(await file.readAsString(encoding: utf8)); return data; } on Exception catch (e) { return {"error": e.toString()}; } } }
内容的提问来源于stack exchange,提问作者Michał
相关产品推荐
相关产品推荐

