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

Flutter中如何判断PageView完全切换以控制视频播放

Fixing Simultaneous Video Playback in 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

  1. Add a playback control parameter to VideoApp
    Modify the VideoApp widget to accept an isPlaying boolean, so the parent can tell it when to play/pause.

  2. Track the currently active page in MultiplePage
    Use the PageController to monitor which page is fully visible, then update the state to inform child widgets.

  3. Update playback state based on parent signals
    In VideoApp, listen for changes to the isPlaying parameter 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 MultiplePage tracks the fully visible page and tells the corresponding VideoApp to play.
  • Smooth state updates: The didUpdateWidget method in VideoApp ensures 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 PageController and 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 08:39:05