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

Flutter音频页面返回后状态不更新及Provider报错问题

Flutter音频播放状态修复方案

一、解决「setState() or markNeedsBuild() called during build.」报错

这个错误本质是在Widget构建周期内触发了Provider的状态通知,打断了Flutter的构建流程,直接修改以下两处即可修复:

  1. 所有需要更新Provider状态的操作,用WidgetsBinding.instance.addPostFrameCallback延迟到当前构建完成后执行:
    // 监听音频位置变化时的示例
    player.onPositionChanged.listen((position) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        context.read<PlayerStateModel>().updatePosition(position);
      });
    });
    
  2. 或者用Future.microtask包裹状态更新逻辑,效果一致:
    Future.microtask(() {
      context.read<PlayerStateModel>().updatePlayState(newState);
    });
    

二、修复页面返回后状态不更新的问题

核心是让播放器状态跨页面持久化,同时确保UI能同步最新状态:

  1. 全局共享PlayerStateModel实例,不要在每个页面重新创建:
    在MaterialApp外层注入Provider,保证整个应用只有一个Model实例:
    void main() {
      runApp(
        ChangeNotifierProvider(
          create: (_) => PlayerStateModel(),
          child: const MyApp(),
        ),
      );
    }
    
  2. 页面进入时主动同步已有播放器状态:
    在音频页面的initState中,同步Model里的播放器状态到UI:
    @override
    void initState() {
      super.initState();
      final model = Provider.of<PlayerStateModel>(context, listen: false);
      // 延迟同步,避免构建中触发通知
      WidgetsBinding.instance.addPostFrameCallback((_) {
        model.syncPlayerState();
      });
    }
    
  3. 在PlayerStateModel中维护完整状态并实时通知:
    把播放器的核心状态(播放状态、进度、总时长)存在Model里,监听播放器回调并更新:
    class PlayerStateModel extends ChangeNotifier {
      AudioPlayer? _player;
      bool isPlaying = false;
      Duration currentPos = Duration.zero;
      Duration totalDur = Duration.zero;
      String? firebaseAudioUrl;
    
      // 初始化播放器,单例模式
      void initPlayer(String url) {
        firebaseAudioUrl = url;
        _player ??= AudioPlayer();
        // 监听位置变化
        _player!.onPositionChanged.listen((pos) {
          currentPos = pos;
          notifyListeners();
        });
        // 监听时长变化
        _player!.onDurationChanged.listen((dur) {
          totalDur = dur;
          notifyListeners();
        });
        // 监听播放状态变化
        _player!.onPlayerStateChanged.listen((state) {
          isPlaying = state == PlayerState.playing;
          notifyListeners();
        });
      }
    
      // 同步当前播放器状态(页面返回时调用)
      Future<void> syncPlayerState() async {
        if (_player == null) return;
        isPlaying = await _player!.playing;
        currentPos = await _player!.currentPosition;
        totalDur = await _player!.duration ?? Duration.zero;
        notifyListeners();
      }
    
      // 播放/暂停切换
      Future<void> togglePlay() async {
        if (_player == null) return;
        isPlaying ? await _player!.pause() : await _player!.play(UrlSource(firebaseAudioUrl!));
      }
    
      // 进度跳转
      Future<void> seekTo(Duration pos) async {
        if (_player != null) {
          await _player!.seek(pos);
          currentPos = pos;
          notifyListeners();
        }
      }
    }
    

三、确保进度条与按钮状态正常更新

UI层必须通过Consumer或Provider.of监听Model状态,实时刷新:

  1. 播放/暂停按钮:
    Consumer<PlayerStateModel>(
      builder: (context, model, _) {
        return IconButton(
          icon: Icon(model.isPlaying ? Icons.pause : Icons.play_arrow),
          onPressed: model.togglePlay,
        );
      },
    )
    
  2. 进度条与已播放时长:
    Consumer<PlayerStateModel>(
      builder: (context, model, _) {
        return Column(
          children: [
            Slider(
              value: model.currentPos.inSeconds.toDouble(),
              max: model.totalDur.inSeconds.toDouble(),
              onChanged: (val) => model.seekTo(Duration(seconds: val.toInt())),
            ),
            Text("${model.currentPos.inSeconds}/${model.totalDur.inSeconds}")
          ],
        );
      },
    )
    

四、页面生命周期处理

页面退出时不要销毁播放器,只暂停即可,保留状态:

@override
void dispose() {
  final model = Provider.of<PlayerStateModel>(context, listen: false);
  model.togglePlay(); // 暂停播放,不释放播放器
  super.dispose();
}

内容的提问来源于stack exchange,提问作者Dhanrajsinh Parmar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 08:44:59