You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter页面切换时如何停止音频播放?附代码求助

解决Flutter切换页面后无法停止音频播放的问题

核心问题分析

你的代码存在以下关键问题导致切换页面后无法停止音频:

  1. 未监听页面生命周期,切换页面时AudioPlayer未停止播放
  2. 同时定义了全局AudioPlayer? player和State内部的final player = AudioPlayer(),造成实例混淆
  3. 用Timer手动累加进度的方式不准确,且未妥善管理Timer生命周期
  4. 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,
                  )),
            ],
          ),
        ],
      ),
    );
  }
}

关键修改说明

  1. 生命周期监听:通过WidgetsBindingObserver的didChangeAppLifecycleState方法,在页面切换到后台/不可见时自动停止音频
  2. 资源管理:在dispose()中销毁播放器、取消所有流订阅和Timer,彻底释放资源
  3. 进度计算:使用positionStream和durationStream实时计算播放进度,替代不准确的Timer累加方式
  4. 播放器实例统一:移除全局播放器变量,仅使用State内部的实例,避免混淆
  5. 播放/暂停逻辑优化:根据播放器当前状态判断是播放还是暂停,无需每次都重新加载音频

内容的提问来源于stack exchange,提问作者Gael Ongoriko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 20:35:28