flutter_for_openharmony城市井盖地图app实战+离线数据实现

1. 这个功能解决什么问题
离线数据管理是井盖地图APP的核心实用功能,主要解决以下场景的用户需求:
- 数据统计:精准展示本地缓存总大小、各类数据条目数量,让用户清晰知晓存储空间占用
- 缓存管理:按类型分类展示缓存占用,区分井盖数据、工单数据、图片缓存等维度
- 清理操作:提供安全的一键清理功能,附带二次确认机制避免误操作
- 模拟数据:开发阶段使用固定统计数据,便于功能调试和UI预览
这个页面是典型的"数据管理"场景,既满足用户对本地数据的掌控需求,也是ListTile + AlertDialog 组件组合的最佳实践示例。
2. 相关文件一览
lib/feature_pages.dart(OfflineDataPage):离线数据管理页面的核心实现文件,包含UI渲染、交互逻辑等全部内容
3. 数据统计展示
页面核心采用ListView作为基础布局容器,具备以下设计考量:
- 外层
ListView设置12px统一内边距,保证页面左右留白均衡 - 每个数据统计项使用
Card包裹,增加视觉层次感和点击反馈边界 ListTile组件分三部分布局:leading图标、title标题、trailing统计值
ListView(
padding: const EdgeInsets.all(12),
children: [
Card(
child: ListTile(
leading: const Icon(Icons.storage),
title: const Text('缓存大小'),
trailing: const Text('12.3 MB'),
),
),
针对不同数据类型的视觉区分设计:
- 缓存大小使用
Icons.storage存储图标,直观关联存储空间概念 - 井盖数据使用
Icons.list_alt列表图标,对应条目类数据展示 - 工单数据使用
Icons.assignment任务图标,贴合工单业务属性 - 图片缓存使用
Icons.photo_library图库图标,匹配图片类缓存特征
Card(
child: ListTile(
leading: const Icon(Icons.list_alt),
title: const Text('井盖数据'),
trailing: const Text('18 条'),
),
),
Card(
child: ListTile(
leading: const Icon(Icons.assignment),
title: const Text('工单数据'),
trailing: const Text('24 条'),
),
),
图片缓存项的实现细节说明:
- 图标选用与图片场景强关联的
Icons.photo_library - trailing文本采用"数值 + 单位"的统一格式,保持视觉一致性
- Card组件默认自带圆角和阴影,无需额外样式调整
Card(
child: ListTile(
leading: const Icon(Icons.photo_library),
title: const Text('图片缓存'),
trailing: const Text('8.7 MB'),
),
),
],
)
4. 清理操作
清理缓存功能的交互设计原则:
- 危险操作视觉强化:使用红色图标区分普通操作和危险操作
- 操作提示分层:标题说明功能,副标题补充操作后果
- 二次确认机制:点击后弹出对话框,避免用户误触
Card(
child: ListTile(
leading: const Icon(Icons.delete_sweep, color: Colors.red),
title: const Text('清理缓存'),
subtitle: const Text('删除所有离线数据'),
点击事件的核心逻辑设计:
- 调用
showDialog弹出确认对话框,上下文传递当前页面context - 对话框使用
AlertDialog组件,遵循Material Design规范 - 对话框内部分为标题、内容、操作按钮三部分,结构清晰
onTap: () {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text('确认清理'),
content: const Text('确定要清理所有离线数据吗?此操作不可恢复。'),
对话框按钮的交互逻辑设计:
- 取消按钮:仅关闭对话框,无其他副作用
- 确定按钮:先关闭对话框,再展示操作结果提示
- 使用
SnackBar反馈操作结果,符合移动端操作反馈习惯
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(content: Text('缓存已清理(模拟)')));
},
child: const Text('确定'),
),
],
),
);
},
),
)
5. 完整页面代码(工程真实内容)
页面类的基础结构设计:
- 采用
StatelessWidget,因页面数据为静态模拟数据 - 构造函数使用
super.key,遵循Flutter最佳实践 - 类名采用大驼峰命名法,符合Dart代码规范
class OfflineDataPage extends StatelessWidget {
const OfflineDataPage({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('离线数据')),
页面主体布局的层级设计:
- 外层
Scaffold提供基础页面结构(导航栏+内容区) - 内容区使用
ListView支持纵向滚动,适配小屏设备 - 统一设置12px内边距,保证所有子组件的间距一致性
body: ListView(
padding: const EdgeInsets.all(12),
children: [
Card(
child: ListTile(
leading: const Icon(Icons.storage),
title: const Text('缓存大小'),
trailing: const Text('12.3 MB'),
),
),
井盖数据项的实现要点:
ListTile的leading属性放置功能图标,增强视觉识别- title属性为数据类型名称,采用简洁文本
- trailing属性展示具体数值,与title形成左右对比布局
Card(
child: ListTile(
leading: const Icon(Icons.list_alt),
title: const Text('井盖数据'),
trailing: const Text('18 条'),
),
),
工单数据项的设计细节:
- 图标选择与工单业务场景匹配,提升用户认知效率
- 数值展示采用"数字 + 单位"格式,保持数据展示一致性
- Card组件自动适配父容器宽度,无需手动设置尺寸
Card(
child: ListTile(
leading: const Icon(Icons.assignment),
title: const Text('工单数据'),
trailing: const Text('24 条'),
),
),
图片缓存项的布局说明:
- 图标颜色使用系统默认主色,保持视觉统一性
- 文本使用默认样式,无需额外设置字体大小
- Card组件之间自动分隔,无需手动添加间距
Card(
child: ListTile(
leading: const Icon(Icons.photo_library),
title: const Text('图片缓存'),
trailing: const Text('8.7 MB'),
),
),
清理缓存项的间距设计:
- 使用
SizedBox设置12px垂直间距,区分数据统计区和操作区 - 间距值与页面整体内边距保持一致,保证布局节奏感
- 视觉上分隔不同功能区域,提升页面可读性
const SizedBox(height: 12),
Card(
child: ListTile(
leading: const Icon(Icons.delete_sweep, color: Colors.red),
title: const Text('清理缓存'),
清理缓存项的文本设计:
- 主标题明确操作名称,副标题补充操作影响
- 红色图标传递危险操作提示,符合用户操作习惯
- 文本与图标左对齐,保持ListTile组件的布局规范
subtitle: const Text('删除所有离线数据'),
onTap: () {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text('确认清理'),
确认对话框的内容设计:
- 标题使用"确认+操作名称"格式,清晰传达对话框用途
- 内容文本明确提示操作不可恢复,强化用户风险认知
- 文本行高使用系统默认值,保证可读性
content: const Text('确定要清理所有离线数据吗?此操作不可恢复。'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
确认按钮的逻辑设计:
- 先关闭对话框,再执行清理反馈
- 使用
ScaffoldMessenger展示操作结果,全局提示更醒目 - SnackBar文本明确标注"模拟",区分开发阶段和生产环境
TextButton(
onPressed: () {
Navigator.pop(context);
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(content: Text('缓存已清理(模拟)')));
},
child: const Text('确定'),
),
],
),
);
},
),
),
],
),
);
}
}
6. 离线数据模型
离线数据模型的设计思路:
- 采用面向对象思想,封装离线数据的所有核心属性
- 必选属性使用
required关键字,保证数据完整性 - 可选属性使用可空类型,适配不同数据场景
class OfflineData {
final String id;
final String name;
final String description;
final DataType dataType;
final double size;
final int count;
时间属性的设计考量:
createdAt为必选,记录数据首次缓存时间lastAccessed和lastModified为可选,记录数据使用状态- 使用
DateTime类型,便于时间计算和格式化展示
final DateTime createdAt;
final DateTime? lastAccessed;
final DateTime? lastModified;
final String? version;
final bool isEncrypted;
final DataStatus status;
final Map<String, dynamic> metadata;
构造函数的设计规范:
- 使用
const构造函数,提升不可变对象的性能 - 所有属性通过参数传入,保证数据封装性
- metadata默认值为空Map,简化实例化操作
const OfflineData({
required this.id,
required this.name,
required this.description,
required this.dataType,
required this.size,
required this.count,
required this.createdAt,
this.lastAccessed,
this.lastModified,
this.version,
required this.isEncrypted,
required this.status,
this.metadata = const {},
});
}
缓存信息模型的设计重点:
- 聚焦缓存管理场景,属性贴合缓存操作需求
cacheKey作为唯一标识,用于缓存增删改查displayName用于UI展示,区分技术标识和用户友好名称
class CacheInfo {
final String cacheKey;
final String displayName;
final CacheType cacheType;
final double totalSize;
final int itemCount;
缓存生命周期属性设计:
createdAt记录缓存创建时间lastUsed记录最后使用时间,用于缓存淘汰策略maxAge设置缓存最大存活时间,单位为秒
final DateTime createdAt;
final DateTime? lastUsed;
final bool isAutoCleanable;
final int maxAge;
final String? storagePath;
const CacheInfo({
required this.cacheKey,
required this.displayName,
required this.cacheType,
required this.totalSize,
required this.itemCount,
缓存信息构造函数:
- 必选属性强制传入,保证缓存基础信息完整
- 可选属性按需设置,适配不同缓存类型
- 无默认值的可选属性默认为null,符合Dart空安全规范
required this.createdAt,
this.lastUsed,
required this.isAutoCleanable,
required this.maxAge,
this.storagePath,
});
}
存储使用情况模型的设计目标:
- 统计设备整体存储状态,而非单个缓存项
- 包含总量、已用、可用等核心指标
- 提供计算属性,简化UI层的百分比计算
class StorageUsage {
final double totalSpace;
final double usedSpace;
final double freeSpace;
final double cacheSize;
final double dataSize;
final double tempSize;
存储统计的时间和分布属性:
calculatedAt记录统计时间,保证数据时效性dataDistribution按数据类型统计占用,便于分类展示- Map键为
DataType枚举,值为占用大小,类型安全
final DateTime calculatedAt;
final Map<DataType, double> dataDistribution;
const StorageUsage({
required this.totalSpace,
required this.usedSpace,
required this.freeSpace,
required this.cacheSize,
required this.dataSize,
存储模型构造函数:
- 所有属性强制传入,保证统计数据完整
- 无可选属性,避免统计数据缺失
- 构造函数参数顺序按重要性排列,提升可读性
required this.tempSize,
required this.calculatedAt,
required this.dataDistribution,
});
计算属性的实现逻辑:
usagePercentage计算整体使用率,取值范围0-1cachePercentage计算缓存占已用空间比例- 使用三元运算符处理分母为0的边界情况
double get usagePercentage => totalSpace > 0 ? usedSpace / totalSpace : 0.0;
double get cachePercentage => usedSpace > 0 ? cacheSize / usedSpace : 0.0;
}
数据类型枚举的设计:
- 按业务场景分类,覆盖APP核心数据类型
- 枚举值使用小写+下划线命名法,符合Dart规范
- 每个枚举值对应具体的业务数据,语义清晰
enum DataType {
manhole,
workOrder,
inspection,
report,
map,
user,
settings,
temp,
}
数据状态枚举的设计考量:
- 覆盖数据全生命周期状态
- 状态值简洁明确,便于业务逻辑判断
- 与后端数据状态保持一致,便于同步
enum DataStatus {
active,
archived,
corrupted,
syncing,
pending,
}
缓存类型枚举的设计:
- 按存储介质和类型分类
- 匹配实际缓存实现方式(内存、磁盘、数据库等)
- 便于后续按类型执行不同的清理策略
enum CacheType {
memory,
disk,
database,
image,
file,
}
同步状态枚举的设计:
- 覆盖数据同步的核心状态
- 包含成功、待同步、冲突、错误等场景
- 便于UI层展示同步状态和处理同步异常
enum SyncStatus {
synced,
pending,
conflict,
error,
}
7. 离线数据状态管理
状态管理类的基础设计:
- 继承
ChangeNotifier,实现数据变更通知 - 使用私有变量存储状态,通过getter暴露数据
- 所有状态变量初始值合理,避免空指针
class OfflineDataProvider extends ChangeNotifier {
List<OfflineData> _offlineData = [];
List<CacheInfo> _cacheInfo = [];
StorageUsage? _storageUsage;
bool _loading = false;
String? _error;
DateTime? _lastRefreshTime;
bool _isCleaning = false;
Getter方法的设计规范:
- 只读属性,无对应的setter方法
- 直接返回私有变量,保证数据封装性
- 命名简洁,与私有变量名称对应
List<OfflineData> get offlineData => _offlineData;
List<CacheInfo> get cacheInfo => _cacheInfo;
StorageUsage? get storageUsage => _storageUsage;
bool get loading => _loading;
String? get error => _error;
DateTime? get lastRefreshTime => _lastRefreshTime;
bool get isCleaning => _isCleaning;
数据加载方法的核心逻辑:
- 加载前设置loading状态,更新UI为加载中
- 清空错误信息,避免旧错误干扰
- 调用
notifyListeners通知UI更新
Future<void> loadOfflineData() async {
_loading = true;
_error = null;
notifyListeners();
try {
await Future.delayed(const Duration(seconds: 1));
模拟数据加载的实现:
- 使用
Future.delayed模拟网络/本地加载延迟 - 调用私有方法生成模拟数据,代码结构清晰
- 记录最后刷新时间,便于展示数据新鲜度
_offlineData = _generateMockOfflineData();
_cacheInfo = _generateMockCacheInfo();
_storageUsage = _calculateStorageUsage();
_lastRefreshTime = DateTime.now();
_loading = false;
notifyListeners();
异常处理的设计:
- catch块捕获所有异常,避免崩溃
- 记录错误信息,便于UI层展示
- 重置loading状态,保证UI恢复可操作
} catch (e) {
_error = e.toString();
_loading = false;
notifyListeners();
}
}
缓存清理方法的参数设计:
cleanAll布尔值控制是否清理全部缓存specificCaches列表指定要清理的缓存项- 参数具有默认值,简化调用
Future<void> cleanCache({bool cleanAll = false, List<String>? specificCaches}) async {
_isCleaning = true;
notifyListeners();
try {
await Future.delayed(const Duration(seconds: 2));
全量清理的逻辑实现:
- 清空缓存信息列表,重置缓存数据
- 更新存储使用信息,重置缓存大小为0
- 重新计算已用空间,排除缓存占用
if (cleanAll) {
_cacheInfo.clear();
_storageUsage = _storageUsage?.copyWith(
cacheSize: 0,
usedSpace: _storageUsage!.dataSize + _storageUsage!.tempSize,
);
指定缓存清理的逻辑:
- 根据cacheKey过滤要删除的缓存项
- 重新计算存储使用情况,保证数据一致性
- 仅删除指定项,保留其他缓存数据
} else if (specificCaches != null) {
_cacheInfo.removeWhere((cache) => specificCaches.contains(cache.cacheKey));
_storageUsage = _calculateStorageUsage();
}
_isCleaning = false;
notifyListeners();
清理操作的异常处理:
- 捕获清理过程中的异常,记录错误信息
- 重置清理状态,保证UI恢复正常
- 通知UI更新状态,展示清理结果
} catch (e) {
_error = e.toString();
_isCleaning = false;
notifyListeners();
}
}
数据刷新方法的设计:
- 简单封装,复用loadOfflineData逻辑
- 无额外参数,调用便捷
- 便于UI层绑定刷新按钮点击事件
Future<void> refreshData() async {
await loadOfflineData();
}
模拟离线数据生成方法:
- 使用固定随机种子,保证数据可复现
- 按业务场景生成井盖数据和工单数据
- 时间属性使用随机偏移,模拟真实使用场景
List<OfflineData> _generateMockOfflineData() {
final rng = Random(42);
return [
OfflineData(
id: 'data_1',
name: '井盖数据',
description: '所有井盖的基本信息和状态',
dataType: DataType.manhole,
size: 8.5,
井盖数据模拟的细节:
- 数量和大小设置符合真实业务场景
- 时间属性使用随机天数/小时数偏移
- 版本号模拟真实版本管理
count: 150,
createdAt: DateTime.now().subtract(Duration(days: rng.nextInt(30))),
lastAccessed: DateTime.now().subtract(Duration(hours: rng.nextInt(24))),
version: '1.2.0',
isEncrypted: false,
status: DataStatus.active,
元数据的设计:
- 使用Map存储扩展属性,灵活性高
- 包含同步状态等业务属性
- 时间属性存储为ISO8601字符串,便于序列化
metadata: {'synced': true, 'lastSync': DateTime.now().toIso8601String()},
),
OfflineData(
id: 'data_2',
name: '工单数据',
description: '所有工单的详细信息',
dataType: DataType.workOrder,
size: 12.3,
工单数据模拟的特点:
- 启用加密属性,模拟敏感数据
- 同步状态设置为未同步,模拟真实业务场景
- 元数据包含待同步数量,便于后续同步处理
count: 85,
createdAt: DateTime.now().subtract(Duration(days: rng.nextInt(30))),
lastAccessed: DateTime.now().subtract(Duration(hours: rng.nextInt(24))),
version: '1.1.5',
isEncrypted: true,
status: DataStatus.active,
metadata: {'synced': false, 'pendingSync': 12},
),
];
}
模拟缓存信息生成方法:
- 按缓存类型分类生成(图片、地图、数据库)
- 大小和数量设置符合不同缓存类型的特征
- 自动清理标识区分不同缓存策略
List<CacheInfo> _generateMockCacheInfo() {
final rng = Random(42);
return [
CacheInfo(
cacheKey: 'image_cache',
displayName: '图片缓存',
cacheType: CacheType.image,
totalSize: 45.6,
itemCount: 234,
图片缓存的特征设置:
- 数量多、单条小,符合图片缓存特点
- 启用自动清理,设置7天最大存活时间
- 指定存储路径,便于定位缓存文件
createdAt: DateTime.now().subtract(Duration(days: rng.nextInt(30))),
lastUsed: DateTime.now().subtract(Duration(hours: rng.nextInt(24))),
isAutoCleanable: true,
maxAge: 7 * 24 * 60 * 60, // 7 days
storagePath: '/data/cache/images',
),
CacheInfo(
cacheKey: 'map_cache',
displayName: '地图缓存',
cacheType: CacheType.file,
totalSize: 28.3,
地图缓存的设计特点:
- 数量少、单条大,符合地图瓦片特征
- 禁用自动清理,重要性高于图片缓存
- 最大存活时间设置为30天,适配地图数据更新频率
itemCount: 45,
createdAt: DateTime.now().subtract(Duration(days: rng.nextInt(30))),
lastUsed: DateTime.now().subtract(Duration(hours: rng.nextInt(24))),
isAutoCleanable: false,
maxAge: 30 * 24 * 60 * 60, // 30 days
storagePath: '/data/cache/maps',
),
CacheInfo(
cacheKey: 'database_cache',
displayName: '数据库缓存',
cacheType: CacheType.database,
数据库缓存的参数设置:
- 条目数量大,符合数据库缓存特征
- 启用自动清理,存活时间短(3天)
- 存储路径指向数据库缓存目录
totalSize: 15.8,
itemCount: 1200,
createdAt: DateTime.now().subtract(Duration(days: rng.nextInt(30))),
lastUsed: DateTime.now().subtract(Duration(hours: rng.nextInt(24))),
isAutoCleanable: true,
maxAge: 3 * 24 * 60 * 60, // 3 days
storagePath: '/data/cache/database',
),
];
}
存储使用情况计算方法:
- 总空间固定为1GB,模拟移动设备存储
- 数据大小通过折叠离线数据列表计算
- 缓存大小通过折叠缓存信息列表计算
StorageUsage _calculateStorageUsage() {
final totalSpace = 1024.0; // 1GB
final dataSize = _offlineData.fold(0.0, (sum, data) => sum + data.size);
final cacheSize = _cacheInfo.fold(0.0, (sum, cache) => sum + cache.totalSize);
final tempSize = 5.2; // 临时文件大小
final usedSpace = dataSize + cacheSize + tempSize;
存储使用模型实例化:
- 计算可用空间(总空间-已用空间)
- 记录计算时间,保证数据时效性
- 构建数据分布Map,按类型统计大小
return StorageUsage(
totalSpace: totalSpace,
usedSpace: usedSpace,
freeSpace: totalSpace - usedSpace,
cacheSize: cacheSize,
dataSize: dataSize,
tempSize: tempSize,
calculatedAt: DateTime.now(),
dataDistribution: {
for (final data in _offlineData) data.dataType: data.size,
},
);
}
}
8. 高级离线数据组件
高级组件的基础结构:
- 继承
StatefulWidget,支持生命周期管理 - 类名使用大驼峰命名,后缀Widget明确组件类型
- 构造函数使用super.key,遵循Flutter规范
class AdvancedOfflineDataWidget extends StatefulWidget {
const AdvancedOfflineDataWidget({super.key});
State<AdvancedOfflineDataWidget> createState() => _AdvancedOfflineDataWidgetState();
}
class _AdvancedOfflineDataWidgetState extends State<AdvancedOfflineDataWidget> {
初始化方法的设计:
- 在
initState中添加帧回调,保证UI构建完成后执行 - 获取Provider实例,设置
listen: false避免不必要重建 - 调用数据加载方法,初始化页面数据
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
final provider = Provider.of<OfflineDataProvider>(context, listen: false);
provider.loadOfflineData();
});
}
构建方法的核心逻辑:
- 使用
Consumer监听Provider变化,自动重建UI - 根据loading状态展示加载指示器或内容
- 导航栏添加清理和刷新按钮,提升操作便捷性
Widget build(BuildContext context) {
return Consumer<OfflineDataProvider>(
builder: (context, provider, child) {
return Scaffold(
appBar: AppBar(
title: const Text('离线数据'),
actions: [
IconButton(
onPressed: () => _showCleanDialog(context, provider),
icon: const Icon(Icons.cleaning_services),
),
IconButton(
onPressed: provider.refreshData,
icon: const Icon(Icons.refresh),
),
],
),
加载状态的UI处理:
- loading为true时展示圆形加载指示器
- 居中布局,符合移动端加载状态设计规范
- loading为false时展示核心内容
body: provider.loading
? const Center(child: CircularProgressIndicator())
: _buildContent(context, provider),
);
},
);
}
内容构建方法的设计:
- 使用
RefreshIndicator支持下拉刷新 - 垂直布局分为存储卡片、数据列表、缓存列表三部分
- 各部分之间设置8px间距,保证视觉呼吸感
Widget _buildContent(BuildContext context, OfflineDataProvider provider) {
return RefreshIndicator(
onRefresh: provider.refreshData,
child: Column(
children: [
_buildStorageCard(context, provider),
const SizedBox(height: 8),
_buildDataList(context, provider),
const SizedBox(height: 8),
_buildCacheList(context, provider),
],
),
);
}
存储卡片的构建逻辑:
- 空值判断,避免storage为null时崩溃
- 使用Card组件包裹,提升视觉层次感
- 16px内边距,保证内容不贴边
Widget _buildStorageCard(BuildContext context, OfflineDataProvider provider) {
final storage = provider.storageUsage;
if (storage == null) return const SizedBox.shrink();
return Card(
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
存储卡片的标题设计:
- 使用主题文本样式,保证与系统风格一致
- 设置加粗字体,突出标题层级
- 标题文本简洁明确,传达卡片用途
Text(
'存储使用情况',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
进度条的属性设置:
- 高度12px,圆角6px,符合现代设计风格
- 进度值限制在0-1之间,避免异常显示
- 背景色使用浅灰色,保证进度条可见
LinearPercentIndicator(
lineHeight: 12,
percent: storage.usagePercentage.clamp(0.0, 1.0),
progressColor: _getStorageColor(storage.usagePercentage),
backgroundColor: Colors.grey.shade200,
barRadius: const Radius.circular(6),
),
const SizedBox(height: 8),
存储统计文本的布局:
- 左右两端对齐,展示不同维度信息
- 左侧展示已用/总空间,右侧展示使用率
- 小号字体(12px),作为辅助信息
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${storage.usedSpace.toStringAsFixed(1)} MB / ${storage.totalSpace.toStringAsFixed(1)} MB',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 12,
),
),
Text(
'${(storage.usagePercentage * 100).toStringAsFixed(1)}%',
style: TextStyle(
color: _getStorageColor(storage.usagePercentage),
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
],
),
],
),
),
);
}
}
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)