Flutter中device_info_plus包的可空值应如何正确处理?
device_info_plus iOS端可空字段处理方案
前置说明
device_info_plus是原官方device_info包的社区继任维护版本,将IosDeviceInfo下的identifierForVendor、systemVersion标记为可空类型,本质是补全了旧包缺失的边界场景类型约束:正常运行的真机、模拟器环境下这两个字段99.9%概率会返回有效值,可空标记只是强制开发者覆盖极端异常场景(比如越狱环境篡改系统接口、设备处于未激活状态、企业管控环境限制接口读取权限),避免无感知的空值崩溃。
分字段落地处理方式
1. systemVersion(系统版本)
这个字段有稳定的系统级兜底方案,不需要担心拿不到有效值:
- 优先使用
device_info_plus返回的字段值 - 如果插件返回null,直接调用Dart自带
dart:io库的接口读取,该接口在iOS环境下100%返回非空值,和插件底层取的是同一个系统API,结果完全一致
import 'dart:io'; String getIosSystemVersion(IosDeviceInfo iosInfo) { // 优先使用插件返回值 if (iosInfo.systemVersion != null) { return iosInfo.systemVersion!; } // 系统接口兜底,无空值风险 return Platform.operatingSystemVersion; }
注意不要给这个字段写死类似
"0.0"、"未知"的默认值,会导致后端上报的系统版本数据失真,影响版本兼容策略判断。
2. identifierForVendor(设备唯一标识)
iOS系统本身就规定了这个字段的返回空场景:比如设备处于锁定状态未完全激活、设备抹除数据后未进入主界面时,系统接口本身就会返回null,处理时要结合持久化做兜底:
- 首次拿到有效值时,将值存入iOS钥匙串(用
flutter_secure_storage即可实现),钥匙串存储的内容不会随应用卸载重装丢失,生命周期和identifierForVendor基本一致 - 读取时优先读本地缓存,缓存不存在再读插件返回值,插件返回null就延迟1秒重试,重试仍失败则生成合规UUID存入钥匙串作为兜底,保证设备标识的唯一性和稳定性
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:uuid/uuid.dart'; final _secureStorage = const FlutterSecureStorage(); final _uuid = const Uuid(); Future<String> getIosVendorId() async { // 优先读本地持久化缓存 final cachedId = await _secureStorage.read(key: 'ios_vendor_id'); if (cachedId != null && cachedId.isNotEmpty) { return cachedId; } // 第一次读取插件值 final iosInfo = await DeviceInfoPlugin().iosInfo; if (iosInfo.identifierForVendor != null) { await _secureStorage.write(key: 'ios_vendor_id', value: iosInfo.identifierForVendor!); return iosInfo.identifierForVendor!; } // 空值场景延迟1秒重试,覆盖设备锁定等临时异常场景 await Future.delayed(const Duration(seconds: 1)); final retryInfo = await DeviceInfoPlugin().iosInfo; if (retryInfo.identifierForVendor != null) { await _secureStorage.write(key: 'ios_vendor_id', value: retryInfo.identifierForVendor!); return retryInfo.identifierForVendor!; } // 最终兜底:生成UUID存钥匙串 final fallbackId = _uuid.v4(); await _secureStorage.write(key: 'ios_vendor_id', value: fallbackId); return fallbackId; }
处理禁忌
- 不要在未做任何兜底的情况下直接用
!强制解包可空字段,极端场景下会直接触发应用闪退,影响比业务bug更严重 - 不要为了省事给
identifierForVendor传空字符串、固定默认值,会导致后端设备风控、用户唯一标识识别逻辑完全失效 - 不要因为字段变可空就回退到已停止维护的旧版
device_info包,后续iOS大版本更新不会有兼容维护,出问题排查成本更高
内容的提问来源于stack exchange,提问作者tomoya.nagasaki
相关产品推荐
相关产品推荐

