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

Flutter中VideoPlayer无法正常初始化问题求助

问题

我正在构建一个ListView,当post的video参数不为空时显示VideoPlayer。目前已成功显示播放器,但视频无法正常初始化,仅显示黑色容器,只有点击按钮触发才能完成初始化,尽管我已在initState中执行了初始化操作。

代码片段

PostItem 组件

import 'package:c_joga/models/classes/post.dart';
import 'package:c_joga/models/classes/user.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

class PostItem extends StatefulWidget {
  const PostItem({
    super.key,
    required this.post,
  });

  final Post post;

  @override
  State<PostItem> createState() => _PostItemState();
}

class _PostItemState extends State<PostItem> {
  VideoPlayerController? videoController;

  @override
  void initState() {
    if (widget.post.video != null) {
      setState(() {
        videoController = _initializeVideoPlayer(widget.post.video!);
      });
    }
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
    if (widget.post.video != null) {
      videoController?.dispose();
    }
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: MediaQuery.of(context).size.width * 0.185,
      child: Card(
          color: Theme.of(context).colorScheme.secondary,
          shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(
            20,
          )),
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Row(
                      children: [
                        CircleAvatar(),
                        const SizedBox(
                          width: 10,
                        ),
                        Text('@${widget.post.user.username}')
                      ],
                    ),
                    IconButton(
                      onPressed: () {},
                      icon: const Icon(
                        Icons.more_horiz,
                      ),
                    ),
                  ],
                ),
                if (widget.post.description != null)
                  Text(widget.post.description!),
                const SizedBox(
                  height: 10,
                ),
                if (widget.post.photo != null)
                  Container(
                    height: 250,
                    decoration: BoxDecoration(
                      image: DecorationImage(
                          image: NetworkImage(
                            widget.post.photo!,
                          ),
                          fit: BoxFit.cover),
                    ),
                  ),
                if (videoController != null)
                  Stack(
                    alignment: Alignment.center,
                    children: [
                      Container(
                        height: 350,
                        child: VideoPlayer(videoController!),
                      ),
                      Stack(
                        alignment: Alignment.center,
                        children: [
                          CircleAvatar(
                              backgroundColor: Colors.black26,
                              child: Container(
                                  decoration: BoxDecoration(
                                shape: BoxShape.circle,
                                border: Border.all(
                                  color: Colors.white,
                                  width: 3,
                                ),
                              ))),
                          IconButton(
                            style: ButtonStyle(
                              elevation: MaterialStatePropertyAll(300),
                            ),
                            onPressed: () {
                              setState(() {
                                if (videoController!.value.isPlaying) {
                                  videoController!.pause();
                                } else {
                                  videoController = _initializeVideoPlayer(
                                      widget.post.video!);
                                  videoController!.play();
                                }
                              });
                            },
                            icon: Icon(
                              color: Colors.white,
                              videoController!.value.isPlaying
                                  ? Icons.pause
                                  : Icons.play_arrow,
                            ),
                          ),
                        ],
                      )
                    ],
                  ),
              ],
            ),
          )),
    );
  }

  VideoPlayerController _initializeVideoPlayer(String videoUrl) {
    VideoPlayerController controller = VideoPlayerController.network(videoUrl);
    controller.initialize().then((_) {
      setState(() {});
    });
    return controller;
  }
}

ListView 调用

ListView.builder(
  physics: const BouncingScrollPhysics(),
  shrinkWrap: true,
  itemCount: posts.length,
  itemBuilder: (BuildContext context, int i) {
    return PostItem(post: posts[i]);
  },
),

解决方法

  • 修正异步初始化逻辑:initState中直接赋值未完成初始化的控制器,导致UI提前渲染未就绪的VideoPlayer。改用异步方法等待初始化完成后再更新状态:

    @override
    void initState() {
      super.initState();
      if (widget.post.video != null) {
        _initVideoController();
      }
    }
    
    void _initVideoController() async {
      final controller = VideoPlayerController.network(widget.post.video!);
      await controller.initialize();
      setState(() {
        videoController = controller;
      });
    }
    
  • 复用已有控制器,避免重复创建:点击播放按钮时无需重新初始化控制器,直接对现有控制器执行播放/暂停操作,同时避免内存泄漏:

    IconButton(
      style: ButtonStyle(
        elevation: MaterialStatePropertyAll(300),
      ),
      onPressed: () {
        setState(() {
          if (videoController!.value.isPlaying) {
            videoController!.pause();
          } else {
            videoController!.play();
          }
        });
      },
      icon: Icon(
        color: Colors.white,
        videoController!.value.isPlaying
            ? Icons.pause
            : Icons.play_arrow,
      ),
    ),
    
  • 添加初始化状态反馈:在控制器未完成初始化时显示加载指示器,替代黑色容器提升用户体验:

    if (videoController != null) {
      if (videoController!.value.isInitialized) {
        // 原有VideoPlayer堆叠布局
        Stack(
          alignment: Alignment.center,
          children: [
            Container(
              height: 350,
              child: VideoPlayer(videoController!),
            ),
            // 播放按钮组件...
          ],
        )
      } else {
        Container(
          height: 350,
          child: const Center(child: CircularProgressIndicator()),
        );
      }
    }
    
  • 修正资源释放顺序:先释放控制器资源再执行父类dispose,确保资源正确回收:

    @override
    void dispose() {
      videoController?.dispose();
      super.dispose();
    }
    
  • 检查基础配置:确认视频URL可正常访问,AndroidManifest.xml中添加INTERNET权限,iOS项目中配置ATS(若使用HTTP链接)。


内容的提问来源于stack exchange,提问作者pedro.curti

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 17:14:57