切换Tab时停止音频播放器的Flutter实现方案咨询
问题:切换TabBarView时停止音频播放
我有一个包含3个子项的TabBarView,其中一个子项包含音频播放器组件。代码如下:
length: 3, child: Scaffold( appBar: AppBar(widget.title, myTabs), body: TabBarView( children: [ ChildrenA(), ChildrenB(), ChildrenC(), ], ), ), );
ChildrenA是一个包含ListView的StatefulWidget,ListView中的一个元素是我的AudioPlayer,我使用just_audio组件来播放音频,代码如下:
addAutomaticKeepAlives: true, itemCount: awaitedContents!.length, itemBuilder: (context, index) { Content content = awaitedContents[index]; ... return AudioContent(content.value), ); }
切换Tab时dispose方法不会被调用,只有返回路由中的上一页面时才会触发该方法,请问如何在切换Tab时停止音频播放?
解决方案
方法1:利用AutomaticKeepAliveClientMixin结合页面可见性监听
TabBarView的子Widget切换时不会销毁,只会被隐藏,可通过监听页面可见状态变化控制音频:
class ChildrenAState extends State<ChildrenA> with AutomaticKeepAliveClientMixin, WidgetsBindingObserver { late AudioPlayer _audioPlayer; @override void initState() { super.initState(); _audioPlayer = AudioPlayer(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); _audioPlayer.dispose(); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { // 页面从可见转为不可见(切换Tab)时停止播放 if (state == AppLifecycleState.paused) { _audioPlayer.stop(); } } @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { super.build(context); // 必须调用以保证AutomaticKeepAlive生效 return ListView( addAutomaticKeepAlives: true, itemCount: awaitedContents!.length, itemBuilder: (context, index) { Content content = awaitedContents[index]; return AudioContent(content.value); }, ); } }
方法2:监听TabController切换事件
在父Widget中监听Tab索引变化,切换到非目标Tab时直接通知ChildrenA停止音频:
- 父Widget代码:
class ParentWidget extends StatefulWidget { @override _ParentWidgetState createState() => _ParentWidgetState(); } class _ParentWidgetState extends State<ParentWidget> with SingleTickerProviderStateMixin { late TabController _tabController; final GlobalKey<ChildrenAState> _childrenAKey = GlobalKey(); @override void initState() { super.initState(); _tabController = TabController(length: 3, vsync: this); _tabController.addListener(_handleTabChange); } void _handleTabChange() { // 切换到非第一个Tab(ChildrenA所在位置)时停止音频 if (_tabController.index != 0) { _childrenAKey.currentState?.stopAudio(); } } @override void dispose() { _tabController.removeListener(_handleTabChange); _tabController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(widget.title), bottom: TabBar(controller: _tabController, tabs: myTabs)), body: TabBarView( controller: _tabController, children: [ ChildrenA(key: _childrenAKey), ChildrenB(), ChildrenC(), ], ), ); } }
- ChildrenA的State中添加停止方法:
class ChildrenAState extends State<ChildrenA> with AutomaticKeepAliveClientMixin { late AudioPlayer _audioPlayer; void stopAudio() { _audioPlayer.stop(); } // 其他初始化、dispose、build代码同方法1... }
方法3:在AudioContent组件中监听自身可见性
如果音频播放器是独立组件,可使用VisibilityDetector(需引入对应包)监听组件可见度:
class AudioContent extends StatefulWidget { final String audioUrl; AudioContent(this.audioUrl); @override _AudioContentState createState() => _AudioContentState(); } class _AudioContentState extends State<AudioContent> { late AudioPlayer _audioPlayer; @override void initState() { super.initState(); _audioPlayer = AudioPlayer(); } @override void dispose() { _audioPlayer.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return VisibilityDetector( key: Key(widget.audioUrl), onVisibilityChanged: (info) { // 组件可见占比低于50%时停止播放 if (info.visibleFraction < 0.5) { _audioPlayer.stop(); } }, child: // 你的音频播放UI组件 ); } }
内容的提问来源于stack exchange,提问作者sylikon
相关产品推荐
相关产品推荐

