Flutter Spotify克隆应用:点击歌曲卡片同步迷你播放器数据
实现Spotify克隆迷你播放器同步歌曲信息方案
核心思路
通过Navigator跳转时传递歌曲数据,从详情页返回时携带选中的歌曲信息,在主页的StatefulWidget中用setState更新迷你播放器的状态。
步骤1:定义歌曲数据模型
先创建统一的歌曲类管理信息:
class Song { final String id; final String title; final String artist; final String coverUrl; Song({ required this.id, required this.title, required this.artist, required this.coverUrl, }); }
步骤2:改造主页为StatefulWidget
主页需要维护当前选中歌曲的状态,因此改为StatefulWidget:
class HomePage extends StatefulWidget { const HomePage({super.key}); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { // 初始化当前播放歌曲(可设默认值或null) Song? currentPlayingSong; @override Widget build(BuildContext context) { return Scaffold( body: Column( children: [ // 歌曲Grid列表 Expanded( child: GridView.builder( padding: const EdgeInsets.all(16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 16, mainAxisSpacing: 16, ), itemCount: songsList.length, itemBuilder: (context, index) { final song = songsList[index]; return GridTile( child: GestureDetector( onTap: () async { // 跳转详情页并等待返回数据 final returnedSong = await Navigator.push( context, MaterialPageRoute( builder: (context) => SongDetailPage(song: song), ), ); // 返回有效数据时更新状态 if (returnedSong != null && returnedSong is Song) { setState(() { currentPlayingSong = returnedSong; }); } }, child: Image.network(song.coverUrl, fit: BoxFit.cover), ), footer: GridTileBar( title: Text(song.title), subtitle: Text(song.artist), ), ); }, ), ), // 迷你播放器(有选中歌曲时显示) if (currentPlayingSong != null) Container( height: 60, color: Colors.grey[800], padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( children: [ Image.network(currentPlayingSong!.coverUrl, width: 48, height: 48, fit: BoxFit.cover), const SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text(currentPlayingSong!.title, style: const TextStyle(color: Colors.white, fontSize: 14)), Text(currentPlayingSong!.artist, style: TextStyle(color: Colors.grey[400], fontSize: 12)), ], ), const Spacer(), IconButton(onPressed: () {}, icon: const Icon(Icons.play_arrow, color: Colors.white)), ], ), ), ], ), ); } // 模拟歌曲列表数据 final List<Song> songsList = [ Song( id: '1', title: 'Blinding Lights', artist: 'The Weeknd', coverUrl: 'https://example.com/cover1.jpg', ), Song( id: '2', title: 'Levitating', artist: 'Dua Lipa', coverUrl: 'https://example.com/cover2.jpg', ), ]; }
步骤3:实现歌曲详情页
详情页接收歌曲数据,返回时将数据回传给主页:
class SongDetailPage extends StatelessWidget { final Song song; const SongDetailPage({super.key, required this.song}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(song.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.network(song.coverUrl, width: 200, height: 200, fit: BoxFit.cover), const SizedBox(height: 24), Text(song.title, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), Text(song.artist, style: const TextStyle(fontSize: 18, color: Colors.grey)), const SizedBox(height: 32), ElevatedButton( onPressed: () { // 返回主页时携带当前歌曲数据 Navigator.pop(context, song); }, child: const Text('返回主页'), ), ], ), ), ); } }
关键知识点说明
- StatefulWidget:主页需要动态更新UI,因此必须用StatefulWidget,其内部的
State类负责维护可变状态(如currentPlayingSong)。 - setState:调用
setState(() {})会通知Flutter框架状态变更,触发页面重新构建,从而更新迷你播放器的内容。 - Navigator返回传值:通过
await Navigator.push(...)等待详情页返回的数据,拿到后更新主页状态,实现数据同步。
内容的提问来源于stack exchange,提问作者Warrior
相关产品推荐
相关产品推荐

