应用列表页面展示设备上各个应用的流量使用情况,帮助用户找出"流量大户"。这个页面涉及到饼图展示、列表排序、筛选功能等,是流量监控App中交互比较丰富的一个模块。
请添加图片描述

页面布局规划

应用列表页面分为三个区域:

  • 顶部统计区:饼图展示流量占比,总流量数字,系统应用开关
  • 排序选项栏:按总流量、WiFi、移动数据排序
  • 应用列表:展示每个应用的详细流量信息

顶部统计区实现

用饼图直观展示各应用的流量占比:

Widget _buildHeader() {
  return Container(
    margin: EdgeInsets.all(16.w),
    padding: EdgeInsets.all(16.w),
    decoration: BoxDecoration(
      color: Colors.white,

Container作为统计区的容器。
margin和padding设置外边距和内边距。
白色背景与页面灰色背景对比。

      borderRadius: BorderRadius.circular(16.r),
    ),
    child: Row(
      children: [
        Expanded(
          child: Obx(() => SizedBox(

16.r圆角保持视觉一致。
Row横向排列饼图和统计信息。
Expanded让饼图占据一半空间。

            height: 120.h,
            child: PieChart(
              PieChartData(
                sectionsSpace: 2,
                centerSpaceRadius: 30.r,
                sections: _buildPieSections(),

固定高度120.h的饼图容器。
PieChart是fl_chart库的饼图组件。
sectionsSpace设置扇区之间的间隙。

              ),
            ),
          )),
        ),
        SizedBox(width: 16.w),
        Expanded(
          child: Column(

centerSpaceRadius设置中心空白半径。
间距16.w让饼图和统计信息不挤。
右侧Column显示统计信息。

            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text('应用总流量', style: TextStyle(fontSize: 12.sp, color: AppTheme.textSecondary)),
              SizedBox(height: 4.h),
              Obx(() => Text(

crossAxisAlignment让内容左对齐。
标签"应用总流量"用12.sp小字号。
Obx监听totalUsage实现响应式更新。

                controller.formatBytes(controller.totalUsage),
                style: TextStyle(fontSize: 20.sp, fontWeight: FontWeight.bold, color: AppTheme.textPrimary),
              )),
              SizedBox(height: 12.h),
              _buildSystemAppToggle(),

formatBytes格式化字节数为可读字符串。
20.sp大字号突出显示总流量。
_buildSystemAppToggle构建系统应用开关。

            ],
          ),
        ),
      ],
    ),
  );
}

闭合Column、Expanded、Row、Container。
左边饼图右边统计信息的布局。
整体设计直观清晰。

饼图扇区生成

生成饼图的各个扇区:

List<PieChartSectionData> _buildPieSections() {
  final colors = [
    AppTheme.primaryColor,
    AppTheme.secondaryColor,
    AppTheme.wifiColor,
    AppTheme.mobileColor,

返回PieChartSectionData列表。
colors数组定义扇区颜色。
使用App主题色保持视觉一致。

    Colors.purple,
    Colors.pink,
    Colors.teal,
  ];

  final apps = controller.filteredList.take(5).toList();
  final total = controller.totalUsage;

紫色、粉色、青色作为补充颜色。
只取前5个应用显示在饼图上。
total是所有应用的总流量。

  return List.generate(apps.length, (index) {
    final percentage = total > 0 ? (apps[index].totalUsage / total) * 100 : 0;
    return PieChartSectionData(
      color: colors[index % colors.length],

List.generate生成扇区数据。
计算每个应用的流量占比。
颜色循环使用确保不重复。

      value: percentage.toDouble(),
      title: '${percentage.toStringAsFixed(0)}%',
      radius: 35.r,
      titleStyle: TextStyle(fontSize: 10.sp, fontWeight: FontWeight.bold, color: Colors.white),
    );
  });
}

value是扇区的数值。
title显示百分比文字。
radius设置扇区半径。

系统应用开关

控制是否显示系统应用:

Widget _buildSystemAppToggle() {
  return Row(
    children: [
      Obx(() => Checkbox(
        value: controller.showSystemApps.value,
        onChanged: (_) => controller.toggleSystemApps(),

Row横向排列复选框和文字。
Obx监听showSystemApps状态。
onChanged切换显示状态。

        activeColor: AppTheme.primaryColor,
      )),
      Text('显示系统应用', style: TextStyle(fontSize: 12.sp)),
    ],
  );
}

主色调作为复选框激活颜色。
12.sp小字号适合辅助选项。
默认隐藏系统应用。

排序选项栏

三个排序选项用胶囊按钮展示:

Widget _buildSortOptions() {
  final options = ['总流量', 'WiFi', '移动数据'];
  return Container(
    height: 40.h,
    margin: EdgeInsets.symmetric(horizontal: 16.w),

options数组定义排序选项。
Container作为选项栏的容器。
固定高度40.h。

    child: Row(
      children: List.generate(options.length, (index) {
        return Padding(
          padding: EdgeInsets.only(right: 8.w),
          child: GestureDetector(

Row横向排列选项按钮。
List.generate生成选项按钮。
Padding添加右侧间距。

            onTap: () => controller.changeSortType(index),
            child: Obx(() => Container(
              padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
              decoration: BoxDecoration(

GestureDetector处理点击事件。
Obx监听sortType实现响应式更新。
padding设置按钮内边距。

                color: controller.sortType.value == index 
                    ? AppTheme.primaryColor 
                    : Colors.white,
                borderRadius: BorderRadius.circular(20.r),
              ),

选中时用主色背景。
未选中时用白色背景。
20.r圆角形成胶囊形状。

              child: Text(
                options[index],
                style: TextStyle(
                  fontSize: 12.sp,
                  color: controller.sortType.value == index 
                      ? Colors.white 
                      : AppTheme.textSecondary,
                ),
              ),
            )),
          ),
        );
      }),
    ),
  );
}

选中时白色文字,未选中时灰色文字。
12.sp字号适合按钮文字。
整体设计简洁直观。

应用列表实现

展示所有应用的流量信息:

Widget _buildAppList() {
  return Obx(() => ListView.builder(
    padding: EdgeInsets.all(16.w),
    itemCount: controller.filteredList.length,
    itemBuilder: (context, index) {

Obx监听filteredList实现响应式更新。
ListView.builder懒加载列表项。
padding设置列表内边距。

      final app = controller.filteredList[index];
      return GestureDetector(
        onTap: () => Get.toNamed(Routes.APP_DETAIL, arguments: app),
        child: Container(

获取当前索引的应用数据。
GestureDetector处理点击跳转详情。
arguments传递应用数据。

          margin: EdgeInsets.only(bottom: 8.h),
          padding: EdgeInsets.all(12.w),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(12.r),
          ),

底部margin让列表项之间有间距。
白色背景与页面灰色背景对比。
12.r圆角保持视觉一致。

          child: Row(
            children: [
              _buildAppIcon(app),
              SizedBox(width: 12.w),
              Expanded(child: _buildAppInfo(app)),

Row横向排列图标、信息、流量。
_buildAppIcon构建应用图标。
Expanded让信息区域占据剩余空间。

              _buildUsageInfo(app),
              SizedBox(width: 8.w),
              Icon(Icons.chevron_right, color: AppTheme.textSecondary, size: 20.sp),
            ],
          ),
        ),
      );
    },
  ));
}

_buildUsageInfo构建流量信息。
右箭头表示可点击查看详情。
闭合ListView完成列表构建。

应用图标

根据应用类型显示不同图标:

Widget _buildAppIcon(AppUsage app) {
  return Container(
    width: 44.w,
    height: 44.w,
    decoration: BoxDecoration(
      color: AppTheme.primaryColor.withOpacity(0.1),

Container作为图标容器。
44.w的尺寸适合列表项。
浅色背景让图标区域柔和。

      borderRadius: BorderRadius.circular(10.r),
    ),
    child: Icon(
      app.isSystemApp ? Icons.android : Icons.apps,
      color: AppTheme.primaryColor,
      size: 24.sp,
    ),
  );
}

系统应用用android图标。
普通应用用apps图标。
主色调图标与App风格一致。

应用信息区域

显示应用名称和分项流量:

Widget _buildAppInfo(AppUsage app) {
  return Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Row(
        children: [
          Text(app.appName, style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w600)),

Column垂直排列名称和流量。
crossAxisAlignment让内容左对齐。
应用名称用14.sp字号,w600加粗。

          if (app.isSystemApp) ...[
            SizedBox(width: 6.w),
            Container(
              padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h),
              decoration: BoxDecoration(

系统应用显示"系统"标签。
展开运算符…条件添加组件。
小内边距让标签紧凑。

                color: Colors.grey.shade200,
                borderRadius: BorderRadius.circular(4.r),
              ),
              child: Text('系统', style: TextStyle(fontSize: 10.sp, color: AppTheme.textSecondary)),
            ),
          ],
        ],
      ),

灰色背景区分系统应用。
4.r小圆角让标签更精致。
10.sp小字号适合标签。

      SizedBox(height: 4.h),
      Row(
        children: [
          Icon(Icons.wifi, size: 12.sp, color: AppTheme.wifiColor),
          SizedBox(width: 4.w),

间距4.h后显示分项流量。
Row横向排列WiFi和移动数据。
WiFi图标用绿色。

          Text(app.formattedWifi, style: TextStyle(fontSize: 11.sp, color: AppTheme.textSecondary)),
          SizedBox(width: 12.w),
          Icon(Icons.signal_cellular_alt, size: 12.sp, color: AppTheme.mobileColor),

显示WiFi流量数值。
间距12.w分隔WiFi和移动数据。
移动数据图标用橙色。

          SizedBox(width: 4.w),
          Text(app.formattedMobile, style: TextStyle(fontSize: 11.sp, color: AppTheme.textSecondary)),
        ],
      ),
    ],
  );
}

显示移动数据流量数值。
11.sp小字号作为辅助信息。
整体设计信息层次分明。

流量信息区域

显示总流量和占比:

Widget _buildUsageInfo(AppUsage app) {
  return Column(
    crossAxisAlignment: CrossAxisAlignment.end,
    children: [
      Text(
        app.formattedTotal, 
        style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.bold, color: AppTheme.primaryColor),
      ),

Column垂直排列总流量和占比。
crossAxisAlignment让内容右对齐。
总流量用14.sp加粗,主色调。

      SizedBox(height: 4.h),
      Text(
        '${((app.totalUsage / controller.totalUsage) * 100).toStringAsFixed(1)}%',
        style: TextStyle(fontSize: 11.sp, color: AppTheme.textSecondary),
      ),
    ],
  );
}

间距4.h后显示占比百分比。
计算该应用占总流量的百分比。
11.sp小字号作为辅助信息。

Controller层实现

控制器管理应用列表的状态和逻辑:

class AppsController extends GetxController {
  final isLoading = false.obs;
  final sortType = 0.obs;
  final showSystemApps = false.obs;
  final appList = <AppUsage>[].obs;
  final filteredList = <AppUsage>[].obs;

isLoading控制加载状态。
sortType记录当前排序类型。
showSystemApps控制是否显示系统应用。

  void changeSortType(int type) {
    sortType.value = type;
    filterAndSort();
  }

  void toggleSystemApps() {
    showSystemApps.value = !showSystemApps.value;
    filterAndSort();
  }

changeSortType更新排序类型后重新筛选排序。
toggleSystemApps切换系统应用显示状态。
两个方法都调用filterAndSort更新列表。

  void filterAndSort() {
    var list = appList.where((app) {
      if (!showSystemApps.value && app.isSystemApp) return false;
      return true;
    }).toList();

filterAndSort执行筛选和排序。
where过滤掉系统应用(如果开关关闭)。
toList转换为列表。

    switch (sortType.value) {
      case 0:
        list.sort((a, b) => b.totalUsage.compareTo(a.totalUsage));
        break;
      case 1:
        list.sort((a, b) => b.wifiUsage.compareTo(a.wifiUsage));

case 0按总流量排序。
case 1按WiFi流量排序。
b.compareTo(a)实现降序排列。

        break;
      case 2:
        list.sort((a, b) => b.mobileUsage.compareTo(a.mobileUsage));
        break;
    }

    filteredList.value = list;
  }

case 2按移动数据排序。
更新filteredList触发UI更新。
流量大的排在前面。

  int get totalUsage => filteredList.fold(0, (sum, app) => sum + app.totalUsage);
}

totalUsage计算所有应用的总流量。
fold方法累加列表中的值。
getter确保每次访问都是最新值。

写在最后

应用列表页面的交互比较丰富,但核心就是筛选和排序。把这两个逻辑封装好,UI层就很简单了。饼图直观展示流量占比,列表详细展示每个应用的信息。

可以继续优化的方向:

  • 添加搜索功能
  • 支持多选批量操作
  • 添加流量使用趋势图
  • 支持按时间段筛选

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐