Flutter中initState调用双API致UI卡顿,求数据优化与本地存储方案
解决Flutter initState中API调用导致的UI卡顿与数据复用问题
问题描述
在Flutter的initState中调用两个获取后端数据的API时,UI出现卡顿,如何解决该卡顿问题?同时希望缩短接口数据获取耗时,且需在多页面复用这些数据,如何实现本地存储以避免重复调用API?
代码示例
@override void onInit() { getCustomerList(); getProductList(); super.onInit(); } Future<void> getCustomerList() async { List<CustomerModel> newItems = (await BaseClient.safeApiCall( ApiConstants.GET_CUSTOMER_LIST, RequestType.get, headers: await BaseClient.generateHeaders(), queryParameters: { '\$count': true, }, onSuccess: (json) { List<CustomerModel>? customers; if (json.data['value'] != null) { customers = []; json.data['value'].forEach((v) { customers?.add(CustomerModel.fromJson(v)); }); } return customers; }, onError: (e) {}, )); for (CustomerModel customer in newItems) { if (customer.companyName != null) { customerFilters.add(customer.customerName!); } } } Future<void> getProductList() async { List<ProductModel> newItems = (await BaseClient.safeApiCall( ApiConstants.GET_PRODUCT_LIST, RequestType.get, headers: await BaseClient.generateHeaders(), queryParameters: { '\$count': true, }, onSuccess: (json) { List<ProductModel>? products; if (json.data['value'] != null) { products = []; json.data['value'].forEach((v) { products?.add(ProductModel.fromJson(v)); }); } return products; }, onError: (e) {}, )); for (ProductModel products in newItems) { if (products.productName != null) { productFilters.add(products.productName!); } } }
一、解决UI卡顿问题
1. 并行发起API请求,减少总等待时间
显式用Future.wait管理并行任务,确保两个API同时发起,避免串行等待:
@override void onInit() { super.onInit(); // 并行执行两个API请求 Future.wait([ getCustomerList(), getProductList(), ]); }
2. 把数据解析、过滤移到后台线程
数据量较大时,fromJson转换和循环过滤会阻塞UI线程,用compute函数将这些计算任务移到后台isolate:
第一步:抽离计算逻辑为独立函数
// 客户列表过滤逻辑 List<String> filterCustomerNames(List<CustomerModel> customers) { return customers .where((customer) => customer.companyName != null) .map((customer) => customer.customerName!) .toList(); } // 客户列表JSON解析逻辑 List<CustomerModel> parseCustomerList(dynamic jsonData) { if (jsonData['value'] == null) return []; return (jsonData['value'] as List) .map((item) => CustomerModel.fromJson(item)) .toList(); } // 产品列表同理 List<String> filterProductNames(List<ProductModel> products) { return products .where((product) => product.productName != null) .map((product) => product.productName!) .toList(); } List<ProductModel> parseProductList(dynamic jsonData) { if (jsonData['value'] == null) return []; return (jsonData['value'] as List) .map((item) => ProductModel.fromJson(item)) .toList(); }
第二步:在API请求中使用compute执行后台任务
Future<void> getCustomerList() async { List<CustomerModel> newItems = (await BaseClient.safeApiCall( ApiConstants.GET_CUSTOMER_LIST, RequestType.get, headers: await BaseClient.generateHeaders(), queryParameters: { '\$count': true, }, onSuccess: (json) async { // JSON解析移到后台线程 return await compute(parseCustomerList, json.data); }, onError: (e) {}, )); // 过滤操作移到后台线程 final filteredNames = await compute(filterCustomerNames, newItems); // 回到主线程更新UI数据 customerFilters.addAll(filteredNames); } // getProductList做同样修改 Future<void> getProductList() async { List<ProductModel> newItems = (await BaseClient.safeApiCall( ApiConstants.GET_PRODUCT_LIST, RequestType.get, headers: await BaseClient.generateHeaders(), queryParameters: { '\$count': true, }, onSuccess: (json) async { return await compute(parseProductList, json.data); }, onError: (e) {}, )); final filteredNames = await compute(filterProductNames, newItems); productFilters.addAll(filteredNames); }
二、实现本地存储复用数据,避免重复调用API
用Hive做本地存储(性能优于SharedPreferences,支持直接存储对象),结合状态管理实现多页面数据复用:
1. 配置Hive依赖
在pubspec.yaml中添加:
dependencies: hive: ^2.2.3 hive_flutter: ^1.1.0 dev_dependencies: build_runner: ^2.4.4 hive_generator: ^1.1.5
2. 为模型类生成Hive适配器
给模型类添加注解,生成存储适配器:
import 'package:hive/hive.dart'; part 'customer_model.g.dart'; @HiveType(typeId: 0) class CustomerModel { @HiveField(0) final String? customerName; @HiveField(1) final String? companyName; CustomerModel({this.customerName, this.companyName}); factory CustomerModel.fromJson(Map<String, dynamic> json) { return CustomerModel( customerName: json['customerName'], companyName: json['companyName'], ); } } // ProductModel同理,typeId设为1
运行命令生成适配器:
flutter pub run build_runner build
3. 初始化Hive
在main函数中初始化并注册适配器:
import 'package:hive_flutter/hive_flutter.dart'; void main() async { await Hive.initFlutter(); Hive.registerAdapter(CustomerModelAdapter()); Hive.registerAdapter(ProductModelAdapter()); runApp(MyApp()); }
4. 封装缓存+网络请求逻辑
优先读取本地缓存,缓存过期或无缓存时再发起网络请求,同时更新缓存:
Future<void> getCustomerList() async { final customerBox = await Hive.openBox<CustomerModel>('customers'); final cacheTimeBox = await Hive.openBox('cache_timestamps'); // 缓存有效期24小时 const cacheExpiry = 24 * 60 * 60 * 1000; final lastFetchTime = cacheTimeBox.get('customer_list') ?? 0; final isCacheValid = DateTime.now().millisecondsSinceEpoch - lastFetchTime < cacheExpiry; // 先使用有效缓存更新UI if (customerBox.isNotEmpty && isCacheValid) { final cachedCustomers = customerBox.values.toList(); final filteredNames = await compute(filterCustomerNames, cachedCustomers); customerFilters.addAll(filteredNames); } // 发起网络请求获取最新数据 try { List<CustomerModel> newItems = (await BaseClient.safeApiCall( ApiConstants.GET_CUSTOMER_LIST, RequestType.get, headers: await BaseClient.generateHeaders(), queryParameters: { '\$count': true, }, onSuccess: (json) async { return await compute(parseCustomerList, json.data); }, onError: (e) {}, )); // 更新缓存和缓存时间 await customerBox.clear(); await customerBox.addAll(newItems); await cacheTimeBox.put('customer_list', DateTime.now().millisecondsSinceEpoch); // 用最新数据更新UI customerFilters.clear(); final filteredNames = await compute(filterCustomerNames, newItems); customerFilters.addAll(filteredNames); } catch (e) { // 无缓存且请求失败时处理错误(比如显示空状态) if (customerBox.isEmpty) { // 错误处理逻辑 } } } // getProductList做同样修改
5. 多页面数据复用
用Riverpod/Provider封装数据逻辑,多个页面直接监听状态即可:
以Riverpod为例:
final customerListProvider = FutureProvider<List<CustomerModel>>((ref) async { final customerBox = await Hive.openBox<CustomerModel>('customers'); final cacheTimeBox = await Hive.openBox('cache_timestamps'); const cacheExpiry = 24 * 60 * 60 * 1000; final lastFetchTime = cacheTimeBox.get('customer_list') ?? 0; final isCacheValid = DateTime.now().millisecondsSinceEpoch - lastFetchTime < cacheExpiry; if (customerBox.isNotEmpty && isCacheValid) { return customerBox.values.toList(); } final newItems = await BaseClient.safeApiCall( ApiConstants.GET_CUSTOMER_LIST, RequestType.get, headers: await BaseClient.generateHeaders(), queryParameters: { '\$count': true, }, onSuccess: (json) async { return await compute(parseCustomerList, json.data); }, onError: (e) {}, ); await customerBox.clear(); await customerBox.addAll(newItems); await cacheTimeBox.put('customer_list', DateTime.now().millisecondsSinceEpoch); return newItems; });
页面中使用:
final customerListAsync = ref.watch(customerListProvider); customerListAsync.when( loading: () => const CircularProgressIndicator(), error: (e, _) => const Text('加载失败'), data: (customers) { final filteredNames = customers.where((c) => c.companyName != null).map((c) => c.customerName!).toList(); customerFilters.addAll(filteredNames); return // 你的UI组件; }, );
内容的提问来源于stack exchange,提问作者Ahmed Raza
相关产品推荐
相关产品推荐

