在这里插入图片描述

发现音乐是用户探索新音乐的核心入口,聚合了分类导航、热门话题、新歌推荐等多元化内容,为用户打造沉浸式的音乐发现体验。该页面需兼顾内容展示的丰富性与操作的便捷性,是音乐App提升用户留存的关键模块。本篇将完整实现发现页面的功能与布局。

功能分析

发现页面核心功能包含:分类入口网格(歌单、歌手、专辑等6大核心模块)、热门话题标签云(流式布局展示音乐圈热点)、新歌速递列表(展示最新上线歌曲)。页面需实现整体滚动,各子模块自适应布局,同时保证点击跳转的交互完整性。

核心技术点

本篇涉及的核心技术包括:GridView网格布局实现分类入口、Wrap流式布局构建标签云、ListView列表展示歌曲、嵌套滚动冲突处理、GetX路由导航、数据驱动的UI构建方式。

对应代码文件

lib/pages/discover/discover_page.dart

完整代码实现

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../playlist/playlist_list_page.dart';
import '../artist/artist_list_page.dart';
import '../album/album_list_page.dart';
import '../ranking/ranking_page.dart';
import '../mv/mv_list_page.dart';
import '../radio/radio_page.dart';

这段代码导入了Flutter核心组件库、GetX路由管理库,以及分类入口对应的歌单、歌手、专辑等页面引用。GetX提供轻量且高效的路由跳转能力,无需依赖BuildContext即可完成页面导航。

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('发现音乐')),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            _buildCategoryGrid(),
            const SizedBox(height: 24),
            _buildHotTopics(),
            const SizedBox(height: 24),
            _buildNewSongs(),
            const SizedBox(height: 100),
          ],
        ),
      ),
    );
  }

DiscoverPage继承StatelessWidget,页面无内部状态管理需求。Scaffold作为页面根容器,AppBar展示“发现音乐”标题。SingleChildScrollView包裹Column实现页面整体滚动,16像素内边距保证内容与屏幕边缘的间距,各模块间通过SizedBox设置24像素间距,底部预留100像素避免被迷你播放器遮挡。

  /// 构建分类入口网格
  Widget _buildCategoryGrid() {
    final categories = [
      {'icon': Icons.queue_music, 'label': '歌单', 'page': () => const PlaylistListPage()},
      {'icon': Icons.people, 'label': '歌手', 'page': () => const ArtistListPage()},
      {'icon': Icons.album, 'label': '专辑', 'page': () => const AlbumListPage()},
      {'icon': Icons.leaderboard, 'label': '排行榜', 'page': () => const RankingPage()},
      {'icon': Icons.videocam, 'label': 'MV', 'page': () => const MVListPage()},
      {'icon': Icons.radio, 'label': '电台', 'page': () => const RadioPage()},
    ];

_buildCategoryGrid方法构建分类入口网格。categories列表以Map形式存储每个分类的图标、名称、跳转页面构造函数,采用数据驱动模式,后续新增/修改分类仅需调整数据,无需改动UI逻辑。

    return GridView.builder(
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 3, 
        childAspectRatio: 1.2, 
        crossAxisSpacing: 16, 
        mainAxisSpacing: 16
      ),
      itemCount: categories.length,
      itemBuilder: (context, index) {
        final cat = categories[index];
        return GestureDetector(
          onTap: () => Get.to(cat['page'] as Widget Function()),
          child: Container(
            decoration: BoxDecoration(
              color: Theme.of(context).cardColor, 
              borderRadius: BorderRadius.circular(12)
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Icon(
                  cat['icon'] as IconData, 
                  size: 32, 
                  color: const Color(0xFFE91E63)
                ),
                const SizedBox(height: 8),
                Text(cat['label'] as String),
              ],
            ),
          ),
        );
      },
    );
  }

GridView.builder实现网格布局,shrinkWrap设为true让网格高度自适应内容,NeverScrollableScrollPhysics禁用网格自身滚动,避免与外层SingleChildScrollView产生滚动冲突。SliverGridDelegateWithFixedCrossAxisCount配置网格规则:3列布局、子项宽高比1.2、列间距/行间距16像素。

itemBuilder遍历categories生成每个分类项:GestureDetector实现点击跳转,Container设置卡片背景色与12像素圆角,Column垂直居中展示图标(32像素粉色主题色)与文字标签,图标与文字间8像素间距保证视觉层次。

  /// 构建热门话题标签云
  Widget _buildHotTopics() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          '热门话题', 
          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)
        ),
        const SizedBox(height: 12),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: List.generate(
            8, 
            (i) => Chip(
              label: Text('话题 ${i + 1}'), 
              backgroundColor: const Color(0xFFE91E63).withOpacity(0.1)
            )
          ),
        ),
      ],
    );
  }

_buildHotTopics方法构建热门话题模块。标题“热门话题”采用18像素粗体,与标签云间12像素间距。Wrap实现流式布局,spacing(水平间距)、runSpacing(垂直间距)均设为8像素,自动适配屏幕宽度换行。List.generate生成8个话题标签,Chip组件作为标签容器,粉色10%透明度背景保证与主题风格统一。

  /// 构建新歌速递列表
  Widget _buildNewSongs() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          '新歌速递', 
          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)
        ),
        const SizedBox(height: 12),
        ListView.builder(
          shrinkWrap: true,
          physics: const NeverScrollableScrollPhysics(),
          itemCount: 5,
          itemBuilder: (context, index) => ListTile(
            leading: Container(
              width: 50, 
              height: 50, 
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(8), 
                color: Colors.primaries[index % Colors.primaries.length].withOpacity(0.3)
              ), 
              child: const Icon(Icons.music_note, color: Colors.white70)
            ),
            title: Text('新歌 ${index + 1}'),
            subtitle: const Text('歌手名'),
            trailing: const Icon(
              Icons.play_circle_outline, 
              color: Color(0xFFE91E63)
            ),
          ),
        ),
      ],
    );
  }
}

_buildNewSongs方法构建新歌速递列表。标题样式与热门话题保持一致,保证视觉统一性。ListView.builder构建5行新歌列表,同样设置shrinkWrap和禁用内部滚动。

每个ListTile的leading为50×50像素的封面容器,通过Colors.primaries[index % Colors.primaries.length]循环使用Flutter内置主色,30%透明度让颜色柔和不刺眼,配合8像素圆角与白色音符图标,提升视觉丰富度。title展示歌曲名,subtitle展示歌手名,trailing为粉色播放图标,明确交互指向。

网格布局设计详解

分类入口采用GridView.builder实现3列网格,childAspectRatio设置为1.2(宽高比),让每个分类项呈现“宽略大于高”的视觉效果,符合移动端卡片设计习惯。Theme.of(context).cardColor适配系统主题,保证不同主题模式下的视觉一致性。点击事件通过Get.to跳转到对应页面,无需传递context,简化路由逻辑。

流式布局与标签云实现

Wrap组件是实现标签云的最优选择,相比Row+SingleChildScrollView,Wrap可自动换行且无需处理横向滚动,更符合标签类内容的展示逻辑。Chip组件自带内边距和圆角,无需额外封装即可实现美观的标签样式,通过透明度调整背景色,平衡视觉突出度与整体协调性。

嵌套滚动冲突处理

在SingleChildScrollView中嵌套GridView/ListView时,必须设置shrinkWrap: true(让子列表高度自适应内容)和physics: NeverScrollableScrollPhysics()(禁用子列表滚动),否则会出现布局溢出或滚动冲突问题。这是Flutter嵌套滚动场景的标准解决方案,保证页面滚动体验的统一性。

色彩设计技巧

页面统一使用粉色(#E91E63)作为主题色,应用于图标、按钮、标签背景等核心交互元素,形成视觉焦点。新歌列表封面采用Flutter内置主色列表循环取值,既保证色彩多样性,又避免手动定义颜色的繁琐,同时通过透明度降低色彩饱和度,避免视觉疲劳。

小结

发现页面通过模块化拆分(分类网格、话题标签、新歌列表)实现清晰的代码结构,数据驱动的UI构建方式提升了代码可维护性。掌握GridView、Wrap、ListView的组合使用,以及嵌套滚动的处理技巧,能够应对大多数移动端列表/网格类页面的开发需求。页面整体风格统一,交互逻辑清晰,为用户提供了流畅的音乐发现体验。

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

Logo

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

更多推荐