请添加图片描述

阅读目标页面是帮助用户设定和追踪阅读计划的功能。很多人都有"今年要读50本书"这样的目标,但往往坚持不下来。这个页面就是要让用户随时看到自己的进度,激励他们继续阅读。

做这个功能的时候,我参考了一些健身App的目标追踪设计,它们都很擅长用进度可视化来激励用户。

依赖引入

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:percent_indicator/circular_percent_indicator.dart';
import '../../app/routes/app_routes.dart';

这个页面用到了 percent_indicator 库来显示圆形进度条,这个库在鸿蒙设备上也能正常运行。

页面主体结构

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFFDF8F3),
      appBar: AppBar(
        title: const Text('阅读目标'),
        backgroundColor: const Color(0xFF5B4636),
        foregroundColor: Colors.white,
      ),

页面结构比较简单,一个 AppBar 加上内容区域,底部有个悬浮按钮用来添加新目标。

页面内容布局

      body: SingleChildScrollView(
        padding: EdgeInsets.all(16.w),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            _buildCurrentGoal(),
            SizedBox(height: 20.h),
            _buildGoalList(),
          ],
        ),
      ),

页面分两个部分:顶部是当前的主要目标(年度阅读目标),下面是其他目标列表。

悬浮添加按钮

      floatingActionButton: FloatingActionButton(
        backgroundColor: const Color(0xFF5B4636),
        onPressed: () => Get.toNamed(AppRoutes.addGoal),
        child: const Icon(Icons.add, color: Colors.white),
      ),
    );
  }

悬浮按钮用主题色背景,点击跳转到添加目标页面。放在右下角是 Material Design 的标准位置。

年度目标卡片

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

年度目标是最重要的,用渐变背景突出显示。这个卡片会占据页面顶部很大的空间。

目标标题

      child: Column(
        children: [
          Text('2024年阅读目标', style: TextStyle(
            color: Colors.white70,
            fontSize: 14.sp,
          )),
          SizedBox(height: 20.h),

标题用半透明白色,字号小一些,作为副标题存在。

进度展示区域

          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              CircularPercentIndicator(
                radius: 55.r,
                lineWidth: 10.w,
                percent: 0.15,
                center: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text('15%', style: TextStyle(
                      color: Colors.white,
                      fontSize: 18.sp,
                      fontWeight: FontWeight.bold,
                    )),
                    Text('完成', style: TextStyle(
                      color: Colors.white70,
                      fontSize: 11.sp,
                    )),
                  ],
                ),
                progressColor: Colors.amber,
                backgroundColor: Colors.white24,
                circularStrokeCap: CircularStrokeCap.round,
              ),

左边是圆形进度条,用 CircularPercentIndicator 组件实现。进度条用琥珀色,和深棕色背景形成对比。中间显示完成百分比。

circularStrokeCap: CircularStrokeCap.round 让进度条两端是圆形的,看起来更柔和。

目标数据统计

              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  _buildGoalStat('目标', '50 本'),
                  SizedBox(height: 12.h),
                  _buildGoalStat('已完成', '8 本'),
                  SizedBox(height: 12.h),
                  _buildGoalStat('剩余', '42 本'),
                ],
              ),
            ],
          ),

右边显示具体的数字:目标数、已完成数、剩余数。这三个数据让用户对进度有清晰的认识。

统计项组件

  Widget _buildGoalStat(String label, String value) {
    return Row(
      children: [
        Text('$label: ', style: TextStyle(
          color: Colors.white70,
          fontSize: 14.sp,
        )),
        Text(value, style: TextStyle(
          color: Colors.white,
          fontSize: 16.sp,
          fontWeight: FontWeight.bold,
        )),
      ],
    );
  }

每个统计项是标签加数值的组合,标签用半透明白色,数值用纯白加粗。

预测提示

          SizedBox(height: 20.h),
          Container(
            padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.h),
            decoration: BoxDecoration(
              color: Colors.white.withOpacity(0.15),
              borderRadius: BorderRadius.circular(20.r),
            ),
            child: Text(
              '按当前进度,预计可完成 53 本 📚',
              style: TextStyle(color: Colors.white, fontSize: 13.sp),
            ),
          ),
        ],
      ),
    );
  }

底部有个预测提示,根据当前的阅读速度预测年底能完成多少本。这个功能能给用户信心,让他们知道自己的进度是否正常。

用了一个书本的 emoji 增加趣味性。

其他目标列表

  Widget _buildGoalList() {
    final goals = [
      {'title': '月度目标', 'target': '6本/月', 'current': '4本', 'percent': 0.67, 'status': '进行中'},
      {'title': '每日阅读', 'target': '30分钟/天', 'current': '平均45分钟', 'percent': 1.0, 'status': '已达成'},
      {'title': '笔记目标', 'target': '每本3条', 'current': '平均4条', 'percent': 1.0, 'status': '已达成'},
    ];

除了年度目标,还可以设置月度目标、每日阅读时长目标、笔记数量目标等。这些目标用列表形式展示。

目标列表标题

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text('其他目标', style: TextStyle(
          fontSize: 18.sp,
          fontWeight: FontWeight.bold,
          color: const Color(0xFF3D2914),
        )),
        SizedBox(height: 12.h),

标题用深棕色,和页面其他标题保持一致。

目标卡片渲染

        ...goals.map((g) => Container(
          margin: EdgeInsets.only(bottom: 12.h),
          padding: EdgeInsets.all(16.w),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(12.r),
          ),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                children: [
                  Text(g['title'] as String, style: TextStyle(
                    fontWeight: FontWeight.w600,
                    fontSize: 15.sp,
                  )),
                  const Spacer(),

每个目标是一个白色卡片,标题在左边,状态标签在右边。

状态标签

                  Container(
                    padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
                    decoration: BoxDecoration(
                      color: (g['percent'] as double) >= 1.0
                          ? Colors.green.withOpacity(0.1)
                          : const Color(0xFF5B4636).withOpacity(0.1),
                      borderRadius: BorderRadius.circular(4.r),
                    ),
                    child: Text(
                      g['status'] as String,
                      style: TextStyle(
                        fontSize: 11.sp,
                        color: (g['percent'] as double) >= 1.0
                            ? Colors.green
                            : const Color(0xFF5B4636),
                      ),
                    ),
                  ),
                ],
              ),

状态标签根据完成情况显示不同颜色:已达成用绿色,进行中用主题色。这样用户一眼就能看出哪些目标完成了。

目标详情

              SizedBox(height: 12.h),
              Row(
                children: [
                  Text('目标: ${g['target']}', style: TextStyle(
                    color: Colors.grey[600],
                    fontSize: 13.sp,
                  )),
                  SizedBox(width: 16.w),
                  Text('当前: ${g['current']}', style: TextStyle(
                    color: Colors.grey[600],
                    fontSize: 13.sp,
                  )),
                ],
              ),

显示目标值和当前值,让用户知道差距有多大。

进度条

              SizedBox(height: 10.h),
              ClipRRect(
                borderRadius: BorderRadius.circular(4.r),
                child: LinearProgressIndicator(
                  value: (g['percent'] as double).clamp(0.0, 1.0),
                  backgroundColor: Colors.grey[200],
                  valueColor: AlwaysStoppedAnimation(
                    (g['percent'] as double) >= 1.0
                        ? Colors.green
                        : const Color(0xFF5B4636),
                  ),
                  minHeight: 6.h,
                ),
              ),
            ],
          ),
        )).toList(),
      ],
    );
  }
}

每个目标下面有个进度条,已达成的用绿色,进行中的用主题色。clamp(0.0, 1.0) 确保进度值不会超过 100%。

目标类型设计

这个页面支持多种类型的目标:

年度阅读目标:一年要读多少本书,这是最常见的目标类型。

月度目标:每个月要读多少本,比年度目标更容易追踪。

每日时长:每天要阅读多长时间,适合想养成阅读习惯的用户。

笔记目标:每本书要写多少条笔记,鼓励用户深度阅读。

进度计算逻辑

进度的计算需要根据目标类型来:

年度目标:已读书籍数 / 目标书籍数

月度目标:本月已读 / 月度目标

每日时长:本月平均每日时长 / 目标时长

笔记目标:平均每本笔记数 / 目标笔记数

小结

阅读目标页面通过可视化的进度展示,帮助用户追踪自己的阅读计划。圆形进度条直观地显示完成百分比,预测功能给用户信心。

多种目标类型满足不同用户的需求,状态标签让用户快速识别哪些目标已经完成。

下一篇会讲添加目标页面的实现,让用户可以创建自己的阅读目标。


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

Logo

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

更多推荐