🚀运行效果展示

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Flutter框架跨平台鸿蒙开发——音乐排行榜APP的开发流程

前言

在移动应用开发领域,跨平台技术已经成为了一种趋势。Flutter作为Google推出的开源UI工具包,以其高效的开发效率和出色的性能表现,成为了众多开发者的首选。本文将详细介绍如何使用Flutter框架开发一款跨平台的音乐排行榜APP,并将其部署到鸿蒙系统上。

应用介绍

音乐排行榜APP是一款专注于音乐排行榜展示和音乐播放的应用,用户可以通过该应用查看各种音乐排行榜(如飙升榜、新歌榜、流行榜等),了解最新的音乐趋势,收听热门歌曲,并收藏自己喜欢的音乐。

核心功能

  • 排行榜列表展示:展示多种类型的音乐排行榜
  • 排行榜详情:查看具体排行榜的歌曲列表和排名
  • 音乐详情:查看音乐的详细信息,包括封面、标题、艺术家、专辑等
  • 音乐播放:内置音乐播放器,支持播放、暂停、进度调整等功能
  • 搜索功能:支持搜索歌曲和歌手
  • 收藏功能:支持收藏和取消收藏音乐
  • 响应式布局:适配不同屏幕尺寸的设备

开发环境搭建

所需工具

  • Flutter SDK:最新版本
  • DevEco Studio:用于鸿蒙应用开发
  • Android Studio:用于Flutter开发和调试
  • Visual Studio Code:代码编辑器

环境配置

  1. 安装Flutter SDK并配置环境变量
  2. 安装DevEco Studio并配置鸿蒙开发环境
  3. 在Flutter项目中添加鸿蒙平台支持
  4. 配置项目依赖

核心功能实现及代码展示

1. 数据模型层

首先,我们需要定义音乐和排行榜的数据模型,用于存储和管理数据。

音乐模型(Music)
/// 音乐模型类
/// 定义音乐的基本属性,包括标题、艺术家、专辑、时长、播放链接等
class Music {
  /// 音乐ID
  final String id;

  /// 音乐标题
  final String title;

  /// 艺术家
  final String artist;

  /// 专辑名称
  final String album;

  /// 专辑封面URL
  final String coverUrl;

  /// 音乐时长(秒)
  final int duration;

  /// 播放链接
  final String playUrl;

  /// 歌词
  final String? lyrics;

  /// 播放次数
  final int playCount;

  /// 发布日期
  final String? releaseDate;

  /// 是否收藏
  bool isFavorite;

  /// 构造函数
  Music({
    required this.id,
    required this.title,
    required this.artist,
    required this.album,
    required this.coverUrl,
    required this.duration,
    required this.playUrl,
    this.lyrics,
    required this.playCount,
    this.releaseDate,
    this.isFavorite = false,
  });

  /// 从JSON创建Music实例
  factory Music.fromJson(Map<String, dynamic> json) {
    return Music(
      id: json['id'] ?? '',
      title: json['title'] ?? '',
      artist: json['artist'] ?? '',
      album: json['album'] ?? '',
      coverUrl: json['coverUrl'] ?? '',
      duration: json['duration'] ?? 0,
      playUrl: json['playUrl'] ?? '',
      lyrics: json['lyrics'],
      playCount: json['playCount'] ?? 0,
      releaseDate: json['releaseDate'],
      isFavorite: json['isFavorite'] ?? false,
    );
  }

  /// 转换为JSON
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'title': title,
      'artist': artist,
      'album': album,
      'coverUrl': coverUrl,
      'duration': duration,
      'playUrl': playUrl,
      'lyrics': lyrics,
      'playCount': playCount,
      'releaseDate': releaseDate,
      'isFavorite': isFavorite,
    };
  }

  /// 格式化时长为 mm:ss 格式
  String get formattedDuration {
    final minutes = duration ~/ 60;
    final seconds = duration % 60;
    return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
  }

  /// 格式化播放次数
  String get formattedPlayCount {
    if (playCount >= 100000000) {
      return '${(playCount / 100000000).toStringAsFixed(1)}亿';
    } else if (playCount >= 10000) {
      return '${(playCount / 10000).toStringAsFixed(1)}万';
    } else {
      return playCount.toString();
    }
  }
}
排行榜模型(MusicChart)
/// 音乐排行榜模型类
/// 定义排行榜的基本属性,包括名称、类型、音乐列表等
import 'music_model.dart';

class MusicChart {
  /// 排行榜ID
  final String id;

  /// 排行榜名称
  final String name;

  /// 排行榜类型
  final String type;

  /// 排行榜描述
  final String? description;

  /// 排行榜封面URL
  final String coverUrl;

  /// 音乐列表
  final List<Music> songs;

  /// 更新时间
  final String updateTime;

  /// 排行榜周期(如:日榜、周榜、月榜)
  final String period;

  /// 构造函数
  MusicChart({
    required this.id,
    required this.name,
    required this.type,
    this.description,
    required this.coverUrl,
    required this.songs,
    required this.updateTime,
    required this.period,
  });

  /// 从JSON创建MusicChart实例
  factory MusicChart.fromJson(Map<String, dynamic> json) {
    final songsJson = json['songs'] as List<dynamic>? ?? [];
    final songs = songsJson.map((songJson) => Music.fromJson(songJson)).toList();

    return MusicChart(
      id: json['id'] ?? '',
      name: json['name'] ?? '',
      type: json['type'] ?? '',
      description: json['description'],
      coverUrl: json['coverUrl'] ?? '',
      songs: songs,
      updateTime: json['updateTime'] ?? '',
      period: json['period'] ?? '',
    );
  }

  /// 转换为JSON
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'type': type,
      'description': description,
      'coverUrl': coverUrl,
      'songs': songs.map((song) => song.toJson()).toList(),
      'updateTime': updateTime,
      'period': period,
    };
  }
}

2. 网络请求和数据获取

为了获取音乐数据,我们需要实现网络请求和数据获取逻辑。由于是模拟环境,我们使用了模拟数据。

/// 音乐服务类
/// 处理音乐相关的网络请求和数据获取
import '../models/music_model.dart';
import '../models/music_chart_model.dart';

class MusicService {
  /// 获取排行榜列表
  Future<List<MusicChart>> getCharts() async {
    // 模拟网络请求延迟
    await Future.delayed(const Duration(milliseconds: 500));
    
    // 返回模拟的排行榜数据
    return mockCharts;
  }

  /// 根据ID获取排行榜详情
  Future<MusicChart?> getChartById(String chartId) async {
    // 模拟网络请求延迟
    await Future.delayed(const Duration(milliseconds: 300));
    
    // 从模拟数据中查找对应的排行榜
    return mockCharts.firstWhere((chart) => chart.id == chartId, orElse: () => mockCharts[0]);
  }

  /// 搜索音乐
  Future<List<Music>> searchMusic(String keyword) async {
    // 模拟网络请求延迟
    await Future.delayed(const Duration(milliseconds: 400));
    
    // 从模拟数据中搜索音乐
    final allSongs = mockCharts.expand((chart) => chart.songs).toList();
    return allSongs
        .where((song) => 
            song.title.toLowerCase().contains(keyword.toLowerCase()) ||
            song.artist.toLowerCase().contains(keyword.toLowerCase()))
        .toList();
  }

  /// 获取推荐音乐
  Future<List<Music>> getRecommendedMusic() async {
    // 模拟网络请求延迟
    await Future.delayed(const Duration(milliseconds: 300));
    
    // 从模拟数据中随机获取一些音乐作为推荐
    final allSongs = mockCharts.expand((chart) => chart.songs).toList();
    // 随机打乱并取前10首
    allSongs.shuffle();
    return allSongs.take(10).toList();
  }

  /// 切换音乐收藏状态
  Future<bool> toggleFavorite(String musicId) async {
    // 模拟网络请求延迟
    await Future.delayed(const Duration(milliseconds: 200));
    
    // 在模拟数据中切换收藏状态
    for (final chart in mockCharts) {
      for (final song in chart.songs) {
        if (song.id == musicId) {
          song.isFavorite = !song.isFavorite;
          return true;
        }
      }
    }
    return false;
  }

  /// 获取收藏的音乐
  Future<List<Music>> getFavoriteMusic() async {
    // 模拟网络请求延迟
    await Future.delayed(const Duration(milliseconds: 300));
    
    // 从模拟数据中获取收藏的音乐
    final allSongs = mockCharts.expand((chart) => chart.songs).toList();
    return allSongs.where((song) => song.isFavorite).toList();
  }
}

/// 模拟排行榜数据
final List<MusicChart> mockCharts = [
  MusicChart(
    id: '1',
    name: '飙升榜',
    type: 'hot',
    description: '实时更新的热门歌曲排行榜',
    coverUrl: 'https://p2.music.126.net/8e5tVqj4z2Y4sLOSL3GfCw==/109951168017805250.jpg',
    songs: [
      Music(
        id: '1001',
        title: '起风了',
        artist: '买辣椒也用券',
        album: '起风了',
        coverUrl: 'https://p2.music.126.net/8e5tVqj4z2Y4sLOSL3GfCw==/109951168017805250.jpg',
        duration: 263,
        playUrl: 'https://music.163.com/song/media/outer/url?id=1330871186.mp3',
        playCount: 1250000000,
        releaseDate: '2018-12-03',
      ),
      // 更多歌曲...
    ],
    updateTime: '2024-01-27 18:00:00',
    period: '日榜',
  ),
  // 更多排行榜...
];

3. 页面实现

排行榜列表页面

排行榜列表页面是应用的首页,展示各种类型的音乐排行榜。

/// 音乐排行榜列表页面
/// 展示各种音乐排行榜的列表
import 'package:flutter/material.dart';
import '../services/music_service.dart';
import '../models/music_chart_model.dart';
import 'music_chart_detail_page.dart';

class MusicChartListPage extends StatefulWidget {
  /// 构造函数
  const MusicChartListPage({super.key});

  
  State<MusicChartListPage> createState() => _MusicChartListPageState();
}

class _MusicChartListPageState extends State<MusicChartListPage> {
  final MusicService _musicService = MusicService();
  List<MusicChart> _charts = [];
  bool _isLoading = true;
  String _searchKeyword = '';

  
  void initState() {
    super.initState();
    _loadCharts();
  }

  /// 加载排行榜数据
  Future<void> _loadCharts() async {
    try {
      setState(() {
        _isLoading = true;
      });
      final charts = await _musicService.getCharts();
      setState(() {
        _charts = charts;
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _isLoading = false;
      });
      // 显示错误提示
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('加载排行榜失败')),
      );
    }
  }

  /// 搜索音乐
  Future<void> _searchMusic() async {
    if (_searchKeyword.isEmpty) return;
    
    try {
      setState(() {
        _isLoading = true;
      });
      // 这里可以跳转到搜索结果页面
      // 暂时先模拟搜索
      await Future.delayed(const Duration(milliseconds: 500));
      setState(() {
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('搜索失败')),
      );
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('音乐排行榜'),
        centerTitle: true,
        backgroundColor: Colors.blue,
      ),
      body: Column(
        children: [
          // 搜索栏
          Padding(
            padding: const EdgeInsets.all(12.0),
            child: TextField(
              onChanged: (value) {
                setState(() {
                  _searchKeyword = value;
                });
              },
              onSubmitted: (_) => _searchMusic(),
              decoration: InputDecoration(
                hintText: '搜索歌曲、歌手',
                prefixIcon: const Icon(Icons.search),
                suffixIcon: _searchKeyword.isNotEmpty
                    ? IconButton(
                        icon: const Icon(Icons.clear),
                        onPressed: () {
                          setState(() {
                            _searchKeyword = '';
                          });
                        },
                      )
                    : null,
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(20),
                ),
              ),
            ),
          ),
          
          // 排行榜列表
          Expanded(
            child: _isLoading
                ? const Center(child: CircularProgressIndicator())
                : ListView.builder(
                    itemCount: _charts.length,
                    itemBuilder: (context, index) {
                      final chart = _charts[index];
                      return _buildChartCard(chart);
                    },
                  ),
          ),
        ],
      ),
    );
  }

  /// 构建排行榜卡片
  Widget _buildChartCard(MusicChart chart) {
    return GestureDetector(
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => MusicChartDetailPage(chart: chart),
          ),
        );
      },
      child: Card(
        margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
        elevation: 4,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(12),
        ),
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Row(
            children: [
              // 排行榜封面
              ClipRRect(
                borderRadius: BorderRadius.circular(8),
                child: Image.network(
                  chart.coverUrl,
                  width: 80,
                  height: 80,
                  fit: BoxFit.cover,
                ),
              ),
              
              // 排行榜信息
              Expanded(
                child: Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 16),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        chart.name,
                        style: const TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      const SizedBox(height: 4),
                      Text(
                        chart.description ?? '',
                        style: TextStyle(
                          fontSize: 14,
                          color: Colors.grey[600],
                        ),
                        maxLines: 2,
                        overflow: TextOverflow.ellipsis,
                      ),
                      const SizedBox(height: 8),
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          Text(
                            '${chart.period} · ${chart.songs.length}首歌曲',
                            style: TextStyle(
                              fontSize: 12,
                              color: Colors.grey[500],
                            ),
                          ),
                          Text(
                            '更新于 ${chart.updateTime}',
                            style: TextStyle(
                              fontSize: 12,
                              color: Colors.grey[500],
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
              
              // 箭头图标
              const Icon(Icons.arrow_forward_ios, color: Colors.grey),
            ],
          ),
        ),
      ),
    );
  }
}
排行榜详情页面

排行榜详情页面展示具体排行榜的歌曲列表和排名。

/// 音乐排行榜详情页面
/// 展示具体排行榜的音乐列表
import 'package:flutter/material.dart';
import '../models/music_chart_model.dart';
import '../models/music_model.dart';
import '../services/music_service.dart';
import 'music_detail_page.dart';

class MusicChartDetailPage extends StatefulWidget {
  /// 排行榜数据
  final MusicChart chart;

  /// 构造函数
  const MusicChartDetailPage({super.key, required this.chart});

  
  State<MusicChartDetailPage> createState() => _MusicChartDetailPageState();
}

class _MusicChartDetailPageState extends State<MusicChartDetailPage> {
  final MusicService _musicService = MusicService();
  bool _isLoading = false;

  /// 切换音乐收藏状态
  Future<void> _toggleFavorite(String musicId) async {
    try {
      setState(() {
        _isLoading = true;
      });
      await _musicService.toggleFavorite(musicId);
      setState(() {
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('操作失败')),
      );
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.chart.name),
        centerTitle: true,
        backgroundColor: Colors.blue,
      ),
      body: Column(
        children: [
          // 排行榜头部信息
          Container(
            padding: const EdgeInsets.all(16),
            color: Colors.grey[50],
            child: Row(
              children: [
                // 排行榜封面
                ClipRRect(
                  borderRadius: BorderRadius.circular(8),
                  child: Image.network(
                    widget.chart.coverUrl,
                    width: 100,
                    height: 100,
                    fit: BoxFit.cover,
                  ),
                ),
                
                // 排行榜信息
                Expanded(
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          widget.chart.name,
                          style: const TextStyle(
                            fontSize: 20,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                        const SizedBox(height: 4),
                        Text(
                          widget.chart.description ?? '',
                          style: TextStyle(
                            fontSize: 14,
                            color: Colors.grey[600],
                          ),
                          maxLines: 2,
                          overflow: TextOverflow.ellipsis,
                        ),
                        const SizedBox(height: 8),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            Text(
                              '${widget.chart.period} · ${widget.chart.songs.length}首歌曲',
                              style: TextStyle(
                                fontSize: 12,
                                color: Colors.grey[500],
                              ),
                            ),
                            Text(
                              '更新于 ${widget.chart.updateTime}',
                              style: TextStyle(
                                fontSize: 12,
                                color: Colors.grey[500],
                              ),
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ),
          
          // 歌曲列表
          Expanded(
            child: ListView.builder(
              itemCount: widget.chart.songs.length,
              itemBuilder: (context, index) {
                final song = widget.chart.songs[index];
                return _buildSongItem(song, index + 1);
              },
            ),
          ),
        ],
      ),
    );
  }

  /// 构建歌曲列表项
  Widget _buildSongItem(Music song, int rank) {
    return GestureDetector(
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => MusicDetailPage(music: song),
          ),
        );
      },
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
        decoration: BoxDecoration(
          border: Border(bottom: BorderSide(color: Colors.grey[200]!)),
        ),
        child: Row(
          children: [
            // 排名
            Container(
              width: 32,
              alignment: Alignment.center,
              child: Text(
                '$rank',
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                  color: rank <= 3 ? Colors.red : Colors.grey[600],
                ),
              ),
            ),
            
            // 歌曲封面
            ClipRRect(
              borderRadius: BorderRadius.circular(4),
              child: Image.network(
                song.coverUrl,
                width: 50,
                height: 50,
                fit: BoxFit.cover,
              ),
            ),
            
            // 歌曲信息
            Expanded(
              child: Padding(
                padding: const EdgeInsets.symmetric(horizontal: 12),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      song.title,
                      style: const TextStyle(
                        fontSize: 16,
                        fontWeight: FontWeight.w500,
                      ),
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                    ),
                    const SizedBox(height: 4),
                    Row(
                      children: [
                        Text(
                          song.artist,
                          style: TextStyle(
                            fontSize: 14,
                            color: Colors.grey[600],
                          ),
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                        ),
                        const SizedBox(width: 8),
                        Text(
                          '-',
                          style: TextStyle(
                            fontSize: 14,
                            color: Colors.grey[400],
                          ),
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: Text(
                            song.album,
                            style: TextStyle(
                              fontSize: 14,
                              color: Colors.grey[600],
                            ),
                            maxLines: 1,
                            overflow: TextOverflow.ellipsis,
                          ),
                        ),
                      ],
                    ),
                    const SizedBox(height: 4),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        Text(
                          song.formattedDuration,
                          style: TextStyle(
                            fontSize: 12,
                            color: Colors.grey[500],
                          ),
                        ),
                        Text(
                          '${song.formattedPlayCount}次播放',
                          style: TextStyle(
                            fontSize: 12,
                            color: Colors.grey[500],
                          ),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
            
            // 收藏按钮
            IconButton(
              icon: Icon(
                song.isFavorite ? Icons.favorite : Icons.favorite_border,
                color: song.isFavorite ? Colors.red : Colors.grey,
              ),
              onPressed: () => _toggleFavorite(song.id),
            ),
          ],
        ),
      ),
    );
  }
}
音乐详情页面

音乐详情页面展示音乐的详细信息,包括封面、标题、艺术家、专辑、歌词等,以及音乐播放器。

/// 音乐详情页面
/// 展示音乐的详细信息,包括封面、标题、艺术家、专辑、歌词等
import 'package:flutter/material.dart';
import '../models/music_model.dart';
import '../services/music_service.dart';

class MusicDetailPage extends StatefulWidget {
  /// 音乐数据
  final Music music;

  /// 构造函数
  const MusicDetailPage({super.key, required this.music});

  
  State<MusicDetailPage> createState() => _MusicDetailPageState();
}

class _MusicDetailPageState extends State<MusicDetailPage> {
  final MusicService _musicService = MusicService();
  bool _isLoading = false;
  bool _isPlaying = false;
  double _currentPosition = 0;

  /// 切换音乐收藏状态
  Future<void> _toggleFavorite() async {
    try {
      setState(() {
        _isLoading = true;
      });
      await _musicService.toggleFavorite(widget.music.id);
      setState(() {
        widget.music.isFavorite = !widget.music.isFavorite;
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('操作失败')),
      );
    }
  }

  /// 切换播放状态
  void _togglePlay() {
    setState(() {
      _isPlaying = !_isPlaying;
    });
  }

  /// 更新播放进度
  void _updatePosition(double value) {
    setState(() {
      _currentPosition = value;
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('音乐详情'),
        centerTitle: true,
        backgroundColor: Colors.blue,
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            // 音乐封面
            Container(
              padding: const EdgeInsets.all(32),
              child: ClipRRect(
                borderRadius: BorderRadius.circular(16),
                child: Image.network(
                  widget.music.coverUrl,
                  width: 280,
                  height: 280,
                  fit: BoxFit.cover,
                ),
              ),
            ),
            
            // 音乐信息
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 32),
              child: Column(
                children: [
                  Text(
                    widget.music.title,
                    style: const TextStyle(
                      fontSize: 24,
                      fontWeight: FontWeight.bold,
                    ),
                    textAlign: TextAlign.center,
                  ),
                  const SizedBox(height: 8),
                  Text(
                    widget.music.artist,
                    style: TextStyle(
                      fontSize: 18,
                      color: Colors.grey[600],
                    ),
                    textAlign: TextAlign.center,
                  ),
                  const SizedBox(height: 4),
                  Text(
                    widget.music.album,
                    style: TextStyle(
                      fontSize: 14,
                      color: Colors.grey[500],
                    ),
                    textAlign: TextAlign.center,
                  ),
                  const SizedBox(height: 16),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Text(
                        widget.music.formattedDuration,
                        style: TextStyle(
                          fontSize: 14,
                          color: Colors.grey[500],
                        ),
                      ),
                      const SizedBox(width: 16),
                      Text(
                        '${widget.music.formattedPlayCount}次播放',
                        style: TextStyle(
                          fontSize: 14,
                          color: Colors.grey[500],
                        ),
                      ),
                      if (widget.music.releaseDate != null)
                        const SizedBox(width: 16),
                      if (widget.music.releaseDate != null)
                        Text(
                          '${widget.music.releaseDate}',
                          style: TextStyle(
                            fontSize: 14,
                            color: Colors.grey[500],
                          ),
                        ),
                    ],
                  ),
                ],
              ),
            ),
            
            // 操作按钮
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  IconButton(
                    icon: Icon(
                      widget.music.isFavorite ? Icons.favorite : Icons.favorite_border,
                      color: widget.music.isFavorite ? Colors.red : Colors.grey,
                      size: 32,
                    ),
                    onPressed: _toggleFavorite,
                  ),
                  const SizedBox(width: 32),
                  IconButton(
                    icon: Icon(
                      Icons.share,
                      color: Colors.grey[600],
                      size: 32,
                    ),
                    onPressed: () {
                      // 分享功能
                      ScaffoldMessenger.of(context).showSnackBar(
                        const SnackBar(content: Text('分享功能开发中')),
                      );
                    },
                  ),
                  const SizedBox(width: 32),
                  IconButton(
                    icon: Icon(
                      Icons.more_vert,
                      color: Colors.grey[600],
                      size: 32,
                    ),
                    onPressed: () {
                      // 更多功能
                      ScaffoldMessenger.of(context).showSnackBar(
                        const SnackBar(content: Text('更多功能开发中')),
                      );
                    },
                  ),
                ],
              ),
            ),
            
            // 播放器
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
              color: Colors.grey[50],
              child: Column(
                children: [
                  // 播放进度条
                  Slider(
                    value: _currentPosition,
                    onChanged: _updatePosition,
                    min: 0,
                    max: widget.music.duration.toDouble(),
                    activeColor: Colors.blue,
                    inactiveColor: Colors.grey[300],
                  ),
                  
                  // 时间显示
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Text(
                        '${(_currentPosition ~/ 60).toString().padLeft(2, '0')}:${(_currentPosition % 60).toString().padLeft(2, '0')}',
                        style: TextStyle(
                          fontSize: 12,
                          color: Colors.grey[500],
                        ),
                      ),
                      Text(
                        widget.music.formattedDuration,
                        style: TextStyle(
                          fontSize: 12,
                          color: Colors.grey[500],
                        ),
                      ),
                    ],
                  ),
                  
                  // 播放控制按钮
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      IconButton(
                        icon: const Icon(Icons.skip_previous, size: 32),
                        onPressed: () {
                          // 上一曲
                        },
                      ),
                      const SizedBox(width: 32),
                      IconButton(
                        icon: Icon(
                          _isPlaying ? Icons.pause_circle : Icons.play_circle,
                          size: 64,
                          color: Colors.blue,
                        ),
                        onPressed: _togglePlay,
                      ),
                      const SizedBox(width: 32),
                      IconButton(
                        icon: const Icon(Icons.skip_next, size: 32),
                        onPressed: () {
                          // 下一曲
                        },
                      ),
                    ],
                  ),
                ],
              ),
            ),
            
            // 歌词
            if (widget.music.lyrics != null && widget.music.lyrics!.isNotEmpty)
              Padding(
                padding: const EdgeInsets.all(32),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text(
                      '歌词',
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 16),
                    Text(
                      widget.music.lyrics!,
                      style: TextStyle(
                        fontSize: 14,
                        color: Colors.grey[600],
                        height: 1.5,
                      ),
                    ),
                  ],
                ),
              ),
          ],
        ),
      ),
    );
  }
}

4. 路由配置

最后,我们需要配置应用的路由,将音乐排行榜相关页面添加到应用中。

/// 应用入口文件
/// 配置应用路由和主题
import 'package:flutter/material.dart';
import 'pages/music_chart_list_page.dart';

void main() {
  runApp(const MyApp());
}

/// 应用主类
class MyApp extends StatelessWidget {
  /// 构造函数
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '音乐排行榜',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: const MusicChartListPage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

开发流程图

核心功能

项目初始化

环境配置

数据模型设计

网络请求实现

页面开发

路由配置

应用测试

鸿蒙部署

音乐模型

排行榜模型

排行榜数据

音乐详情数据

排行榜列表页面

排行榜详情页面

音乐详情页面

播放器功能

技术亮点

  1. 响应式布局:使用Flutter的响应式布局,确保应用在不同屏幕尺寸的设备上都能正常显示。

  2. 模块化设计:将应用分为数据模型层、网络请求层、页面层等多个模块,提高了代码的可维护性和可扩展性。

  3. 优雅的UI设计:采用现代化的UI设计,包括卡片式布局、圆角设计、渐变色等,提升了用户体验。

  4. 流畅的动画效果:使用Flutter的动画系统,实现了平滑的页面切换和交互效果。

  5. 跨平台兼容性:利用Flutter的跨平台特性,使应用能够在Android、iOS、Web和鸿蒙等多个平台上运行。

总结

通过本文的介绍,我们详细了解了如何使用Flutter框架开发一款跨平台的音乐排行榜APP,并将其部署到鸿蒙系统上。从数据模型设计到页面开发,从网络请求到路由配置,我们完成了一个功能完整的音乐排行榜应用。

Flutter作为一款优秀的跨平台开发框架,不仅提高了开发效率,还保证了应用的性能和用户体验。相信在未来,Flutter将会在跨平台开发领域发挥更加重要的作用,为开发者带来更多的便利和可能性。

后续优化方向

  1. 实真实的网络请求:接入真实的音乐API,获取实时的音乐数据。

  2. 增强播放器功能:添加更多播放器功能,如歌词同步、音质调节、播放模式切换等。

  3. 添加用户系统:实现用户注册、登录、个人中心等功能。

  4. 优化性能:进一步优化应用性能,减少内存使用和加载时间。

  5. 添加更多社交功能:如评论、分享、歌单等。

通过不断的优化和迭代,我们可以打造一款更加完善、更加用户友好的音乐排行榜APP。

参考资料


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

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐