Flutter中如何在App初始化时一次性处理数据并全局复用?
问题描述
我用以下Map格式存储国家数据,方便维护大量记录与参数:
Map countries = { 'abw' : { 'continent' : "noam", 'population' : 112269, 'size' : 180, 'hasCapital': true }, 'afg' : { 'continent' : "apac", 'population' : 32890171, 'size' : 652864, 'hasCapital': true }, 'ago' : { 'continent' : "afri", 'population' : 24383301, 'size' : 1246700, 'hasCapital': true }, 'aia' : { 'continent' : "noam", 'population' : 14869, 'size' : 91, 'hasCapital': true }, 'ala' : { 'continent' : "euro", 'population' : 30055, 'size' : 1580, 'hasCapital': true }, };
我可以通过import 'filewithcountries.dart'在不同Widget/页面全局访问这份数据,但需要生成过滤、排序后的衍生数据,比如:
Map countriesByContinent = { 'afri' : ['ago'], 'apac' : ['afg'], 'euro' : ['ala'], 'noam' : ['abw','aia'], }; List<String> countriesBySize = ['aia', 'abw', 'ala', 'afg', 'ago'];
我知道可以用辅助方法生成这些数据,但每次使用都要重新过滤排序,想在App启动初始化时一次性处理好,之后直接复用,且不想用SQL数据库,该怎么做?
解决方案
1. 全局变量惰性初始化(最简方案)
在存储原始countries的文件里,直接用带缓存的全局getter实现,第一次访问时自动计算衍生数据,之后直接返回缓存值:
// filewithcountries.dart Map<String, Map<String, dynamic>> countries = { /* 原始数据 */ }; // 按大洲分组的衍生数据 Map<String, List<String>> _countriesByContinent; Map<String, List<String>> get countriesByContinent { if (_countriesByContinent == null) { _countriesByContinent = {}; countries.forEach((code, data) { String continent = data['continent']; _countriesByContinent.putIfAbsent(continent, () => []).add(code); }); } return _countriesByContinent; } // 按面积排序的衍生数据 List<String> _countriesBySize; List<String> get countriesBySize { if (_countriesBySize == null) { _countriesBySize = countries.keys.toList() ..sort((a, b) => countries[a]['size'].compareTo(countries[b]['size'])); } return _countriesBySize; }
使用时直接导入文件,访问countriesByContinent和countriesBySize即可,无需额外初始化,Dart会自动处理缓存。
2. 单例类封装(更规范的结构)
如果需要更清晰的代码结构和可扩展性,用单例类封装所有数据,在类初始化时一次性完成衍生数据的计算:
// CountryData.dart class CountryData { // 单例实例,确保全局唯一 static final CountryData _instance = CountryData._internal(); factory CountryData() => _instance; // 原始国家数据 final Map<String, Map<String, dynamic>> countries; // 衍生数据,初始化时计算完成 Map<String, List<String>> countriesByContinent; List<String> countriesBySize; // 私有构造函数:执行初始化逻辑 CountryData._internal() : countries = { /* 原始数据 */ } { _calculateDerivedData(); } // 计算所有衍生数据的方法 void _calculateDerivedData() { // 按大洲分组 countriesByContinent = {}; countries.forEach((code, data) { String continent = data['continent']; countriesByContinent.putIfAbsent(continent, () => []).add(code); }); // 按面积从小到大排序 countriesBySize = countries.keys.toList() ..sort((a, b) => countries[a]['size'].compareTo(countries[b]['size'])); } }
使用时,可以在App启动的main函数里提前初始化,避免首次访问的延迟:
// main.dart void main() { // 提前初始化单例,衍生数据会在此时计算完成 CountryData(); runApp(MyApp()); } // 在任意Widget中调用 final countryData = CountryData(); print(countryData.countriesByContinent['noam']); // 输出 ['abw','aia']
3. 异步预加载(适合大数据量场景)
如果原始数据量极大,或者需要从本地文件(如JSON)读取,用异步初始化避免阻塞UI:
// CountryData.dart class CountryData { static final CountryData _instance = CountryData._internal(); factory CountryData() => _instance; Map<String, Map<String, dynamic>> countries; Map<String, List<String>> countriesByContinent; List<String> countriesBySize; bool _isReady = false; CountryData._internal(); // 异步初始化方法 Future<void> prepareData() async { if (_isReady) return; // 这里可以替换为从本地文件读取原始数据的逻辑 countries = { /* 原始数据 */ }; // 计算衍生数据 _calculateDerivedData(); _isReady = true; } void _calculateDerivedData() { // 同单例类的计算逻辑 } }
在main函数中等待初始化完成再启动App:
void main() async { await CountryData().prepareData(); runApp(MyApp()); }
这种方式确保App启动后,所有衍生数据都已准备就绪,不会出现首次访问时的卡顿。
内容的提问来源于stack exchange,提问作者kreatywny
相关产品推荐
相关产品推荐

