Flutter中如何判断PageView完全切换以控制视频播放
Got it, let's tackle this overlapping video playback issue in your PageView. The problem right now is that when you scroll slowly, adjacent pages are partially visible and both play videos because your VideoApp starts playing as soon as it initializes. Here's how to fix it to only play when a page is fully visible and pause when it's completely out of view:
Core Idea
We'll let the parent MultiplePage component control the playback state of each VideoApp child. Only the page that's fully centered in the viewport will get the "play" signal, while others stay paused.
Step-by-Step Solution
Add a playback control parameter to VideoApp
Modify theVideoAppwidget to accept anisPlayingboolean, so the parent can tell it when to play/pause.Track the currently active page in MultiplePage
Use thePageControllerto monitor which page is fully visible, then update the state to inform child widgets.Update playback state based on parent signals
InVideoApp, listen for changes to theisPlayingparameter and adjust video playback accordingly.
Modified Full Code
import 'package:video_player/video_player.dart'; import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue), home: const MultiplePage(), ); } } class MultiplePage extends StatefulWidget { const MultiplePage({Key? key}) : super(key: key); @override _MultiplePageState createState() => _MultiplePageState(); } class _MultiplePageState extends State<MultiplePage> { late PageController _controller; int currentPageIndex = 0; @override void initState() { super.initState(); _controller = PageController()..addListener(_scrollListener); } void _scrollListener() { final page = _controller.page; if (page != null) { final roundedPage = page.round(); // Only update when the page is fully settled (within 0.01 of the integer index) if ((page - roundedPage).abs() < 0.01 && roundedPage != currentPageIndex) { setState(() { currentPageIndex = roundedPage; }); } } } @override Widget build(BuildContext context) { return PageView.builder( controller: _controller, scrollDirection: Axis.vertical, // Optional: Disable preloading if you don't need it (may affect scroll smoothness) // cacheExtent: 0.0, itemBuilder: (context, position) { // Pass whether this page is the currently active one return VideoApp(isPlaying: position == currentPageIndex); }, ); } @override void dispose() { _controller.dispose(); super.dispose(); } } class VideoApp extends StatefulWidget { final bool isPlaying; const VideoApp({Key? key, required this.isPlaying}) : super(key: key); @override _VideoAppState createState() => _VideoAppState(); } class _VideoAppState extends State<VideoApp> { VideoPlayerController? _controller; @override void initState() { super.initState(); _initializeVideo(); } @override void didUpdateWidget(covariant VideoApp oldWidget) { super.didUpdateWidget(oldWidget); // Trigger playback state change when the isPlaying parameter updates if (oldWidget.isPlaying != widget.isPlaying) { _updatePlaybackState(); } } Future<void> _initializeVideo() async { _controller = VideoPlayerController.network( 'http://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4', ); await _controller!.initialize(); setState(() {}); // Set initial playback state based on parent's signal _updatePlaybackState(); } void _updatePlaybackState() { if (_controller == null || !_controller!.value.initialized) return; if (widget.isPlaying) { _controller!.play(); } else { _controller!.pause(); } } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: _controller?.value.initialized ?? false ? AspectRatio( aspectRatio: _controller!.value.aspectRatio, child: VideoPlayer(_controller!), ) : const CircularProgressIndicator(), ), ); } @override void dispose() { _controller?.dispose(); super.dispose(); } }
Key Improvements
- Parent-controlled playback: The
MultiplePagetracks the fully visible page and tells the correspondingVideoAppto play. - Smooth state updates: The
didUpdateWidgetmethod inVideoAppensures we react immediately when the playback signal changes. - No automatic playback: Videos no longer start playing on initialization—they wait for the parent's signal.
- Cleaner disposal: Added proper disposal of the
PageControllerand null safety checks for the video controller.
Optional Tweak
If you want to disable PageView's default preloading of adjacent pages, add cacheExtent: 0.0 to the PageView.builder. Note that this might make scrolling less smooth, so test it out to see what works for your use case.
内容的提问来源于stack exchange,提问作者Nature.Li

