Flutter页面切换时如何停止音频播放?附代码求助
解决Flutter切换页面后无法停止音频播放的问题
核心问题分析
你的代码存在以下关键问题导致切换页面后无法停止音频:
- 未监听页面生命周期,切换页面时AudioPlayer未停止播放
- 同时定义了全局
AudioPlayer? player和State内部的final player = AudioPlayer(),造成实例混淆 - 用Timer手动累加进度的方式不准确,且未妥善管理Timer生命周期
- State销毁时未释放AudioPlayer资源,存在内存泄漏风险
解决方案步骤
- 移除全局的
AudioPlayer? player变量,仅保留State内部的播放器实例 - 让State类混入
WidgetsBindingObserver,监听应用生命周期/页面可见性变化 - 在页面不可见时(如切换到其他页面)停止音频播放,释放Timer
- 使用
just_audio提供的positionStream和durationStream实时计算播放进度,替代Timer手动累加 - 在
dispose()方法中销毁播放器、取消流监听和Timer,避免内存泄漏
修改后的完整代码
import 'dart:async'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:just_audio/just_audio.dart'; class Hom extends StatelessWidget { const Hom({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("title")), body: const Center( child: AudioPlayers(), ), ); } } class AudioPlayers extends StatefulWidget { const AudioPlayers({Key? key}) : super(key: key); @override State<AudioPlayers> createState() => _AudioPlayersState(); } class _AudioPlayersState extends State<AudioPlayers> with WidgetsBindingObserver { Timer? timer2; final player = AudioPlayer(); Duration? _duration; Duration _position = Duration(seconds: 0); double _progress = 0.0; StreamSubscription? _playerStateSubscription; StreamSubscription? _positionSubscription; StreamSubscription? _durationSubscription; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); // 监听播放器状态变化 _playerStateSubscription = player.playerStateStream.listen((state) { if (state.playing) { setState(() {}); } else { switch (state.processingState) { case ProcessingState.ready: setState(() { _progress = 0; timer2?.cancel(); }); break; case ProcessingState.completed: setState(() { _progress = 1; }); break; default: break; } } }); // 监听播放进度 _positionSubscription = player.positionStream.listen((position) { setState(() { _position = position; if (_duration != null) { _progress = _position.inMilliseconds / _duration!.inMilliseconds; } }); }); // 监听音频时长 _durationSubscription = player.durationStream.listen((duration) { setState(() { _duration = duration; }); }); } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); // 当页面切换到后台或不可见时,停止播放 if (state == AppLifecycleState.paused || state == AppLifecycleState.inactive) { player.stop(); timer2?.cancel(); } } @override void dispose() { // 释放所有资源 WidgetsBinding.instance.removeObserver(this); _playerStateSubscription?.cancel(); _positionSubscription?.cancel(); _durationSubscription?.cancel(); timer2?.cancel(); player.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( image: DecorationImage( image: AssetImage("images/Night-Sky-Backgrounds.jpg"), fit: BoxFit.cover), ), padding: EdgeInsets.all(8), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ LinearProgressIndicator( value: _progress.clamp(0.0, 1.0), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( onPressed: () async { if (player.playing) { // 当前正在播放,暂停 await player.pause(); timer2?.cancel(); } else { // 当前未播放,开始播放 if (_position == Duration.zero) { // 首次播放,加载音频 await player.setAsset('assets/Meditation1.mp3'); } await player.play(); } setState(() {}); }, icon: Icon( player.playing ? Icons.pause_circle_fill : Icons.play_circle_fill, size: 45, )), SizedBox( width: 45, ), IconButton( onPressed: () async { await player.stop(); timer2?.cancel(); setState(() { _position = Duration.zero; _progress = 0; }); }, icon: Icon( Icons.stop, size: 45, )), ], ), ], ), ); } }
关键修改说明
- 生命周期监听:通过
WidgetsBindingObserver的didChangeAppLifecycleState方法,在页面切换到后台/不可见时自动停止音频 - 资源管理:在
dispose()中销毁播放器、取消所有流订阅和Timer,彻底释放资源 - 进度计算:使用
positionStream和durationStream实时计算播放进度,替代不准确的Timer累加方式 - 播放器实例统一:移除全局播放器变量,仅使用State内部的实例,避免混淆
- 播放/暂停逻辑优化:根据播放器当前状态判断是播放还是暂停,无需每次都重新加载音频
内容的提问来源于stack exchange,提问作者Gael Ongoriko
相关产品推荐
相关产品推荐

