请添加图片描述

阅读报告页面是统计分析的进阶版,它不仅展示数据,还会给出一些有趣的分析结论,比如"你已超越85%的读者"这样的激励信息。这个页面的设计目标是让用户有成就感,愿意继续保持阅读习惯。

做这个页面的时候,我参考了一些运动类App的年度报告设计,它们都很擅长用数据讲故事,让用户觉得自己很厉害。

依赖引入

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fl_chart/fl_chart.dart';

这个页面也用到了 fl_chart,主要是用折线图展示阅读趋势。

页面主体结构

class ReadingReportPage extends StatelessWidget {
  const ReadingReportPage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFFDF8F3),
      appBar: AppBar(
        title: const Text('阅读报告'),
        backgroundColor: const Color(0xFF5B4636),
        foregroundColor: Colors.white,
        actions: [
          IconButton(icon: const Icon(Icons.share), onPressed: () {}),
        ],
      ),

AppBar 右上角有个分享按钮,用户可以把自己的阅读报告分享到社交平台。这个功能在实际项目中需要接入分享SDK。

页面内容布局

      body: SingleChildScrollView(
        padding: EdgeInsets.all(16.w),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            _buildHeader(),
            SizedBox(height: 20.h),
            _buildHighlights(),
            SizedBox(height: 20.h),
            _buildTrendChart(),
            SizedBox(height: 20.h),
            _buildTopBooks(),
            SizedBox(height: 20.h),
            _buildReadingHabits(),
          ],
        ),
      ),
    );
  }

页面分五个模块:头部标题、数据亮点、趋势图表、本月最爱、阅读习惯。每个模块都有自己的特色。

头部标题卡片

  Widget _buildHeader() {
    return Container(
      padding: EdgeInsets.all(24.w),
      decoration: BoxDecoration(
        gradient: const LinearGradient(
          colors: [Color(0xFF5B4636), Color(0xFF8B7355)],
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
        ),
        borderRadius: BorderRadius.circular(16.r),
      ),

头部用渐变背景,从左上到右下,比单纯的水平渐变更有层次感。

标题内容

      child: Column(
        children: [
          Text('2024年1月', style: TextStyle(
            color: Colors.white70,
            fontSize: 14.sp,
          )),
          SizedBox(height: 8.h),
          Text('阅读报告', style: TextStyle(
            color: Colors.white,
            fontSize: 28.sp,
            fontWeight: FontWeight.bold,
          )),

标题分两行,上面是月份,下面是"阅读报告"四个大字。月份用半透明白色,报告标题用纯白加粗,字号 28,非常醒目。

激励文案

          SizedBox(height: 16.h),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(Icons.auto_awesome, color: Colors.amber, size: 20.sp),
              SizedBox(width: 8.w),
              Text('你已超越 85% 的读者', style: TextStyle(
                color: Colors.amber,
                fontSize: 14.sp,
              )),
            ],
          ),
        ],
      ),
    );
  }

底部是一句激励文案,用琥珀色显示,前面加个星星图标。这种设计能给用户成就感,激励他们继续阅读。

这个百分比可以根据用户的实际阅读量计算,和其他用户对比得出。

数据亮点卡片

  Widget _buildHighlights() {
    return Row(
      children: [
        Expanded(child: _buildHighlightCard('阅读天数', '28', '天', Icons.calendar_today)),
        SizedBox(width: 12.w),
        Expanded(child: _buildHighlightCard('完成书籍', '4', '本', Icons.menu_book)),
        SizedBox(width: 12.w),
        Expanded(child: _buildHighlightCard('阅读时长', '32', '小时', Icons.timer)),
      ],
    );
  }

三个数据亮点并排显示,分别是阅读天数、完成书籍数、阅读时长。用 Expanded 让它们平分宽度。

亮点卡片组件

  Widget _buildHighlightCard(String label, String value, String unit, IconData icon) {
    return Container(
      padding: EdgeInsets.all(14.w),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12.r),
      ),
      child: Column(
        children: [
          Icon(icon, color: const Color(0xFF5B4636), size: 24.sp),
          SizedBox(height: 8.h),

每个卡片顶部是一个图标,用主题色显示。图标能让用户快速识别这是什么数据。

数值显示

          RichText(
            text: TextSpan(
              children: [
                TextSpan(
                  text: value,
                  style: TextStyle(
                    fontSize: 22.sp,
                    fontWeight: FontWeight.bold,
                    color: const Color(0xFF3D2914),
                  ),
                ),
                TextSpan(
                  text: ' $unit',
                  style: TextStyle(
                    fontSize: 12.sp,
                    color: Colors.grey[600],
                  ),
                ),
              ],
            ),
          ),
          SizedBox(height: 4.h),
          Text(label, style: TextStyle(color: Colors.grey[600], fontSize: 11.sp)),
        ],
      ),
    );
  }

数值用 RichText 实现,数字大号加粗,单位小号灰色。这样数字更突出,单位作为补充说明。

阅读趋势折线图

  Widget _buildTrendChart() {
    return Container(
      padding: EdgeInsets.all(16.w),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12.r),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('阅读趋势', style: TextStyle(
            fontSize: 16.sp,
            fontWeight: FontWeight.bold,
            color: const Color(0xFF3D2914),
          )),
          SizedBox(height: 20.h),

趋势图用折线图展示,比柱状图更能体现数据的连续变化。

折线图配置

          SizedBox(
            height: 160.h,
            child: LineChart(
              LineChartData(
                gridData: FlGridData(
                  show: true,
                  drawVerticalLine: false,
                  horizontalInterval: 10,
                  getDrawingHorizontalLine: (v) => FlLine(
                    color: Colors.grey[200]!,
                    strokeWidth: 1,
                  ),
                ),

LineChartfl_chart 的折线图组件。网格线只显示水平线,间隔 10,用浅灰色绘制。

坐标轴设置

                titlesData: FlTitlesData(
                  bottomTitles: AxisTitles(
                    sideTitles: SideTitles(
                      showTitles: true,
                      getTitlesWidget: (v, m) => Text(
                        ['1周', '2周', '3周', '4周'][v.toInt()],
                        style: TextStyle(fontSize: 11.sp, color: Colors.grey[600]),
                      ),
                    ),
                  ),
                  leftTitles: AxisTitles(
                    sideTitles: SideTitles(
                      showTitles: true,
                      reservedSize: 35,
                      getTitlesWidget: (v, m) => Text(
                        '${v.toInt()}页',
                        style: TextStyle(fontSize: 10.sp, color: Colors.grey[500]),
                      ),
                    ),
                  ),
                  topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
                  rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
                ),
                borderData: FlBorderData(show: false),

底部显示周数,左边显示页数。顶部和右边的标题隐藏,让图表更简洁。

折线数据

                lineBarsData: [
                  LineChartBarData(
                    spots: const [
                      FlSpot(0, 35),
                      FlSpot(1, 48),
                      FlSpot(2, 42),
                      FlSpot(3, 55),
                    ],
                    isCurved: true,
                    color: const Color(0xFF5B4636),
                    barWidth: 3,
                    dotData: FlDotData(
                      show: true,
                      getDotPainter: (s, d, b, i) => FlDotCirclePainter(
                        radius: 4,
                        color: const Color(0xFF5B4636),
                        strokeWidth: 2,
                        strokeColor: Colors.white,
                      ),
                    ),
                    belowBarData: BarAreaData(
                      show: true,
                      color: const Color(0xFF5B4636).withOpacity(0.1),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

折线用主题色,宽度 3,isCurved: true 让线条平滑。数据点用圆形标记,有白色描边。折线下方填充浅色区域,增加视觉层次。

本月最爱书籍

  Widget _buildTopBooks() {
    final books = [
      {'title': '百年孤独', 'author': '加西亚·马尔克斯', 'hours': '12小时'},
      {'title': '人类简史', 'author': '尤瓦尔·赫拉利', 'hours': '8小时'},
      {'title': '三体', 'author': '刘慈欣', 'hours': '6小时'},
    ];
    return Container(
      padding: EdgeInsets.all(16.w),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12.r),
      ),

本月最爱展示阅读时间最长的三本书,用排行榜的形式呈现。

书籍列表渲染

      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('本月最爱', style: TextStyle(
            fontSize: 16.sp,
            fontWeight: FontWeight.bold,
            color: const Color(0xFF3D2914),
          )),
          SizedBox(height: 12.h),
          ...books.asMap().entries.map((e) => Padding(
            padding: EdgeInsets.symmetric(vertical: 8.h),
            child: Row(
              children: [
                Container(
                  width: 24.w,
                  height: 24.w,
                  decoration: BoxDecoration(
                    color: e.key == 0 ? Colors.amber : (e.key == 1 ? Colors.grey[400] : Colors.brown[300]),
                    shape: BoxShape.circle,
                  ),
                  child: Center(
                    child: Text(
                      '${e.key + 1}',
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 12.sp,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ),
                ),

排名用圆形徽章显示,第一名金色,第二名银色,第三名铜色。这种设计很有仪式感。

书籍信息

                SizedBox(width: 12.w),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(e.value['title'] as String, style: TextStyle(
                        fontWeight: FontWeight.w600,
                        fontSize: 14.sp,
                      )),
                      Text(e.value['author'] as String, style: TextStyle(
                        color: Colors.grey[600],
                        fontSize: 12.sp,
                      )),
                    ],
                  ),
                ),
                Text(e.value['hours'] as String, style: TextStyle(
                  color: const Color(0xFF5B4636),
                  fontSize: 13.sp,
                )),
              ],
            ),
          )).toList(),
        ],
      ),
    );
  }

每本书显示书名、作者、阅读时长。时长用主题色显示,作为排名的依据。

阅读习惯分析

  Widget _buildReadingHabits() {
    return Container(
      padding: EdgeInsets.all(16.w),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12.r),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('阅读习惯', style: TextStyle(
            fontSize: 16.sp,
            fontWeight: FontWeight.bold,
            color: const Color(0xFF3D2914),
          )),
          SizedBox(height: 16.h),

阅读习惯模块分析用户的阅读行为,给出一些有趣的结论。

习惯数据展示

          _buildHabitItem(Icons.wb_sunny, '最常阅读时段', '晚上 21:00-23:00'),
          _buildHabitItem(Icons.timer, '平均每次阅读', '45 分钟'),
          _buildHabitItem(Icons.speed, '平均阅读速度', '每小时 35 页'),
          _buildHabitItem(Icons.favorite, '最爱分类', '文学'),
        ],
      ),
    );
  }

四个习惯数据:最常阅读时段、平均阅读时长、阅读速度、最爱分类。这些数据能帮助用户了解自己的阅读模式。

习惯项组件

  Widget _buildHabitItem(IconData icon, String label, String value) {
    return Padding(
      padding: EdgeInsets.symmetric(vertical: 8.h),
      child: Row(
        children: [
          Icon(icon, color: const Color(0xFF5B4636), size: 20.sp),
          SizedBox(width: 12.w),
          Text(label, style: TextStyle(color: Colors.grey[600], fontSize: 13.sp)),
          const Spacer(),
          Text(value, style: TextStyle(fontWeight: FontWeight.w600, fontSize: 13.sp)),
        ],
      ),
    );
  }
}

每个习惯项是图标加标签加数值的布局,标签靠左,数值靠右。图标用主题色,让页面更有活力。

数据分析思路

阅读习惯的数据需要通过分析用户的阅读记录得出:

最常阅读时段:统计每条阅读记录的时间,找出出现频率最高的时间段。

平均阅读时长:所有阅读记录的时长总和除以记录数。

阅读速度:总页数除以总时长。

最爱分类:统计每个分类的阅读量,找出最多的那个。

小结

阅读报告页面通过数据可视化和文案设计,让用户对自己的阅读成就有清晰的认识。激励文案能增强用户的成就感,排行榜设计增加趣味性。

折线图展示趋势变化,习惯分析帮助用户了解自己。这些设计都是为了让用户愿意继续使用这个App记录阅读。

下一篇会讲阅读目标页面的实现,让用户可以设定和追踪自己的阅读目标。


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

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐