概述

在视力保护提醒应用中,月统计数据分析是一个重要的功能模块。它帮助用户了解整个月份的用眼习惯和提醒执行情况。本文将详细讲解如何实现一个完整的月统计数据分析页面,包括月度概览、日历视图、周趋势分析等功能。

月统计数据分析的核心功能

月统计数据分析页面主要包含以下几个部分:

  1. 月度概览卡片 - 展示本月的总提醒次数、总休息时间和平均评分
  2. 日历视图 - 使用table_calendar库展示整个月的日期,支持日期选择
  3. 周趋势分析 - 展示每周的提醒次数和趋势变化

这些功能结合在一起,为用户提供了一个全面的月度数据视图。

项目依赖配置

在pubspec.yaml中,我们已经配置了所需的依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_screenutil: ^5.9.0
  table_calendar: ^3.0.9
  get: ^4.6.5

flutter_screenutil用于屏幕适配,table_calendar用于日历显示,get用于状态管理。这些依赖都是为了支持鸿蒙系统的Flutter开发。

月统计详情页面实现

导入和类定义

class MonthlyStatsDetail extends StatefulWidget {
  const MonthlyStatsDetail({super.key});

  
  State<MonthlyStatsDetail> createState() => _MonthlyStatsDetailState();
}

定义了MonthlyStatsDetail有状态组件,用于实现月统计数据分析功能。使用StatefulWidget是因为我们需要管理日期选择的状态。createState方法返回_MonthlyStatsDetailState实例,负责管理月统计的具体逻辑。这种设计模式使月统计页面能够响应用户交互并更新显示。

状态变量初始化

class _MonthlyStatsDetailState extends State<MonthlyStatsDetail> {
  late DateTime _focusedDay;
  late DateTime _selectedDay;

  
  void initState() {
    super.initState();
    _focusedDay = DateTime.now();
    _selectedDay = DateTime.now();
  }

定义了两个DateTime变量来管理日期状态。_focusedDay表示日历当前显示的月份,_selectedDay表示用户选中的日期。在initState中,我们将它们都初始化为当前日期。这样用户打开页面时,日历会显示当前月份,并且当前日期被选中。这种初始化方式提供了良好的用户体验。

主构建方法

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('月详统计'),
        backgroundColor: const Color(0xFF2196F3),
        foregroundColor: Colors.white,
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            _buildMonthlyOverview(),
            SizedBox(height: 16.h),
            _buildCalendar(),
            SizedBox(height: 16.h),
            _buildMonthlyTrend(),
            SizedBox(height: 20.h),
          ],
        ),
      ),
    );
  }

build方法构建了整个月统计页面的布局。Scaffold提供了Material Design的基本框架。AppBar显示页面标题"月详统计",使用蓝色背景和白色文字。SingleChildScrollView包装了主体内容,确保当内容超过屏幕高度时可以滚动。通过SizedBox添加垂直间距,使页面布局更加清晰。这种分层的UI结构使代码易于理解和维护。

月度概览卡片实现

概览容器

  Widget _buildMonthlyOverview() {
    return Container(
      margin: EdgeInsets.all(16.w),
      padding: EdgeInsets.all(16.w),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12.r),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.1),
            blurRadius: 8,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            '本月概览',
            style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
          ),
          SizedBox(height: 16.h),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              _buildOverviewItem('总提醒', '224次', const Color(0xFF2196F3)),
              _buildOverviewItem('总休息', '1120分', const Color(0xFF4CAF50)),
              _buildOverviewItem('平均评分', '8.1', const Color(0xFFFFC107)),
            ],
          ),
        ],
      ),
    );
  }

_buildMonthlyOverview方法构建月度概览区域。使用Container创建一个白色卡片,添加圆角和阴影效果。Column竖直排列标题和数据行。标题使用较大的加粗字体。Row使用mainAxisAlignment: MainAxisAlignment.spaceAround均匀分布三个数据项。这种设计使月度概览数据清晰易读,用户可以快速了解一月的关键指标。

概览项目组件

  Widget _buildOverviewItem(String label, String value, Color color) {
    return Column(
      children: [
        Text(
          value,
          style: TextStyle(
            fontSize: 18.sp,
            fontWeight: FontWeight.bold,
            color: color,
          ),
        ),
        SizedBox(height: 4.h),
        Text(
          label,
          style: TextStyle(fontSize: 12.sp, color: Colors.grey),
        ),
      ],
    );
  }

_buildOverviewItem是一个辅助方法,用于构建单个概览项。使用Column竖直排列数值和标签。数值使用较大的加粗字体和指定的颜色,提高了重要数据的可见性。标签使用较小的灰色字体,作为数值的说明。这种设计模式提高了代码的复用性,避免了重复的UI构建代码。

日历视图实现

日历容器

  Widget _buildCalendar() {
    return Container(
      margin: EdgeInsets.symmetric(horizontal: 16.w),
      padding: EdgeInsets.all(16.w),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12.r),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.1),
            blurRadius: 8,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      child: TableCalendar(
        firstDay: DateTime.utc(2024, 1, 1),
        lastDay: DateTime.utc(2025, 12, 31),
        focusedDay: _focusedDay,
        selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
        onDaySelected: (selectedDay, focusedDay) {
          setState(() {
            _selectedDay = selectedDay;
            _focusedDay = focusedDay;
          });
        },
        calendarStyle: CalendarStyle(
          selectedDecoration: BoxDecoration(
            color: const Color(0xFF2196F3),
            shape: BoxShape.circle,
          ),
          todayDecoration: BoxDecoration(
            color: const Color(0xFF2196F3).withOpacity(0.3),
            shape: BoxShape.circle,
          ),
        ),
        headerStyle: HeaderStyle(
          formatButtonVisible: false,
          titleCentered: true,
          titleTextStyle: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
        ),
      ),
    );
  }

_buildCalendar方法构建日历视图区域。使用Container创建一个白色卡片。TableCalendar是table_calendar库提供的核心组件。firstDaylastDay定义了日历的可选范围。focusedDay表示当前显示的月份。selectedDayPredicate用于判断某个日期是否被选中。onDaySelected回调在用户选择日期时触发,通过setState更新_selectedDay_focusedDaycalendarStyle定义了日历的样式,包括选中日期的颜色和今天的颜色。这种设计使用户能够直观地选择和查看日期。

周趋势分析实现

趋势容器

  Widget _buildMonthlyTrend() {
    return Container(
      margin: EdgeInsets.symmetric(horizontal: 16.w),
      padding: EdgeInsets.all(16.w),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12.r),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.1),
            blurRadius: 8,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            '月度趋势',
            style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.bold),
          ),
          SizedBox(height: 12.h),
          ...[
            {'week': '第1周', 'reminders': 52, 'color': const Color(0xFF2196F3)},
            {'week': '第2周', 'reminders': 56, 'color': const Color(0xFF4CAF50)},
            {'week': '第3周', 'reminders': 58, 'color': const Color(0xFFFFC107)},
            {'week': '第4周', 'reminders': 58, 'color': const Color(0xFF00BCD4)},
          ].map((item) => _buildWeekItem(
            item['week'] as String,
            item['reminders'] as int,
            item['color'] as Color,
          )),
        ],
      ),
    );
  }

_buildMonthlyTrend方法构建月度趋势区域。使用Container创建一个白色卡片。标题使用较大的加粗字体。使用spread operator (…)展开列表中的Widget。定义了一个包含四周数据的列表,每周包含周标签、提醒次数和颜色。使用map方法将每个数据项转换为_buildWeekItem Widget。这种设计使月度趋势数据清晰地展示在卡片中。

周项目组件

  Widget _buildWeekItem(String week, int reminders, Color color) {
    return Padding(
      padding: EdgeInsets.symmetric(vertical: 8.h),
      child: Row(
        children: [
          SizedBox(
            width: 50.w,
            child: Text(
              week,
              style: TextStyle(fontSize: 12.sp, fontWeight: FontWeight.w500),
            ),
          ),
          Expanded(
            child: ClipRRect(
              borderRadius: BorderRadius.circular(4.r),
              child: LinearProgressIndicator(
                value: reminders / 60,
                minHeight: 8.h,
                backgroundColor: Colors.grey[200],
                valueColor: AlwaysStoppedAnimation<Color>(color),
              ),
            ),
          ),
          SizedBox(width: 12.w),
          Text(
            '$reminders次',
            style: TextStyle(
              fontSize: 12.sp,
              fontWeight: FontWeight.bold,
              color: color,
            ),
          ),
        ],
      ),
    );
  }
}

_buildWeekItem方法构建单个周的趋势项。使用Row横向排列周标签、进度条和提醒次数。第一个SizedBox固定周标签的宽度为50.w。Expanded使进度条占据中间的剩余空间。LinearProgressIndicator用于可视化显示提醒次数的比例,value: reminders / 60将提醒次数转换为0-1之间的比例值。ClipRRect为进度条添加圆角。最后显示具体的提醒次数。这种设计使用户能够快速比较不同周的提醒次数。

屏幕适配处理

在整个页面中,我们使用flutter_screenutil库来处理屏幕适配。.w表示宽度单位,.h表示高度单位,.sp表示字体大小单位。这样可以确保在不同屏幕尺寸的设备上,UI元素的大小和间距都能正确显示。例如,EdgeInsets.all(16.w)表示四周都有16个宽度单位的边距。TextStyle(fontSize: 16.sp)表示字体大小为16个字体单位。

日历库的使用

table_calendar库提供了强大的日历功能,包括日期选择、月份导航、事件标记等。在这个例子中,我们使用了TableCalendar来显示整个月的日期。通过配置CalendarStyleHeaderStyle,我们可以自定义日历的样式。

数据绑定与更新

在实际应用中,月统计数据应该从数据库或API获取。可以在initState中加载数据,然后通过setState更新UI。例如:


void initState() {
  super.initState();
  _focusedDay = DateTime.now();
  _selectedDay = DateTime.now();
  _loadMonthlyData();
}

void _loadMonthlyData() {
  // 从数据库或API加载月度数据
  // 然后调用setState更新UI
}

总结

月统计数据分析页面通过组合多个UI组件,为用户提供了一个全面的月度数据视图。使用table_calendar库简化了日历的实现,使用LinearProgressIndicator可视化显示了趋势数据。通过flutter_screenutil库,我们确保了UI在不同屏幕尺寸上的正确显示。

这个页面的设计遵循了Material Design的设计规范,使用了统一的颜色方案和间距。用户可以通过选择日期来查看特定日期的详细数据,通过周趋势分析来了解整个月的变化趋势。

在实际应用中,我们可以进一步扩展这个系统,添加更多的筛选选项、数据导出等功能。例如,可以添加月份选择器让用户查看不同月份的数据,可以添加数据导出功能让用户保存数据。

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

Logo

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

更多推荐