Flutter电台播放器切换页面时isPlaying布尔值重置问题求助
Flutter电台播放器页面切换后播放状态重置的解决方案
问题描述
我用Flutter开发一款电台播放器应用,通过GNav实现两个页面的切换。启动电台播放后,播放按钮会切换为暂停按钮,但切换页面再返回播放器页面时,音频明明还在播放,按钮却变回了播放按钮。排查后发现是每次切换页面都会重新执行bool isPlaying = false;,导致状态被重置。请问怎么让isPlaying在返回页面时不被重置?
解决方案
方法一:用AutomaticKeepAliveClientMixin保持页面状态
这个方法能让Home页面在切换时不被销毁,从而保留内部的状态变量。
修改home.dart的代码:
import 'package:flutter/material.dart'; import 'package:radio_player/radio_player.dart'; class Home extends StatefulWidget { Home({super.key}); @override State<Home> createState() => _HomeState(); } // 混入AutomaticKeepAliveClientMixin class _HomeState extends State<Home> with AutomaticKeepAliveClientMixin { RadioPlayer _radioPlayer = RadioPlayer(); List<String>? metadata; bool isPlaying = false; // 重写该方法,返回true表示需要保持状态 @override bool get wantKeepAlive => true; void initRadioPlayer() { _radioPlayer.setChannel( title: 'My Radio', url: 'https://myradio.url', imagePath: 'assets/radio.jpg', ); _radioPlayer.stateStream.listen((value) { setState(() { isPlaying = value; }); }); _radioPlayer.metadataStream.listen((value) { setState(() { metadata = value; }); }); } @override void initState() { super.initState(); initRadioPlayer(); } @override Widget build(BuildContext context) { // 必须调用super.build(context),否则状态保持不生效 super.build(context); return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ FutureBuilder( future: _radioPlayer.getArtworkImage(), builder: (BuildContext context, AsyncSnapshot snapshot) { Image artwork; if (snapshot.hasData) { artwork = snapshot.data; } else { artwork = Image.asset( 'assets/radio.jpg', fit: BoxFit.cover, ); } return SizedBox( height: 180, width: 180, child: ClipRRect( child: artwork, borderRadius: BorderRadius.circular(15.0), ), ); }, ), const SizedBox(height: 20), const Text( 'Radio Orient Live', softWrap: false, overflow: TextOverflow.fade, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24), ), Text( metadata?[1] ?? '', softWrap: false, overflow: TextOverflow.fade, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ), SizedBox(height: 20), FloatingActionButton( onPressed: () { isPlaying ? _radioPlayer.pause() : _radioPlayer.play(); }, tooltip: 'Control button', child: Icon( isPlaying ? Icons.pause_rounded : Icons.play_arrow_rounded, ), ), ], ), ); } }
方法二:将播放状态提升到父组件(推荐)
把播放状态和RadioPlayer实例移到顶层的RadioApp组件中,这样页面切换时状态不会丢失,还能方便在多个页面共享播放状态。
1. 修改main.dart:
import 'package:flutter/material.dart'; import 'package:radio_orient/components/bottom_nav_bar.dart'; import 'package:radio_orient/pages/home.dart'; import 'package:radio_orient/pages/more_info.dart'; import 'package:radio_player/radio_player.dart'; void main() { runApp(RadioApp()); } class RadioApp extends StatefulWidget { @override _RadioAppState createState() => _RadioAppState(); } class _RadioAppState extends State<RadioApp> { int _selectedIndex = 0; late RadioPlayer _radioPlayer; bool isPlaying = false; List<String>? metadata; void navigateBottomBar(int index) { setState(() { _selectedIndex = index; }); } void initRadioPlayer() { _radioPlayer = RadioPlayer(); _radioPlayer.setChannel( title: 'My Radio', url: 'https://myradio.url', imagePath: 'assets/radio.jpg', ); _radioPlayer.stateStream.listen((value) { setState(() { isPlaying = value; }); }); _radioPlayer.metadataStream.listen((value) { setState(() { metadata = value; }); }); } @override void initState() { super.initState(); initRadioPlayer(); } @override Widget build(BuildContext context) { // 动态传递状态和方法给Home页面 final pages = [ Home( isPlaying: isPlaying, metadata: metadata, onPlayPause: () { isPlaying ? _radioPlayer.pause() : _radioPlayer.play(); }, getArtworkImage: _radioPlayer.getArtworkImage, ), const MoreInfo() ]; return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('Radio Orient'), backgroundColor: Color.fromARGB(255, 36, 120, 85), ), body: pages[_selectedIndex], backgroundColor: const Color.fromRGBO(207, 185, 151, 1), bottomNavigationBar: MyBottomNavBar( onTabChange: (index) => navigateBottomBar(index), ), ), ); } }
2. 修改home.dart为无状态组件:
import 'package:flutter/material.dart'; import 'package:radio_player/radio_player.dart'; class Home extends StatelessWidget { final bool isPlaying; final List<String>? metadata; final VoidCallback onPlayPause; final Future<Image> Function() getArtworkImage; const Home({ super.key, required this.isPlaying, required this.metadata, required this.onPlayPause, required this.getArtworkImage, }); @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ FutureBuilder( future: getArtworkImage(), builder: (BuildContext context, AsyncSnapshot snapshot) { Image artwork; if (snapshot.hasData) { artwork = snapshot.data; } else { artwork = Image.asset( 'assets/radio.jpg', fit: BoxFit.cover, ); } return SizedBox( height: 180, width: 180, child: ClipRRect( child: artwork, borderRadius: BorderRadius.circular(15.0), ), ); }, ), const SizedBox(height: 20), const Text( 'Radio Orient Live', softWrap: false, overflow: TextOverflow.fade, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24), ), Text( metadata?[1] ?? '', softWrap: false, overflow: TextOverflow.fade, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ), SizedBox(height: 20), FloatingActionButton( onPressed: onPlayPause, tooltip: 'Control button', child: Icon( isPlaying ? Icons.pause_rounded : Icons.play_arrow_rounded, ), ), ], ), ); } }
方法对比
- 方法一改动小,适合只需要保持单个页面状态的简单场景。
- 方法二更符合Flutter状态管理的最佳实践,适合需要在多个页面共享播放状态的场景(比如在Info页面显示当前播放状态)。
内容的提问来源于stack exchange,提问作者Ah.Kh
相关产品推荐
相关产品推荐

