Hey, codeproject wonderful community!
I am trying to implement the Chewie Flutter package into my app as a custom widget and I got everything down and made it as customizable as possible. It complies well, but when I run it on the web, android, or any device possible all I get a white container with the loading indicator and the text "loading."
The good thing is, the code complies with no errors!
Here's my custom widget code, it's really simple. All I did was get Pubdev Chewie package code, add dependencies, add as many params as possible that will define the autoplay, looping, etc as per Chewie's documentation, and remove the unnecessary appbar and bottom texts in the pubdev code.
I also have videoPath and videosList just in case if the user would like to use a playlist or just one video path. The pubdev code uses a list, so I used a list and would check if the param is null, if so it'll use the videoPath singular link as the first index instead. I commented that code out and hardcoded a video link that I found on Chewie's Pubdev page and several other ones with no hope.
<pre>
import '/backend/backend.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart';
import '/custom_code/actions/index.dart';
import '/flutter_flow/custom_functions.dart';
import 'package:flutter/material.dart';
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;
@override
void initState() {
super.initState();
initializePlayer();
srcs = [
'https://assets.mixkit.co/videos/preview/mixkit-daytime-city-traffic-aerial-view-56-large.mp4'
];
}
@override
void dispose() {
_videoPlayerController1.dispose();
_videoPlayerController2.dispose();
_chewieController?.dispose();
super.dispose();
}
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(() {});
}
void _createChewieController() {
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:
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),
),
),
);
}
Future<void> toggleVideo() async {
await _videoPlayerController1.pause();
widget.currentPlayIndex = (widget.currentPlayIndex ?? 0) + 1;
if (widget.currentPlayIndex! >= srcs.length) {
widget.currentPlayIndex = 0;
}
await initializePlayer();
}
@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'),
],
),
),
),
],
),
),
);
}
}
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,
);
}
What I have tried:
So I tried to download the app, run it on the web, run it on Android 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's official documentation with no hope.
If anyone can figure out what might be causing this problem I will greatly appreciate you.