Skip to content

[video_player IOS] VideoPlayerController.network is blocking the UI while loading #97356

@Theunodb

Description

@Theunodb

We have an application that displays a feed of images and videos similar to instagram. After version 2.2.11 we found that whenever there are videos in the feed the UI gets blocked while the video is loading. We confirmed it's related to the video by commenting out certain code and observing the results, or by downgrading to 2.2.11

Please help us find what we are doing wrong.

This is the widget that generates the video. The mediaUlrs list contains 2 items, the first is the video thumbnail, the second the url of the video.

import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:xxx/constants/constants.dart';
import 'package:xxx/models/feed/feed_item.dart';
import 'package:xxx/ui/widgets/feedWidgets/like_comment_actions.dart';
import 'package:video_player/video_player.dart';

class FeedItemVideoHolder extends StatefulWidget {
  final FeedItem feedItem;
  final List<dynamic>? mediaUrls;
  final VoidCallback? addComment;
  final VoidCallback? likeFeedItem;

  const FeedItemVideoHolder(this.feedItem, this.mediaUrls, this.addComment, this.likeFeedItem, {Key? key}) : super(key: key);

  @override
  _FeedItemVideoHolderState createState() => _FeedItemVideoHolderState();
}

class _FeedItemVideoHolderState extends State<FeedItemVideoHolder> {
  late VideoPlayerController _controller;
  late Future<void> _initializeVideoPlayerFuture;

  _playPauseVideo() async {
    if (_controller.value.volume == 0 && _controller.value.isPlaying) {
      _controller.setVolume(1.0);
      setState(() {});
    } else if (_controller.value.isPlaying) {
      _controller.setVolume(0.0);
      await _controller.pause();
      setState(() {});
    } else {
      _controller.setVolume(1.0);
      await _controller.play();
      setState(() {});
    }
  }

  _initialiseVideoPlayer() async {
    _controller = VideoPlayerController.network(widget.feedItem.media![1], videoPlayerOptions: VideoPlayerOptions(mixWithOthers: false));
    _controller.setLooping(true);
    _controller.setVolume(0.0);
    _initializeVideoPlayerFuture = _controller.initialize();
    _controller.play();
  }

  @override
  initState() {
    super.initState();
    _initialiseVideoPlayer();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _initializeVideoPlayerFuture,
      builder: (context, snapshot) {
        if (snapshot.connectionState != ConnectionState.done) {
          return Column(
            children: [
              ExtendedImage.network(widget.mediaUrls![0], cache: true, fit: BoxFit.cover),
              SizedBox(
                height: 50,
                child: LikeCommentActions(
                  feedItem: widget.feedItem,
                  addComment: widget.addComment,
                  likeFeedItem: widget.likeFeedItem,
                ),
              )
            ],
          );
        }

        return GestureDetector(
          onTap: () => _playPauseVideo(),
          child: Column(
            children: [
              Stack(
                children: [
                  Container(
                    color: darkGrey,
                    child: _controller.value.isPlaying
                        ? AspectRatio(
                            aspectRatio: _controller.value.aspectRatio,
                            child: VideoPlayer(_controller),
                          )
                        : widget.mediaUrls!.length == widget.feedItem.media!.length
                            ? ExtendedImage.network(widget.mediaUrls![0], cache: true, fit: BoxFit.cover)
                            : Image.asset('assets/feed_item_loading.png', fit: BoxFit.fill),
                  ),
                  Offstage(
                    offstage: _controller.value.isPlaying,
                    child: Center(
                      child: IconButton(
                        icon: Icon(_controller.value.isPlaying ? Icons.pause : Icons.play_circle_filled, color: orange, size: 50),
                        onPressed: () => _playPauseVideo(),
                      ),
                    ),
                  ),
                  Positioned(
                    bottom: 0,
                    right: 0,
                    child: Offstage(
                      offstage: !(_controller.value.volume == 0 && _controller.value.isPlaying),
                      child: Icon(Icons.volume_off_outlined, color: orange, size: 25),
                    ),
                  ),
                ],
              ),
              SizedBox(
                height: 50,
                child: LikeCommentActions(
                  feedItem: widget.feedItem,
                  addComment: widget.addComment,
                  likeFeedItem: widget.likeFeedItem,
                ),
              )
            ],
          ),
        );
      },
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

When we comment out the _controller = VideoPlayerController.network line the UI responds fine.

We tried running the project in both stable and master channels.

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work listc: performanceRelates to speed or footprint issues (see "perf:" labels)c: regressionIt was better in the past than it is nowfound in release: 2.10Found to occur in 2.10found in release: 2.8Found to occur in 2.8has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: video_playerThe Video Player pluginpackageflutter/packages repository. See also p: labels.platform-iosiOS applications specificallyr: fixedIssue is closed as already fixed in a newer version

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions