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

Flutter中使用底部导航栏切换页面时如何暂停视频播放?

问题分析与解决方案

你的代码里在build方法中通过路由判断控制视频播放,但切换底部导航时,首页的Widget树不会重建,build方法不会重新执行,导致判断逻辑无法触发,视频也就不会自动暂停。下面是两种可行的解决方法:

方法一:监听GetX路由变化

利用GetX的路由监听功能,实时监控当前路由变化,离开首页时暂停视频,返回首页且是当前显示的视频时恢复播放。

修改后的VideoTile代码:

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:ramadan/routes.dart';
import 'package:cached_video_player_plus/cached_video_player_plus.dart';

class VideoTile extends StatefulWidget {
  const VideoTile({
    super.key,
    required this.video,
    required this.snappedPageIndex,
    required this.currentIndex,
  });
  final dynamic video;
  final int snappedPageIndex;
  final int currentIndex;

  @override
  State<VideoTile> createState() => _VideoTileState();
}

class _VideoTileState extends State<VideoTile> {
  late CachedVideoPlayerPlusController _cacheController;
  late Future _initializeVideoplayer;
  bool _isVideoPlaying = true;
  StreamSubscription<String>? _routeSubscription;

  @override
  void initState() {
    super.initState();
    // 初始化视频控制器
    _cacheController = CachedVideoPlayerPlusController.networkUrl(
      Uri.parse(widget.video['url']),
      httpHeaders: {'Connection': 'keep-alive'},
      invalidateCacheIfOlderThan: const Duration(minutes: 60),
    );
    _initializeVideoplayer = _cacheController.initialize().then((value) async {
      await _cacheController.setLooping(true);
      _cacheController.play();
      setState(() {});
    });

    // 监听路由变化
    _routeSubscription = Get.routing.listen((route) {
      if (route.current != AppRoutes.home) {
        // 离开首页时暂停视频
        _cacheController.pause();
        setState(() => _isVideoPlaying = false);
      } else if (widget.snappedPageIndex == widget.currentIndex) {
        // 返回首页且是当前显示的视频时恢复播放
        _cacheController.play();
        setState(() => _isVideoPlaying = true);
      }
    });
  }

  @override
  void dispose() {
    // 取消路由监听,避免内存泄漏
    _routeSubscription?.cancel();
    _cacheController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.black,
      child: FutureBuilder(
        future: _initializeVideoplayer,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            return InkWell(
              onTap: () {
                // 手动点击切换播放/暂停状态
                if (_isVideoPlaying) {
                  _cacheController.pause();
                } else {
                  _cacheController.play();
                }
                setState(() => _isVideoPlaying = !_isVideoPlaying);
              },
              child: Stack(
                alignment: Alignment.center,
                children: [
                  CachedVideoPlayerPlus(_cacheController),
                  IconButton(
                    onPressed: () {
                      _cacheController.play();
                      setState(() => _isVideoPlaying = true);
                    },
                    icon: Icon(
                      Icons.play_arrow,
                      color: Colors.white.withOpacity(_isVideoPlaying ? 0 : 0.5),
                      size: 60,
                    ),
                  )
                ],
              ),
            );
          } else {
            return Container(color: Colors.pink);
          }
        },
      ),
    );
  }
}

方法二:监听页面可见性(含应用前后台处理)

如果需要同时处理应用前后台切换的情况,可以使用WidgetsBindingObserver监听页面生命周期,结合路由判断实现更全面的控制:

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:ramadan/routes.dart';
import 'package:cached_video_player_plus/cached_video_player_plus.dart';

class VideoTile extends StatefulWidget {
  const VideoTile({
    super.key,
    required this.video,
    required this.snappedPageIndex,
    required this.currentIndex,
  });
  final dynamic video;
  final int snappedPageIndex;
  final int currentIndex;

  @override
  State<VideoTile> createState() => _VideoTileState();
}

class _VideoTileState extends State<VideoTile> with WidgetsBindingObserver {
  late CachedVideoPlayerPlusController _cacheController;
  late Future _initializeVideoplayer;
  bool _isVideoPlaying = true;

  @override
  void initState() {
    super.initState();
    // 添加生命周期观察者
    WidgetsBinding.instance.addObserver(this);
    // 初始化视频控制器
    _cacheController = CachedVideoPlayerPlusController.networkUrl(
      Uri.parse(widget.video['url']),
      httpHeaders: {'Connection': 'keep-alive'},
      invalidateCacheIfOlderThan: const Duration(minutes: 60),
    );
    _initializeVideoplayer = _cacheController.initialize().then((value) async {
      await _cacheController.setLooping(true);
      _cacheController.play();
      setState(() {});
    });
  }

  @override
  void dispose() {
    // 移除生命周期观察者
    WidgetsBinding.instance.removeObserver(this);
    _cacheController.dispose();
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    // 应用进入后台或 inactive 状态时暂停视频
    if (state == AppLifecycleState.paused || state == AppLifecycleState.inactive) {
      _cacheController.pause();
      setState(() => _isVideoPlaying = false);
    } 
    // 应用回到前台且当前视频是显示中的,恢复播放
    else if (state == AppLifecycleState.resumed && widget.snappedPageIndex == widget.currentIndex) {
      _cacheController.play();
      setState(() => _isVideoPlaying = true);
    }
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 路由变化时检查,离开首页则暂停视频
    final currentRoute = Get.currentRoute;
    if (currentRoute != AppRoutes.home) {
      _cacheController.pause();
      setState(() => _isVideoPlaying = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.black,
      child: FutureBuilder(
        future: _initializeVideoplayer,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            return InkWell(
              onTap: () {
                if (_isVideoPlaying) {
                  _cacheController.pause();
                } else {
                  _cacheController.play();
                }
                setState(() => _isVideoPlaying = !_isVideoPlaying);
              },
              child: Stack(
                alignment: Alignment.center,
                children: [
                  CachedVideoPlayerPlus(_cacheController),
                  IconButton(
                    onPressed: () {
                      _cacheController.play();
                      setState(() => _isVideoPlaying = true);
                    },
                    icon: Icon(
                      Icons.play_arrow,
                      color: Colors.white.withOpacity(_isVideoPlaying ? 0 : 0.5),
                      size: 60,
                    ),
                  )
                ],
              ),
            );
          } else {
            return Container(color: Colors.pink);
          }
        },
      ),
    );
  }
}

核心原因

原来的代码依赖build方法触发播放/暂停判断,但底部导航切换时,首页的Widget不会重建,build方法不会执行,所以逻辑无法触发。通过主动监听路由或页面生命周期,才能实时响应页面状态变化,控制视频播放。


内容的提问来源于stack exchange,提问作者Mohammed Bekele

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 08:33:12