套餐管理是流量监控App的核心功能之一,用户在这里设置自己的流量套餐信息,App才能准确计算剩余流量和使用进度。这个页面需要展示套餐使用情况,并提供编辑套餐的入口。

页面结构规划

套餐管理页面分为几个部分:

  • 套餐进度卡片:大号环形进度条展示使用百分比
  • 详情信息卡片:剩余流量、剩余天数、日均可用
  • 提醒状态卡片:根据使用情况显示正常或警告

空状态处理

如果用户还没设置套餐,需要显示引导界面:

Widget _buildNoPlan() {
  return Center(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Icon(Icons.sim_card_outlined, size: 80.sp, color: AppTheme.textSecondary),

Center让内容居中显示。
Column垂直排列图标和文字。
sim_card_outlined图标表示套餐。

        SizedBox(height: 16.h),
        Text('暂无套餐', style: TextStyle(fontSize: 16.sp, color: AppTheme.textSecondary)),
        SizedBox(height: 16.h),
        ElevatedButton(onPressed: () {}, child: const Text('添加套餐')),

间距16.h后显示提示文字。
ElevatedButton引导用户添加套餐。
简洁的空状态引导用户操作。

      ],
    ),
  );
}

闭合Column和Center完成空状态。
空状态页面简洁明了。
一个图标、一句提示、一个按钮。

套餐进度卡片

用大号环形进度条作为页面的视觉中心:

Widget _buildPlanCard() {
  final plan = controller.plan.value!;
  return Container(
    padding: EdgeInsets.all(20.w),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(16.r),
    ),

获取当前套餐数据。
Container作为进度卡片的容器。
白色背景与页面灰色背景对比。

    child: Column(
      children: [
        CircularPercentIndicator(
          radius: 80.r,
          lineWidth: 12.w,
          percent: plan.usagePercentage / 100,

Column垂直排列进度条和信息。
CircularPercentIndicator是环形进度条组件。
radius设置进度条半径。

          center: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                '${plan.usagePercentage.toStringAsFixed(0)}%',
                style: TextStyle(fontSize: 24.sp, fontWeight: FontWeight.bold),
              ),

center放置进度条中间的内容。
显示使用百分比,不保留小数。
24.sp大字号突出显示。

              Text('已使用', style: TextStyle(fontSize: 12.sp, color: AppTheme.textSecondary)),
            ],
          ),
          progressColor: plan.isOverThreshold ? AppTheme.warningColor : AppTheme.primaryColor,
          backgroundColor: Colors.grey.shade200,
        ),

"已使用"标签用12.sp小字号。
超过阈值时进度条变成警告色。
灰色背景作为进度条底色。

        SizedBox(height: 20.h),
        Text(plan.name, style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.w600)),
        SizedBox(height: 8.h),
        Text(
          '${plan.formattedUsed} / ${plan.formattedTotal}',
          style: TextStyle(fontSize: 14.sp, color: AppTheme.textSecondary),
        ),

间距20.h后显示套餐名称。
18.sp字号,w600加粗突出名称。
显示已用/总量的格式化字符串。

      ],
    ),
  );
}

闭合Column和Container完成进度卡片。
环形进度条是页面的视觉焦点。
整体设计直观清晰。

详情信息卡片

展示更多套餐相关的数据:

Widget _buildDetailsCard() {
  final plan = controller.plan.value!;
  return Container(
    padding: EdgeInsets.all(16.w),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(12.r),
    ),

获取当前套餐数据。
Container作为详情卡片的容器。
白色背景与页面灰色背景对比。

    child: Column(
      children: [
        _buildDetailRow('剩余流量', plan.formattedRemaining),
        const Divider(),
        _buildDetailRow('剩余天数', '${plan.daysRemaining} 天'),

Column垂直排列各详情项。
_buildDetailRow构建单行详情。
Divider分隔各行。

        const Divider(),
        _buildDetailRow(
          '日均可用',
          '${(plan.remainingData / plan.daysRemaining / (1024 * 1024)).toStringAsFixed(0)} MB',
        ),
      ],
    ),
  );
}

日均可用=剩余流量/剩余天数。
转换为MB单位显示。
帮助用户控制每日流量使用。

详情行组件

单行详情的显示:

Widget _buildDetailRow(String label, String value) {
  return Padding(
    padding: EdgeInsets.symmetric(vertical: 8.h),
    child: Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,

Padding添加垂直内边距。
Row两端对齐标签和数值。
spaceBetween让两端对齐。

      children: [
        Text(label, style: TextStyle(fontSize: 14.sp, color: AppTheme.textSecondary)),
        Text(value, style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w600)),
      ],
    ),
  );
}

标签用次要颜色作为说明。
数值用w600加粗突出显示。
整体设计简洁直观。

提醒状态卡片

根据套餐使用情况显示不同的提示:

Widget _buildAlertCard() {
  final plan = controller.plan.value!;
  return Container(
    padding: EdgeInsets.all(16.w),
    decoration: BoxDecoration(

获取当前套餐数据。
Container作为提醒卡片的容器。
16.w内边距让内容不贴边。

      color: plan.isOverThreshold 
          ? AppTheme.warningColor.withOpacity(0.1) 
          : AppTheme.wifiColor.withOpacity(0.1),
      borderRadius: BorderRadius.circular(12.r),
    ),

超过阈值用警告色浅色背景。
正常状态用绿色浅色背景。
12.r圆角保持视觉一致。

    child: Row(
      children: [
        Icon(
          plan.isOverThreshold ? Icons.warning_outlined : Icons.check_circle_outline,
          color: plan.isOverThreshold ? AppTheme.warningColor : AppTheme.wifiColor,
          size: 24.sp,
        ),

Row横向排列图标和文字。
超过阈值显示警告图标。
正常状态显示勾选图标。

        SizedBox(width: 12.w),
        Expanded(
          child: Text(
            plan.isOverThreshold 
                ? '流量使用已超过${plan.alertThreshold}%,请注意控制' 
                : '流量使用正常',

间距12.w让图标和文字不挤。
Expanded让文字占据剩余空间。
根据状态显示不同提示文字。

            style: TextStyle(
              fontSize: 13.sp,
              color: plan.isOverThreshold ? AppTheme.warningColor : AppTheme.wifiColor,
            ),
          ),
        ),
      ],
    ),
  );
}

文字颜色与图标颜色一致。
13.sp字号适合提示信息。
整体设计让用户一眼看出状态。

DataPlan数据模型

套餐数据的模型定义:

class DataPlan {
  final String id;
  final String name;
  final int totalData;
  final int usedData;
  final DateTime startDate;

id是套餐的唯一标识。
name是套餐名称。
totalData和usedData是总流量和已用流量。

  final DateTime endDate;
  final int alertThreshold;
  final bool isActive;

  int get remainingData => totalData - usedData;

startDate和endDate是套餐有效期。
alertThreshold是提醒阈值百分比。
remainingData计算剩余流量。

  double get usagePercentage => totalData > 0 ? (usedData / totalData) * 100 : 0;
  int get daysRemaining => endDate.difference(DateTime.now()).inDays;
  bool get isOverThreshold => usagePercentage >= alertThreshold;

usagePercentage计算使用百分比。
daysRemaining计算剩余天数。
isOverThreshold判断是否超过阈值。

  String get formattedTotal => _formatBytes(totalData);
  String get formattedUsed => _formatBytes(usedData);
  String get formattedRemaining => _formatBytes(remainingData);
}

格式化方法将字节数转为可读字符串。
formattedTotal、formattedUsed、formattedRemaining。
计算属性让数据访问更方便。

Controller层实现

控制器管理套餐相关的状态:

class DataPlanController extends GetxController {
  final plan = Rx<DataPlan?>(null);
  final isEditing = false.obs;

  
  void onInit() {

plan存储当前套餐,可为null。
isEditing控制编辑状态。
onInit在控制器初始化时调用。

    super.onInit();
    loadPlan();
  }

  void loadPlan() {
    plan.value = DataPlan(
      id: '1',
      name: '月度套餐',

loadPlan加载套餐数据。
创建DataPlan对象。
模拟数据用于演示。

      totalData: 1024 * 1024 * 1024 * 10,
      usedData: 1024 * 1024 * 1024 * 6,
      startDate: DateTime.now().subtract(const Duration(days: 15)),
      endDate: DateTime.now().add(const Duration(days: 15)),
      alertThreshold: 80,

总流量10GB,已用6GB。
套餐从15天前开始,还有15天结束。
alertThreshold设为80%。

    );
  }
}

闭合loadPlan方法和控制器类。
实际项目中从本地存储加载数据。
套餐数据应该持久化保存。

写在最后

套餐管理页面的核心是数据展示,把DataPlan模型设计好,UI层就很简单了。环形进度条直观展示使用进度,详情卡片提供更多数据,提醒卡片根据状态给出提示。

可以继续优化的方向:

  • 支持多套餐管理
  • 添加套餐编辑功能
  • 支持套餐到期提醒
  • 添加流量预测功能

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

Logo

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

更多推荐