Flutter for OpenHarmony移动数据使用监管助手App实战 - 应用详情实现
用户在应用列表中点击某个App后,会进入应用详情页面。这个页面展示单个应用的详细流量使用情况,包括WiFi和移动数据的分别统计、近期使用趋势图、以及后台流量控制开关。
页面设计思路
应用详情页面要回答用户几个核心问题:
- 这个App总共用了多少流量?
- WiFi和移动数据各占多少?
- 最近几天的使用趋势是什么样的?
- 能不能限制它的后台流量?
数据传递方式
从应用列表跳转到详情页时,需要传递应用信息:
// 列表页跳转时传参
Get.toNamed(Routes.APP_DETAIL, arguments: appUsage);
// 详情页接收参数
class AppDetailController extends GetxController {
final app = Rx<AppUsage?>(null);
Get.toNamed跳转并传递参数。
arguments可以传递任意类型的对象。
Rx<AppUsage?>存储可为null的应用数据。
void onInit() {
super.onInit();
if (Get.arguments != null) {
app.value = Get.arguments as AppUsage;
}
loadWeeklyData();
}
}
onInit在控制器初始化时调用。
Get.arguments获取传递的参数。
loadWeeklyData加载近7天的数据。
页面整体结构
首先定义应用详情页面的基本框架:
class AppDetailView extends GetView<AppDetailController> {
const AppDetailView({super.key});
Widget build(BuildContext context) {
return Scaffold(
继承GetView自动注入AppDetailController控制器。
const构造函数优化widget重建性能。
build方法返回页面的完整UI结构。
backgroundColor: AppTheme.backgroundColor,
appBar: AppBar(
title: Obx(() => Text(controller.app.value?.appName ?? '应用详情')),
actions: [
IconButton(
Scaffold提供Material Design页面框架。
Obx监听app变化动态显示应用名称。
AppBar右侧放置更多操作按钮。
icon: Icon(Icons.more_vert),
onPressed: () => _showMoreOptions(),
),
],
),
body: Obx(() => controller.app.value == null
? const Center(child: Text('无数据'))
more_vert图标表示更多操作。
Obx监听app状态显示不同内容。
app为null时显示无数据提示。
: SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Column(
children: [
_buildAppHeader(),
SingleChildScrollView让内容可滚动。
Column垂直排列四个区域。
_buildAppHeader构建应用信息头部。
SizedBox(height: 16.h),
_buildUsageCard(),
SizedBox(height: 16.h),
_buildChart(),
SizedBox(height: 16.h),
_buildSettings(),
_buildUsageCard构建流量统计卡片。
_buildChart构建趋势图表。
_buildSettings构建设置选项。
],
),
)),
);
}
}
16.h间距让各区域视觉分隔清晰。
闭合所有括号完成页面结构。
整体布局清晰直观。
应用信息头部
展示应用图标、名称和包名:
Widget _buildAppHeader() {
final app = controller.app.value!;
return Container(
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
color: Colors.white,
获取当前应用数据。
Container作为头部的容器。
20.w内边距让内容更宽松。
borderRadius: BorderRadius.circular(16.r),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.03),
blurRadius: 10.r,
offset: Offset(0, 4.h),
),
],
16.r圆角保持视觉一致。
轻微阴影让卡片有悬浮感。
透明度0.03的阴影非常柔和。
),
child: Row(
children: [
Container(
width: 64.w,
height: 64.w,
decoration: BoxDecoration(
Row横向排列图标和信息。
图标容器尺寸64.w比列表项更大。
BoxDecoration定义容器样式。
color: AppTheme.primaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(16.r),
),
child: Icon(Icons.apps, size: 36.sp, color: AppTheme.primaryColor),
),
SizedBox(width: 16.w),
浅色背景让图标区域柔和。
16.r圆角让容器更圆润。
间距16.w让图标和信息不挤。
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
app.appName,
style: TextStyle(
Expanded让信息区域占据剩余空间。
Column垂直排列名称、包名、最后使用时间。
crossAxisAlignment让内容左对齐。
fontSize: 20.sp,
fontWeight: FontWeight.bold,
color: AppTheme.textPrimary,
),
),
SizedBox(height: 6.h),
应用名称用20.sp大字号加粗。
主要文字颜色确保可读性。
间距6.h后显示包名。
Text(
app.packageName,
style: TextStyle(fontSize: 12.sp, color: AppTheme.textSecondary),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
包名用12.sp小字号。
maxLines: 1限制单行显示。
overflow处理文字溢出。
SizedBox(height: 4.h),
Text(
'最后使用: ${_formatLastUsed(app.lastUsed)}',
style: TextStyle(fontSize: 11.sp, color: AppTheme.textSecondary),
),
],
),
),
],
),
);
}
显示最后使用时间。
_formatLastUsed格式化为相对时间。
整体设计信息层次分明。
格式化最后使用时间
将时间转换为相对时间显示:
String _formatLastUsed(DateTime dateTime) {
final now = DateTime.now();
final diff = now.difference(dateTime);
if (diff.inMinutes < 60) return '${diff.inMinutes}分钟前';
计算当前时间与最后使用时间的差值。
小于60分钟显示"X分钟前"。
相对时间比绝对时间更直观。
if (diff.inHours < 24) return '${diff.inHours}小时前';
return '${diff.inDays}天前';
}
小于24小时显示"X小时前"。
否则显示"X天前"。
简洁的时间格式化逻辑。
流量统计卡片
展示WiFi、移动数据和总计三个数据:
Widget _buildUsageCard() {
final app = controller.app.value!;
return Container(
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
),
获取当前应用数据。
Container作为统计卡片的容器。
白色背景与页面灰色背景对比。
child: Column(
children: [
Row(
children: [
Expanded(
child: _buildUsageItem('WiFi', app.formattedWifi, AppTheme.wifiColor, Icons.wifi),
),
Column垂直排列分项和总计。
Row横向排列WiFi和移动数据。
_buildUsageItem构建单个统计项。
Container(width: 1, height: 60.h, color: Colors.grey.shade200),
Expanded(
child: _buildUsageItem('移动数据', app.formattedMobile, AppTheme.mobileColor, Icons.signal_cellular_alt),
),
],
),
竖线分隔WiFi和移动数据。
60.h的高度适中。
Expanded让两项平分宽度。
SizedBox(height: 16.h),
Divider(height: 1),
SizedBox(height: 16.h),
_buildTotalUsage(app),
],
),
);
}
间距16.h后显示分隔线。
Divider水平分隔分项和总计。
_buildTotalUsage构建总计显示。
统计项组件
单个流量统计项的显示:
Widget _buildUsageItem(String label, String value, Color color, IconData icon) {
return Column(
children: [
Container(
width: 44.w,
height: 44.w,
接收标签、数值、颜色、图标四个参数。
Column垂直排列图标、标签、数值。
图标容器尺寸44.w。
decoration: BoxDecoration(
color: color.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(icon, color: color, size: 24.sp),
),
SizedBox(height: 10.h),
浅色背景让图标区域柔和。
BoxShape.circle创建圆形容器。
间距10.h后显示标签。
Text(label, style: TextStyle(fontSize: 13.sp, color: AppTheme.textSecondary)),
SizedBox(height: 4.h),
Text(
value,
style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: color),
),
],
);
}
标签用13.sp小字号。
数值用18.sp大字号加粗。
颜色参数让不同项有不同颜色。
总计显示
显示总流量:
Widget _buildTotalUsage(AppUsage app) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('总计使用 ', style: TextStyle(fontSize: 14.sp, color: AppTheme.textSecondary)),
Row居中排列标签和数值。
标签"总计使用"用14.sp字号。
次要颜色作为说明文字。
Text(
app.formattedTotal,
style: TextStyle(fontSize: 24.sp, fontWeight: FontWeight.bold, color: AppTheme.primaryColor),
),
],
);
}
总流量用24.sp超大字号加粗。
主色调突出显示总流量。
整体设计让总流量最醒目。
趋势图表
用折线图展示近7天的流量趋势:
Widget _buildChart() {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
),
Container作为图表的容器。
白色背景与页面灰色背景对比。
16.r圆角保持视觉一致。
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'近7天使用趋势',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600, color: AppTheme.textPrimary),
),
Column垂直排列标题和图表。
标题用16.sp字号,w600加粗。
crossAxisAlignment让标题左对齐。
SizedBox(height: 20.h),
SizedBox(
height: 160.h,
child: Obx(() => LineChart(
LineChartData(
间距20.h后显示图表。
固定高度160.h的图表容器。
LineChart是fl_chart库的折线图组件。
gridData: FlGridData(
show: true,
drawVerticalLine: false,
horizontalInterval: 0.5,
getDrawingHorizontalLine: (value) => FlLine(color: Colors.grey.shade200, strokeWidth: 1),
),
gridData配置网格线。
只显示水平网格线。
horizontalInterval设置网格间隔。
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 40.w,
titlesData配置坐标轴标签。
leftTitles配置左侧Y轴标签。
reservedSize预留标签空间。
getTitlesWidget: (value, meta) => Text(
'${value.toStringAsFixed(1)}G',
style: TextStyle(fontSize: 10.sp, color: AppTheme.textSecondary),
),
),
),
显示流量数值,单位G。
10.sp小字号适合坐标轴标签。
次要颜色让标签不会太突出。
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) {
final days = ['一', '二', '三', '四', '五', '六', '日'];
bottomTitles配置底部X轴标签。
days数组存储星期的中文简写。
getTitlesWidget返回标签组件。
final index = value.toInt();
if (index >= 0 && index < days.length) {
return Text(days[index], style: TextStyle(fontSize: 10.sp, color: AppTheme.textSecondary));
}
return const Text('');
},
根据索引获取星期文字。
边界检查避免数组越界。
无效索引返回空文字。
),
),
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
),
隐藏顶部和右侧标签。
让图表更简洁。
只显示必要的坐标轴信息。
borderData: FlBorderData(show: false),
lineBarsData: [
LineChartBarData(
spots: List.generate(
controller.weeklyData.length,
(i) => FlSpot(i.toDouble(), controller.weeklyData[i]),
),
隐藏边框让图表更简洁。
lineBarsData定义折线数据。
List.generate生成数据点。
isCurved: true,
curveSmoothness: 0.3,
color: AppTheme.primaryColor,
barWidth: 3,
isCurved启用曲线。
curveSmoothness控制曲线平滑度。
barWidth设置线条宽度。
dotData: FlDotData(
show: true,
getDotPainter: (spot, percent, barData, index) => FlDotCirclePainter(
radius: 4,
color: Colors.white,
strokeWidth: 2,
strokeColor: AppTheme.primaryColor,
),
),
dotData配置数据点样式。
空心圆点,白色填充加主色描边。
radius: 4设置点的大小。
belowBarData: BarAreaData(
show: true,
color: AppTheme.primaryColor.withOpacity(0.1),
),
),
],
minY: 0,
),
)),
),
],
),
);
}
belowBarData配置曲线下方填充。
浅色填充增加视觉层次感。
minY: 0确保Y轴从0开始。
设置选项
提供后台流量控制等设置:
Widget _buildSettings() {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
),
child: Column(
Container作为设置区域的容器。
白色背景与页面灰色背景对比。
Column垂直排列设置项。
children: [
Obx(() => SwitchListTile(
title: Text('允许后台流量', style: TextStyle(fontSize: 15.sp, color: AppTheme.textPrimary)),
subtitle: Text('关闭后该应用将无法在后台使用流量', style: TextStyle(fontSize: 12.sp, color: AppTheme.textSecondary)),
SwitchListTile是带开关的列表项。
title显示设置项名称。
subtitle显示说明文字。
value: controller.backgroundDataEnabled.value,
onChanged: (v) => controller.toggleBackgroundData(),
activeColor: AppTheme.primaryColor,
)),
Divider(height: 1, indent: 16.w, endIndent: 16.w),
value绑定开关状态。
onChanged切换后台流量权限。
Divider分隔各设置项。
ListTile(
title: Text('设置流量限额', style: TextStyle(fontSize: 15.sp, color: AppTheme.textPrimary)),
subtitle: Text('限制该应用的每日流量使用', style: TextStyle(fontSize: 12.sp, color: AppTheme.textSecondary)),
trailing: Icon(Icons.chevron_right, color: AppTheme.textSecondary),
ListTile是普通列表项。
trailing显示右箭头表示可点击。
点击弹出限额设置对话框。
onTap: () => _showLimitDialog(),
),
Divider(height: 1, indent: 16.w, endIndent: 16.w),
ListTile(
title: Text('清除流量统计', style: TextStyle(fontSize: 15.sp, color: Colors.red)),
onTap: () => _showClearConfirmDialog(),
),
],
),
);
}
清除统计用红色文字提示危险操作。
点击需要二次确认。
闭合Column和Container完成设置区域。
Controller实现
控制器管理应用详情的状态:
class AppDetailController extends GetxController {
final app = Rx<AppUsage?>(null);
final weeklyData = <double>[].obs;
final backgroundDataEnabled = true.obs;
final usageLimit = 0.0.obs;
app存储当前应用数据。
weeklyData存储近7天的流量数据。
backgroundDataEnabled控制后台流量开关。
void onInit() {
super.onInit();
if (Get.arguments != null) {
app.value = Get.arguments as AppUsage;
loadSettings();
}
loadWeeklyData();
}
onInit初始化时获取传递的参数。
loadSettings加载该应用的设置。
loadWeeklyData加载近7天数据。
void loadWeeklyData() {
weeklyData.value = [0.5, 0.8, 0.6, 1.2, 0.9, 0.7, 1.0];
}
void toggleBackgroundData() {
backgroundDataEnabled.value = !backgroundDataEnabled.value;
saveSettings();
}
}
模拟近7天的流量数据。
toggleBackgroundData切换后台流量状态。
saveSettings保存设置到本地。
写在最后
应用详情页面让用户深入了解单个应用的流量使用情况。通过清晰的数据展示、直观的趋势图表、便捷的控制选项,帮助用户管理应用的流量消耗。
可以继续优化的方向:
- 支持查看更长时间范围的数据
- 添加流量使用预测功能
- 支持设置多个提醒阈值
- 对比不同时间段的使用情况
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)