数据统计是应用的重要功能,它可以帮助用户了解自己的使用情况和眼睛健康状况。本文将详细讲解如何使用fl_chart库来实现数据统计和图表展示功能。

统计页面的整体结构

统计页面包含统计概览、图表选项卡、柱状图和详细统计等多个组件。这样的设计可以让用户从多个角度了解自己的数据。

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fl_chart/fl_chart.dart';
import '../controllers/app_controller.dart';
import 'detail_pages/weekly_stats_detail.dart';
import 'detail_pages/monthly_stats_detail.dart';
import 'detail_pages/health_report_detail.dart';

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

  
  State<StatisticsPage> createState() => _StatisticsPageState();
}

class _StatisticsPageState extends State<StatisticsPage> {
  final AppController appController = Get.find<AppController>();
  int _selectedTab = 0;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('数据统计'),
        elevation: 0,
        backgroundColor: const Color(0xFF2196F3),
        foregroundColor: Colors.white,
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            _buildStatisticsOverview(),
            SizedBox(height: 16.h),
            _buildChartTabs(),
            SizedBox(height: 16.h),
            _buildChart(),
            SizedBox(height: 16.h),
            _buildDetailedStats(),
            SizedBox(height: 20.h),
          ],
        ),
      ),
    );
  }

统计页面使用StatefulWidget来管理选中的图表类型。我们通过_selectedTab变量来跟踪用户选择的是周统计、月统计还是年统计。

统计页面的设计理念

数据统计功能是视力保护应用的重要组成部分,它帮助用户了解自己的使用习惯和眼睛健康状况。统计页面的设计遵循以下原则:首先,提供清晰的数据概览,让用户一目了然地看到关键指标;其次,支持多种统计维度(周、月、年),满足不同用户的需求;再次,使用图表可视化数据,使数据更容易理解;最后,提供详细的统计报告,让用户深入了解自己的数据。

在实现统计功能时,我们使用了fl_chart库来绘制柱状图。这个库提供了丰富的图表类型和自定义选项,使我们能够创建美观且功能完整的图表。同时,我们使用GetX框架来管理应用的全局状态,确保统计数据能够实时更新。

统计概览卡片的实现

统计概览卡片显示本周的关键数据,包括提醒次数、休息时间和眼睛评分。

  Widget _buildStatisticsOverview() {
    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),

统计概览卡片使用Container创建白色卡片,带有圆角和阴影效果。EdgeInsets.all(16.w)设置外边距,EdgeInsets.all(16.w)设置内边距。BoxDecoration配置卡片的样式,包括白色背景、12像素的圆角和阴影。BoxShadow使用Colors.black.withOpacity(0.1)创建半透明黑色阴影,blurRadius: 8使阴影更加柔和。Column包含标题和统计数据行。标题"本周统计"使用16sp的粗体字体,这样可以让标题更加突出。

          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              _buildStatItem('提醒次数', '42', const Color(0xFF2196F3)),
              _buildStatItem('休息时间', '210分', const Color(0xFF4CAF50)),
              _buildStatItem('眼睛评分', '8.5', const Color(0xFFFFC107)),
            ],
          ),
        ],
      ),
    );
  }

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

Row使用mainAxisAlignment: MainAxisAlignment.spaceAround使三个统计项均匀分布。每个统计项通过_buildStatItem方法创建,传入标签、数值和颜色三个参数。_buildStatItem构建单个统计项,使用Column包含两个Text组件:第一个显示数值,使用20sp的粗体字体和指定的颜色,这样可以让数值更加突出;第二个显示标签,使用12sp的灰色字体。每个统计项都有自己的颜色:蓝色代表提醒次数,绿色代表休息时间,黄色代表眼睛评分。这样的设计让用户能够快速识别不同的数据类型。

图表选项卡的实现

图表选项卡允许用户在周统计、月统计和年统计之间切换。

  Widget _buildChartTabs() {
    return Padding(
      padding: EdgeInsets.symmetric(horizontal: 16.w),
      child: Row(
        children: [
          _buildTabButton('周统计', 0),
          SizedBox(width: 12.w),
          _buildTabButton('月统计', 1),
          SizedBox(width: 12.w),
          _buildTabButton('年统计', 2),
        ],
      ),
    );
  }

  Widget _buildTabButton(String label, int index) {
    final isSelected = _selectedTab == index;
    return Expanded(
      child: GestureDetector(
        onTap: () {
          setState(() => _selectedTab = index);
        },
        child: Container(
          padding: EdgeInsets.symmetric(vertical: 10.h),
          decoration: BoxDecoration(
            color: isSelected ? const Color(0xFF2196F3) : Colors.grey[200],
            borderRadius: BorderRadius.circular(8.r),
          ),
          child: Center(
            child: Text(
              label,
              style: TextStyle(
                fontSize: 12.sp,
                fontWeight: FontWeight.w500,
                color: isSelected ? Colors.white : Colors.grey,
              ),
            ),
          ),
        ),
      ),
    );
  }

_buildChartTabs方法使用Padding添加水平边距,Row包含三个_buildTabButton调用,分别为"周统计"、“月统计"和"年统计”。SizedBox(width: 12.w)在按钮之间添加间距。_buildTabButton构建单个选项卡按钮,接收标签和索引两个参数。isSelected变量通过比较_selectedTab和index来判断该选项卡是否被选中。GestureDetector检测点击事件,点击时通过setState更新_selectedTab变量。Expanded使按钮占据相等的宽度。Container的背景色根据isSelected改变:选中时使用蓝色,未选中时使用灰色。Text的颜色也根据isSelected改变:选中时使用白色,未选中时使用灰色。这样的设计让用户能够清楚地看到当前选中的选项卡。

柱状图的实现

柱状图使用fl_chart库来显示数据。

  Widget _buildChart() {
    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(
            _selectedTab == 0 ? '周提醒统计' : _selectedTab == 1 ? '月提醒统计' : '年提醒统计',
            style: TextStyle(
              fontSize: 14.sp,
              fontWeight: FontWeight.bold,
            ),
          ),
          SizedBox(height: 16.h),

柱状图使用Container创建白色卡片,带有圆角和阴影效果。EdgeInsets.symmetric(horizontal: 16.w)设置水平边距,EdgeInsets.all(16.w)设置内边距。BoxDecoration配置卡片的样式,包括白色背景、12像素的圆角和阴影。Column包含标题和图表两个元素。标题使用三元运算符根据_selectedTab的值显示不同的文本:周统计显示"周提醒统计",月统计显示"月提醒统计",年统计显示"年提醒统计"。标题使用14sp的粗体字体,这样可以让标题更加突出。

          SizedBox(
            height: 200.h,
            child: BarChart(
              BarChartData(
                barGroups: _getBarGroups(),
                borderData: FlBorderData(show: false),
                gridData: const FlGridData(show: false),
                titlesData: FlTitlesData(
                  bottomTitles: AxisTitles(
                    sideTitles: SideTitles(
                      showTitles: true,
                      getTitlesWidget: (value, meta) {
                        const titles = ['一', '二', '三', '四', '五', '六', '日'];
                        return Text(
                          titles[value.toInt()],
                          style: TextStyle(fontSize: 10.sp),
                        );
                      },
                    ),
                  ),
                  leftTitles: AxisTitles(
                    sideTitles: SideTitles(
                      showTitles: true,
                      getTitlesWidget: (value, meta) {
                        return Text(
                          '${value.toInt()}',
                          style: TextStyle(fontSize: 10.sp),
                        );
                      },
                    ),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

  List<BarChartGroupData> _getBarGroups() {
    return [
      BarChartGroupData(x: 0, barRods: [BarChartRodData(toY: 5, color: const Color(0xFF2196F3))]),
      BarChartGroupData(x: 1, barRods: [BarChartRodData(toY: 6, color: const Color(0xFF2196F3))]),
      BarChartGroupData(x: 2, barRods: [BarChartRodData(toY: 7, color: const Color(0xFF2196F3))]),
      BarChartGroupData(x: 3, barRods: [BarChartRodData(toY: 8, color: const Color(0xFF2196F3))]),
      BarChartGroupData(x: 4, barRods: [BarChartRodData(toY: 6, color: const Color(0xFF2196F3))]),
      BarChartGroupData(x: 5, barRods: [BarChartRodData(toY: 9, color: const Color(0xFF2196F3))]),
      BarChartGroupData(x: 6, barRods: [BarChartRodData(toY: 7, color: const Color(0xFF2196F3))]),
    ];
  }

SizedBox设置图表的高度为200.h。BarChart组件来自fl_chart库,用于显示柱状图。BarChartData配置图表的数据和样式。barGroups通过_getBarGroups()方法获取,定义了七个柱子,分别代表一周的七天。borderData: FlBorderData(show: false)隐藏图表的边框,gridData: const FlGridData(show: false)隐藏网格线,这样可以简化图表的外观。titlesData配置图表的标题。bottomTitles显示底部标题(星期几),使用getTitlesWidget回调根据value返回对应的星期文字。leftTitles显示左边标题(数值),使用getTitlesWidget回调根据value返回对应的数值。_getBarGroups方法定义了七个BarChartGroupData,每个代表一天的数据。x表示柱子的位置,barRods包含一个BarChartRodData,toY表示柱子的高度,color设置为蓝色。

详细统计按钮的实现

详细统计按钮提供了访问更详细统计信息的方式。

  Widget _buildDetailedStats() {
    return Padding(
      padding: EdgeInsets.symmetric(horizontal: 16.w),
      child: Column(
        children: [
          _buildDetailButton(
            '周详统计',
            Icons.calendar_today,
            () => Get.to(() => const WeeklyStatsDetail()),
          ),
          SizedBox(height: 12.h),
          _buildDetailButton(
            '月详统计',
            Icons.date_range,
            () => Get.to(() => const MonthlyStatsDetail()),
          ),
          SizedBox(height: 12.h),
          _buildDetailButton(
            '健康报告',
            Icons.description,
            () => Get.to(() => const HealthReportDetail()),
          ),
        ],
      ),
    );
  }

  Widget _buildDetailButton(String label, IconData icon, VoidCallback onTap) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        padding: EdgeInsets.all(12.w),
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(8.r),
          border: Border.all(color: Colors.grey[300]!, width: 1),
        ),
        child: Row(
          children: [
            Icon(icon, color: const Color(0xFF2196F3), size: 20.sp),
            SizedBox(width: 12.w),
            Expanded(
              child: Text(
                label,
                style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w500),
              ),
            ),
            Icon(Icons.arrow_forward_ios, size: 16.sp, color: Colors.grey),
          ],
        ),
      ),
    );
  }
}

_buildDetailedStats方法使用Padding添加水平边距,Column包含三个_buildDetailButton调用,分别为"周详统计"、“月详统计"和"健康报告”。SizedBox(height: 12.h)在按钮之间添加间距。_buildDetailButton构建单个详情按钮,接收标签、图标和点击回调三个参数。GestureDetector检测点击事件,点击时通过Get.to()导航到相应的详情页面。Container使用白色背景和灰色边框,EdgeInsets.all(12.w)设置内边距。Row包含四个元素:左边的图标(蓝色,20sp)、标签文本(14sp粗体)、右边的箭头图标(灰色,16sp)。Expanded使标签文本占据中间的空间。这样的设计让用户能够快速访问更详细的统计信息。

导航设计的考虑

详细统计按钮提供了访问更深层次统计信息的入口。通过提供三个不同的详情页面(周详统计、月详统计和健康报告),我们让用户能够根据自己的需求选择查看不同的统计信息。

按钮的设计也很重要。我们使用了白色背景和灰色边框,使按钮与卡片的背景形成对比。按钮中的图标和文字清晰易读,用户能够快速理解按钮的功能。右边的箭头图标表示这是一个可点击的元素,会导航到另一个页面。

在实现导航时,我们使用了GetX框架提供的Get.to()方法。这个方法提供了平滑的页面转换动画,使用户体验更加流畅。同时,GetX框架还提供了返回上一页的功能,用户可以通过返回按钮或手势返回到统计页面。

统计功能的扩展可能性

在实际应用中,我们可以进一步扩展统计功能:

  1. 自定义时间范围:允许用户选择任意时间范围来查看统计数据
  2. 数据导出:允许用户将统计数据导出为CSV或PDF格式
  3. 对比分析:允许用户对比不同时间段的数据
  4. 趋势预测:使用机器学习算法预测未来的趋势
  5. 数据分享:允许用户分享统计数据到社交媒体

总结

通过以上的设计,我们实现了一个完整的数据统计系统。用户可以查看本周的统计数据、切换不同的统计类型、查看详细的统计信息。这样的设计使得数据统计功能易于使用和理解。

在实际应用中,我们可以进一步扩展这个系统,添加更多的图表类型、更详细的数据分析等功能。

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

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐