每日推荐是音乐播放器中一个非常受欢迎的功能,系统根据用户的听歌习惯每天推荐一批歌曲。本篇将详细介绍如何实现每日推荐页面,包括日期展示、渐变头部设计和推荐歌曲列表。

功能分析

每日推荐页面需要实现以下功能:渐变头部显示当前日期、播放全部按钮、收藏和下载按钮、推荐歌曲列表。这个页面是用户发现新歌的重要入口,设计上需要突出日期信息和推荐歌曲。

核心技术点

本篇涉及的核心技术包括:CustomScrollView和Sliver组件、SliverAppBar可折叠头部、LinearGradient渐变背景、SliverList歌曲列表、DateTime日期处理。

对应代码文件

lib/pages/daily/daily_recommend_page.dart

完整代码实现

import 'package:flutter/material.dart';

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

  
  Widget build(BuildContext context) {
    // 获取当前日期
    final now = DateTime.now();
    
    return Scaffold(
      body: CustomScrollView(
        slivers: [
          // 可折叠头部
          _buildSliverAppBar(now),
          // 操作栏
          _buildActionBar(),
          // 推荐歌曲列表
          _buildSongList(),
        ],
      ),
    );
  }

这段代码导入了Flutter核心库。DailyRecommendPage继承StatelessWidget,因为页面不需要管理内部状态。build方法中使用DateTime.now()获取当前日期。CustomScrollView是实现复杂滚动效果的核心组件。

  /// 构建可折叠头部
  Widget _buildSliverAppBar(DateTime now) {
    return SliverAppBar(
      expandedHeight: 220,
      pinned: true,
      flexibleSpace: FlexibleSpaceBar(
        background: _buildHeaderBackground(now),
      ),
    );
  }

SliverAppBar设置展开高度220像素,pinned为true表示折叠后固定在顶部。FlexibleSpaceBar定义可折叠区域的内容,当用户向上滚动时,头部会逐渐折叠。

  /// 构建头部背景
  Widget _buildHeaderBackground(DateTime now) {
    return Container(
      decoration: const BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [
            Color(0xFFE91E63),
            Color(0xFF9C27B0),
          ],
        ),
      ),
      child: SafeArea(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const SizedBox(height: 40),
            // 日期数字
            _buildDateNumber(now),
            // 月份
            _buildMonthText(now),
            const SizedBox(height: 8),
            // 说明文字
            _buildDescriptionText(),
          ],
        ),
      ),
    );
  }

Container使用LinearGradient渐变背景,从粉色(E91E63)过渡到紫色(9C27B0),斜向渐变让视觉效果更加动感。SafeArea确保内容不被系统状态栏遮挡。Column垂直排列日期信息。

  /// 构建日期数字
  Widget _buildDateNumber(DateTime now) {
    return Text(
      '${now.day}',
      style: const TextStyle(
        color: Colors.white,
        fontSize: 60,
        fontWeight: FontWeight.bold,
        shadows: [
          Shadow(
            color: Colors.black26,
            offset: Offset(2, 2),
            blurRadius: 4,
          ),
        ],
      ),
    );
  }

日期数字使用60像素超大白色粗体字突出显示,这是每日推荐页面的标志性视觉元素。添加阴影效果让文字更有立体感,在渐变背景上更加清晰。

  /// 构建月份文字
  Widget _buildMonthText(DateTime now) {
    return Text(
      '${now.month}月',
      style: const TextStyle(
        color: Colors.white70,
        fontSize: 18,
      ),
    );
  }

  /// 构建说明文字
  Widget _buildDescriptionText() {
    return const Text(
      '根据你的音乐口味生成',
      style: TextStyle(
        color: Colors.white54,
        fontSize: 14,
      ),
    );
  }

月份使用18像素白色70%透明度作为辅助信息,说明文字使用白色54%透明度,作为最次要的信息。这种透明度层次让信息主次分明,用户一眼就能看出这是每日推荐功能。

  /// 构建操作栏
  Widget _buildActionBar() {
    return SliverToBoxAdapter(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          children: [
            // 播放全部按钮
            Expanded(
              child: _buildPlayAllButton(),
            ),
            const SizedBox(width: 12),
            // 收藏按钮
            _buildIconButton(Icons.favorite_border),
            // 下载按钮
            _buildIconButton(Icons.download),
          ],
        ),
      ),
    );
  }

SliverToBoxAdapter将普通Widget转换为Sliver,这是在CustomScrollView中使用普通组件的方式。操作栏使用Row水平排列按钮,Expanded让播放全部按钮占据主要位置。

  /// 构建播放全部按钮
  Widget _buildPlayAllButton() {
    return ElevatedButton.icon(
      onPressed: () {
        // 播放全部歌曲
      },
      icon: const Icon(Icons.play_arrow),
      label: const Text('播放全部'),
      style: ElevatedButton.styleFrom(
        backgroundColor: const Color(0xFFE91E63),
        foregroundColor: Colors.white,
        padding: const EdgeInsets.symmetric(vertical: 12),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(25),
        ),
      ),
    );
  }

播放全部使用ElevatedButton.icon,背景色使用粉色主题色。padding设置垂直内边距让按钮更高,shape设置圆角让按钮呈胶囊形状。这是最常用的操作,所以占据最大空间。

  /// 构建图标按钮
  Widget _buildIconButton(IconData icon) {
    return Container(
      decoration: BoxDecoration(
        shape: BoxShape.circle,
        border: Border.all(
          color: Colors.grey.withOpacity(0.3),
        ),
      ),
      child: IconButton(
        icon: Icon(icon),
        onPressed: () {},
        color: Colors.grey,
      ),
    );
  }

收藏和下载使用带边框的圆形IconButton,视觉上与播放全部按钮形成对比。Container添加圆形边框装饰,让按钮更加精致。

  /// 构建推荐歌曲列表
  Widget _buildSongList() {
    return SliverList(
      delegate: SliverChildBuilderDelegate(
        (context, index) => _buildSongItem(index),
        childCount: 30,
      ),
    );
  }

SliverList构建推荐歌曲列表,SliverChildBuilderDelegate实现懒加载,只构建可见区域的子项。childCount设为30,表示每日推荐包含30首歌曲。

  /// 构建单个歌曲项
  Widget _buildSongItem(int index) {
    return ListTile(
      leading: _buildSongCover(index),
      title: Text(
        '每日推荐 ${index + 1}',
        style: const TextStyle(
          fontSize: 16,
          fontWeight: FontWeight.w400,
        ),
      ),
      subtitle: const Text(
        '歌手名称',
        style: TextStyle(
          color: Colors.grey,
          fontSize: 13,
        ),
      ),
      trailing: _buildPlayButton(),
      onTap: () {
        // 播放歌曲
      },
    );
  }

ListTile是Flutter提供的列表项组件,leading放置歌曲封面,title显示歌曲名称,subtitle显示歌手名。trailing放置播放按钮,onTap处理点击事件。

  /// 构建歌曲封面
  Widget _buildSongCover(int index) {
    return Container(
      width: 50,
      height: 50,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(8),
        color: Colors.primaries[index % Colors.primaries.length]
            .withOpacity(0.3),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.1),
            blurRadius: 4,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      child: const Icon(
        Icons.music_note,
        color: Colors.white70,
      ),
    );
  }

封面使用50x50像素的圆角容器,背景色从primaries颜色列表循环取值。添加阴影效果增加层次感,中央显示音乐图标作为占位。

  /// 构建播放按钮
  Widget _buildPlayButton() {
    return const Icon(
      Icons.play_circle_outline,
      color: Color(0xFFE91E63),
      size: 32,
    );
  }
}

播放按钮使用粉色主题色突出显示,size设为32像素让按钮更容易点击。play_circle_outline图标是播放按钮的标准样式。

CustomScrollView和Sliver组件详解

CustomScrollView是Flutter中实现复杂滚动效果的核心组件,它的children必须是Sliver类型的组件:

// CustomScrollView基本结构
CustomScrollView(
  slivers: [
    SliverAppBar(...),        // 可折叠头部
    SliverToBoxAdapter(...),  // 普通Widget转Sliver
    SliverList(...),          // 列表
    SliverGrid(...),          // 网格
  ],
)

常用的Sliver组件包括SliverAppBar、SliverList、SliverGrid、SliverToBoxAdapter等。这种组合可以实现头部可折叠、列表滚动等复杂效果。

SliverAppBar可折叠头部

SliverAppBar是一个可以随滚动折叠的AppBar,提供了丰富的配置选项:

// SliverAppBar配置详解
SliverAppBar(
  expandedHeight: 220,  // 展开时的高度
  pinned: true,         // 折叠后是否固定在顶部
  floating: false,      // 向下滚动时是否立即显示
  snap: false,          // 是否有吸附效果
  flexibleSpace: FlexibleSpaceBar(
    background: // 背景内容
  ),
)

expandedHeight设置展开时的高度,pinned为true表示折叠后固定在顶部。FlexibleSpaceBar定义可折叠区域的内容。

LinearGradient渐变背景

LinearGradient用于创建线性渐变效果,是实现炫酷背景的常用方式:

// 渐变背景配置
decoration: const BoxDecoration(
  gradient: LinearGradient(
    begin: Alignment.topLeft,     // 渐变起点
    end: Alignment.bottomRight,   // 渐变终点
    colors: [
      Color(0xFFE91E63),          // 起始颜色(粉色)
      Color(0xFF9C27B0),          // 结束颜色(紫色)
    ],
  ),
)

begin和end定义渐变方向,从左上到右下的斜向渐变让视觉效果更加动感。colors数组定义渐变颜色。

SliverList列表实现

SliverList用于在CustomScrollView中创建列表,配合SliverChildBuilderDelegate实现懒加载:

// SliverList基本用法
SliverList(
  delegate: SliverChildBuilderDelegate(
    (context, index) {
      return ListTile(...);
    },
    childCount: 30,
  ),
)

SliverChildBuilderDelegate只构建可见区域的子项,对于长列表性能更好。childCount指定列表项总数。

DateTime日期处理

Flutter使用DateTime类处理日期和时间:

// 获取当前日期
final now = DateTime.now();

// 获取日期各部分
int year = now.year;    // 年
int month = now.month;  // 月
int day = now.day;      // 日

DateTime.now()获取当前日期时间,通过属性可以获取年、月、日等各部分。在每日推荐页面中,我们使用day和month来显示当前日期。

SliverToBoxAdapter说明

SliverToBoxAdapter用于将普通Widget转换为Sliver,这样就可以在CustomScrollView中使用:

// 将普通Widget转换为Sliver
SliverToBoxAdapter(
  child: Padding(
    padding: const EdgeInsets.all(16),
    child: Row(...),
  ),
)

这是在CustomScrollView中使用非Sliver组件的标准方式,比如操作栏、分割线等。

小结

本篇实现了音乐播放器的每日推荐页面。使用CustomScrollView和Sliver组件实现头部可折叠效果,渐变背景和大字号日期突出每日推荐的特点。SliverList实现歌曲列表的高效渲染,ListTile简化列表项构建。这种设计在各大音乐App中都很常见,掌握这些技术可以实现各种复杂的滚动界面效果。


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

Logo

更多推荐