Flutter Chewie Video Player Stuck On Loading Forever

55 views Asked by At

I am trying to implement the Chewie flutter package into my application and I got everything down and made it as customizable as possible. It complies well, but when I run it on web, andriod, or any device possible all I get is this loading screen: Chewie Video Player being stuck at loading

Here's my code:

import '/backend/backend.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart'; // Imports other custom widgets
import '/custom_code/actions/index.dart'; // Imports custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom widget code

import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';
import 'dart:io';

class ChewieVideoPlayer extends StatefulWidget {
  ChewieVideoPlayer({
    Key? key,
    this.width,
    this.height,
    this.videoPath,
    this.videosList,
    this.isAutoPlay,
    this.isLooping,
    this.hideControlsAfter,
    this.currentPlayIndex,
    this.startTime,
    this.videoPlatform,
    this.hideControls,
    this.aspectRatio,
    this.disableFullScreen,
    this.isLive,
    this.disableZoomAndPan,
    this.hideOptions,
    this.allowScreenSleep,
    this.disableMuting,
    this.disablePlaybackSpeedChanging,
    this.disableAutoInitialize,
    this.fullScreenByDefault,
    this.playbackSpeedList,
    this.progressIndicatorDelay,
    this.nonDraggableProgressBar,
  }) : super(key: key);

  final double? width;
  final double? height;
  final String? videoPath;
  final List<String>? videosList;
  final bool? isAutoPlay;
  final bool? isLooping;
  final int? hideControlsAfter;
  int? currentPlayIndex;
  final double? startTime;
  final String? videoPlatform;
  final bool? hideControls;
  final double? aspectRatio;
  final bool? disableFullScreen;
  final bool? isLive;
  final bool? disableZoomAndPan;
  final bool? hideOptions;
  final bool? allowScreenSleep;
  final bool? disableMuting;
  final bool? disablePlaybackSpeedChanging;
  final bool? disableAutoInitialize;
  final bool? fullScreenByDefault;
  final List<double>? playbackSpeedList;
  final int? progressIndicatorDelay;
  final bool? nonDraggableProgressBar;
  @override
  State<ChewieVideoPlayer> createState() => _ChewieVideoPlayerState();
}

class _ChewieVideoPlayerState extends State<ChewieVideoPlayer> {
  TargetPlatform? _platform;
  late VideoPlayerController _videoPlayerController1;
  late VideoPlayerController _videoPlayerController2;
  ChewieController? _chewieController;
  late List<String> srcs;

  //Initializing the widget state
  @override
  void initState() {
    super.initState();
    initializePlayer();
    /* srcs = widget.videosList ??
        [
          widget.videoPath ??
          'https://assets.mixkit.co/videos/preview/mixkit-daytime-city-traffic-aerial-view-56-large.mp4']; */
     srcs = [
      'https://assets.mixkit.co/videos/preview/mixkit-daytime-city-traffic-aerial-view-56-large.mp4'
    ];
  }

  //Function to clear the memory to avoid leaks
  @override
  void dispose() {
    _videoPlayerController1.dispose();
    _videoPlayerController2.dispose();
    _chewieController?.dispose();
    super.dispose();
  }

  //Function to initialize the player
  Future<void> initializePlayer() async {
    _videoPlayerController1 = VideoPlayerController.networkUrl(
        Uri.parse(srcs[widget.currentPlayIndex ?? 0]));
    _videoPlayerController2 = VideoPlayerController.networkUrl(
        Uri.parse(srcs[widget.currentPlayIndex ?? 0]));
    await Future.wait([
      _videoPlayerController1.initialize(),
      _videoPlayerController2.initialize()
    ]);

    _createChewieController();
    setState(() {});
  }

//Function that creates ChewieController and custmoize it (e.g. set color, autoplay, looping,etc)
  void _createChewieController() {
    // Check if videoPlatform is provided and set the platform accordingly
    if (widget.videoPlatform != null) {
      switch (widget.videoPlatform) {
        case 'android':
          _platform = TargetPlatform.android;
          break;
        case 'ios':
          _platform = TargetPlatform.iOS;
          break;
        case 'windows':
          _platform = TargetPlatform.windows;
          break;
        default:
          // Handle other cases or leave platform as null
          break;
      }
    }
    final subtitles = [
      Subtitle(
        index: 0,
        start: Duration.zero,
        end: const Duration(seconds: 10),
        text: const TextSpan(
          children: [
            TextSpan(
              text: 'Hello',
              style: TextStyle(color: Colors.red, fontSize: 22),
            ),
            TextSpan(
              text: ' from ',
              style: TextStyle(color: Colors.green, fontSize: 20),
            ),
            TextSpan(
              text: 'subtitles',
              style: TextStyle(color: Colors.blue, fontSize: 18),
            )
          ],
        ),
      ),
      Subtitle(
        index: 0,
        start: const Duration(seconds: 10),
        end: const Duration(seconds: 20),
        text: 'Whats up? :)',
      ),
    ];

    _chewieController = ChewieController(
      videoPlayerController: _videoPlayerController1,
      autoPlay: widget.isAutoPlay ?? false,
      looping: widget.isLooping ?? false,
      hideControlsTimer: Duration(seconds: widget.hideControlsAfter ?? 3),
      showControls: widget.hideControls ?? true,
      startAt: widget.startTime != null
          ? Duration(milliseconds: (widget.startTime! * 1000).toInt())
          : null,
      aspectRatio: widget.aspectRatio ?? 1.7,
      allowFullScreen: !(widget.disableFullScreen ?? false),
      isLive: widget.isLive ?? false,
      zoomAndPan: !(widget.disableZoomAndPan ?? false),
      showOptions: !(widget.hideOptions ?? false),
      allowedScreenSleep: widget.allowScreenSleep ?? false,
      allowMuting: !(widget.disableMuting ?? false),
      allowPlaybackSpeedChanging:
          !(widget.disablePlaybackSpeedChanging ?? false),
      autoInitialize: !(widget.disableAutoInitialize ?? false),
      fullScreenByDefault: widget.fullScreenByDefault ?? false,
      draggableProgressBar: !(widget.nonDraggableProgressBar ?? false),
      playbackSpeeds:
          widget.playbackSpeedList ?? [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2],
      progressIndicatorDelay: widget.progressIndicatorDelay != null
          ? Duration(milliseconds: (widget.progressIndicatorDelay!))
          : null,
      additionalOptions: (context) {
        return <OptionItem>[
          OptionItem(
            onTap: toggleVideo,
            iconData: Icons.live_tv_sharp,
            title: 'Next Video',
          ),
        ];
      },
      subtitle: Subtitles(subtitles),
      subtitleBuilder: (context, dynamic subtitle) => Container(
        padding: const EdgeInsets.all(10.0),
        child: subtitle is InlineSpan
            ? RichText(
                text: subtitle,
              )
            : Text(
                subtitle.toString(),
                style: const TextStyle(color: Colors.black),
              ),
      ),
    );
  }

  //Function to switch to the next video in list
  Future<void> toggleVideo() async {
    await _videoPlayerController1.pause();
    widget.currentPlayIndex = (widget.currentPlayIndex ?? 0) + 1;
    if (widget.currentPlayIndex! >= srcs.length) {
      widget.currentPlayIndex = 0;
    }
    await initializePlayer();
  }

  //Building the widget
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: AppTheme.light.copyWith(
        platform: _platform ?? Theme.of(context).platform,
      ),
      home: Scaffold(
        body: Column(
          children: <Widget>[
            Expanded(
              child: Center(
                child: _chewieController != null && _chewieController!
                  .videoPlayerController.value.isInitialized
                  ? Chewie(controller: _chewieController!,)
                const Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    CircularProgressIndicator(),
                    SizedBox(height: 20),
                    Text('Loading'),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      );
    }
  }

// Theme
class AppTheme {
  static final light = ThemeData(
    brightness: Brightness.light,
    useMaterial3: true,
    colorScheme: const ColorScheme.light(secondary: Colors.red),
    disabledColor: Colors.grey.shade400,
    visualDensity: VisualDensity.adaptivePlatformDensity,
  );

  static final dark = ThemeData(
    brightness: Brightness.dark,
    colorScheme: const ColorScheme.dark(secondary: Colors.red),
    disabledColor: Colors.grey.shade400,
    useMaterial3: true,
    visualDensity: VisualDensity.adaptivePlatformDensity,
  );
}

So I tried to download the app, run it on web, run it on andriod studio, etc with no hope. I was expecting to simply have the video player playing the videosList if it's not empty, and if it's empty then it'll instead use the videoPath as its first entry and play that video. I tried to remove all of that logic and instead hardcode the video and tried several different ones used in Chewie offical documentaions with no hope.

If anyone can figure what might be causing this problem I will greatly apperciate you. If I'm not mistaken, this might be my first ever StackOverFlow question after spending 8 hours with ChatGPT trying to debug it haha.

1

There are 1 answers

0
Prashant On

Step 1: Change compileSdkVersion in android>app>build.gradle

From compileSdkVersion flutter.compileSdkVersion

To compileSdkVersion 34

compileSdkVersion is the version of the Android SDK that your app is compiled against during the build process. It determines the set of APIs and features that are available for you to use in your code.

So according to latest version of chewie package you need atleast compileSdkVersion 24

Step 2: Add Internet Permission (It will work without permission in debug mode but in release or debug apk, network calls will not be made if permission is not added in Android Manifest File)

Go to android>app>main>AndroidManifest.xml

<manifest xlmns:android...>
 ...
 <uses-permission android:name="android.permission.INTERNET" /> //TODO: Add this line
 <application ...
</manifest>

Step 3: Initialize your source(srcs) before calling initializePlayer

void initState() {
    super.initState();
    //TODO: Initialize srcs before initializePlayer()
    srcs = [
      'https://assets.mixkit.co/videos/preview/mixkit-daytime-city-traffic-aerial-view-56-large.mp4'
    ];
    initializePlayer();
    // /* srcs = widget.videosList ??
    //     [
    //       widget.videoPath ??
    //       'https://assets.mixkit.co/videos/preview/mixkit-daytime-city-traffic-aerial-view-56-large.mp4']; */
    // srcs = [
    //   'https://assets.mixkit.co/videos/preview/mixkit-daytime-city-traffic-aerial-view-56-large.mp4'
    // ];
  }