Flutter"s built-in video tools can feel limited when an app needs smooth playback, reliable controls, and a consistent viewing experience across devices. Chewie steps in to solve this gap by offering a simple, customizable layer over the video_player package. It allows developers to handle daily playback needs (like controls and full-screen behavior) without building everything right from scratch.
Using Chewie will allow you to create a clean workflow and keep video features predictable, which matters when an app relies heavily on media. This introduction sets the stage for you to understand why Chewie becomes the practical choice for improving Flutter video playback.
Prerequisites
Before you begin, ensure you have these in place:
- Flutter SDK and an IDE like Android Studio or VS Code installed.
- Basic Dart and Flutter widget familiarity.
- Access to video files on secure HTTPS URLs.
- chewie (version 1.7.5 or later) and video_player (version 2.6.1 or later) packages included in your Flutter project.
Project Setup
Edit pubspec.yaml to add dependencies:
dependencies:
chewie: ^1.7.5
video_player: ^2.6.1After saving, run flutter pub get in your terminal.
Import the necessary packages in your Dart file:
import 'package:flutter/material.dart';
import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';This brings in Chewie, which builds on video_player to give you ready-made controls for videos. Think about your video source next. Use an HTTPS URL to load the video safely. If the video needs a user login, plan to add that later.
Core Implementation
Initialize both controllers, making use of VideoPlayerController.networkUrl with a parsed Uri for modern best practice, supporting headers if needed (e.g., auth tokens):
class VideoScreen extends StatefulWidget {
@override
_VideoScreenState createState() => _VideoScreenState();
}
class _VideoScreenState extends State<VideoScreen> {
late VideoPlayerController _videoPlayerController;
late ChewieController _chewieController;
bool _isLoading = true;
bool _hasError = false;
@override
void initState() {
super.initState();
_videoPlayerController = VideoPlayerController.networkUrl(
Uri.parse('https://example.com/your-video.mp4'),
// Optional headers for secured video access.
// httpHeaders: {'Authorization': 'Bearer your_token'},
);
_videoPlayerController.initialize().then((_) {
setState(() {
_isLoading = false;
});
}).catchError((error) {
setState(() {
_hasError = true;
_isLoading = false;
});
print('Video initialization error: $error');
});
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
aspectRatio: 16 / 9,
autoPlay: false,
looping: false,
showControls: true,
errorBuilder: (context, errorMessage) {
return Center(
child: Text('Video could not load: $errorMessage'),
);
},
materialProgressColors: ChewieProgressColors(
playedColor: Colors.red,
handleColor: Colors.blue,
backgroundColor: Colors.grey,
),
// Options like allowFullScreen, allowPlaybackSpeedChanging, placeholder, etc., can be added here.
);
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (_hasError) {
return const Center(child: Text('Failed to load video.'));
}
return Scaffold(
appBar: AppBar(title: const Text('Chewie Video Player')),
body: Center(child: Chewie(controller: _chewieController)),
);
}
@override
void dispose() {
_chewieController.dispose();
_videoPlayerController.dispose();
super.dispose();
}
}This code sets up the controllers. The ChewieController handles the video and adds controls automatically. It keeps the video in a 16:9 shape and doesn't start playing on its own.
Building the Video Player
With the controllers ready, the Chewie widget does most of the work. It shows the video with play buttons, a progress bar, and volume controls right out of the box. Place it in the center of your screen, and users can tap to play or pause.
If the video isn't loading, check your URL. The widget waits for the video to be ready before showing it. Once it plays, you see the full controls overlaying the video. This setup lets you focus on the app's other parts while Chewie manages the playback details.
Customizing Controls
Chewie provides multiple customization options on ChewieController:
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
aspectRatio: 16 / 9,
autoPlay: false,
looping: false,
showControls: true,
materialProgressColors: ChewieProgressColors(
playedColor: Colors.red,
handleColor: Colors.blue,
backgroundColor: Colors.grey,
),
);Now, the progress bar is red for played parts, blue for the handle, and grey for the rest. You can turn off controls by setting showControls to false, or add subtitles if your video has them. Test these changes by running the app. Adjust the colors or settings until they fit your app's style.
Handling Errors and Improving Playback
Videos can fail to load, so add error handling. In the ChewieController, you can set an errorBuilder:
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
aspectRatio: 16 / 9,
autoPlay: false,
looping: false,
errorBuilder: (context, errorMessage) {
return Center(
child: Text('Video could not load: $errorMessage'),
);
},
);If something goes wrong, this shows a message instead of a blank screen. For better quality, if your video has different sizes, allow users to switch by changing the controller's source. Run the app on a real device to check how it plays.
Videos might buffer or skip on slow connections, so you need to test them in different spots. Also, you must tweak the settings based on what you see, and you'll have a smooth player with Chewie.

