Building a Flutter Video Player App with Multiple Videos and Full-Screen Mode
In this post, we will walk through creating a simple Flutter app that utilizes the video_player
package to load and play multiple videos, along with supporting features like full-screen mode, volume control, and a custom UI for navigating between videos.
What We’ll Build
The app will have the following features:
- A video player to play videos from a list of URLs.
- The ability to switch between videos by tapping on a video thumbnail.
- Full-screen mode support, which will display the video in landscape orientation.
- Play/Pause functionality and volume control.
- A sleek UI with a video list and controls.
Let’s dive into the code!
Step 1: Setting Up Dependencies
To start, we need to add the video_player
package to your pubspec.yaml
file:
dependencies:
flutter:
sdk: flutter
video_player: ^2.5.0 # Check for the latest version
After adding this dependency, run flutter pub get
to install the package.
Step 2: Creating the Video Player App
Here’s the main code for the app:
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:flutter/services.dart';
void main() => runApp(const VideoApp());
class VideoApp extends StatefulWidget {
const VideoApp({super.key});
@override
_VideoAppState createState() => _VideoAppState();
}
class _VideoAppState extends State<VideoApp> {
late VideoPlayerController _controller;
List<String> videoUrls = [
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4',
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4',
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/VolkswagenGTIReview.mp4',
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
];
double _volume = 1.0; // Volume level
bool _isFullScreen = false; // Full screen state
@override
void initState() {
super.initState();
_controller = VideoPlayerController.network(videoUrls[0])
..initialize().then((_) {
_controller.play();
setState(() {});
});
}
void changeVideo(int index) {
setState(() {
_controller.pause(); // Pause the current video
_controller = VideoPlayerController.network(videoUrls[index])
..initialize().then((_) {
_controller.play();
setState(() {});
});
});
}
void toggleFullScreen(BuildContext context) {
setState(() {
_isFullScreen = !_isFullScreen;
});
if (_isFullScreen) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeRight]);
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => FullScreenVideoPlayer(controller: _controller),
));
} else {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
Navigator.of(context).pop();
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Video Demo',
home: Scaffold(
appBar: AppBar(
title: const Text('Video Player Demo'),
),
body: Column(
children: [
Center(
child: _controller.value.isInitialized
? Stack(children: [
AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
),
Positioned.fill(
child: GestureDetector(
onTap: () {
setState(() {
_controller.value.isPlaying
? _controller.pause()
: _controller.play();
});
},
child: Container(
color: Colors.black54,
child: Center(
child: Icon(
_controller.value.isPlaying
? Icons.pause
: Icons.play_arrow,
color: Colors.white,
size: 64.0,
),
),
),
),
),
Positioned(
bottom: 10,
left: 10,
right: 10,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: Icon(
_controller.value.isPlaying
? Icons.pause
: Icons.play_arrow,
color: Colors.white,
),
onPressed: () {
setState(() {
_controller.value.isPlaying
? _controller.pause()
: _controller.play();
});
},
),
IconButton(
icon: Icon(
Icons.volume_up,
color: Colors.white,
),
onPressed: () {
setState(() {
_volume = _volume == 1.0 ? 0.0 : 1.0;
_controller.setVolume(_volume);
});
},
),
IconButton(
icon: Icon(
_isFullScreen
? Icons.fullscreen_exit
: Icons.fullscreen,
color: Colors.white,
),
onPressed: () {
toggleFullScreen(context);
},
),
],
),
),
])
: const CircularProgressIndicator(),
),
VideoProgressIndicator(_controller, allowScrubbing: true),
Expanded(
child: ListView.builder(
itemCount: videoUrls.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () {
changeVideo(index);
},
child: Container(
decoration: BoxDecoration(
color: Colors.white, // White color for the container
boxShadow: [
BoxShadow(
color: Colors.black
.withOpacity(0.1), // Light shadow color
offset: Offset(4.0, 4.0), // Shadow offset
blurRadius: 8.0, // Blur effect
spreadRadius: 2.0, // Spread the shadow a bit
),
],
borderRadius: BorderRadius.circular(12),
),
height: 150,
width: double.infinity,
child: Row(
children: [
SizedBox(width: 15),
Container(
height: 90,
width: 90,
child: Center(
child: Text("${index + 1}"),
),
decoration: BoxDecoration(
color: Color.fromARGB(255, 227, 225, 225),
borderRadius: BorderRadius.circular(12)),
),
],
),
),
),
);
},
),
),
],
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
class FullScreenVideoPlayer extends StatelessWidget {
final VideoPlayerController controller;
const FullScreenVideoPlayer({Key? key, required this.controller})
: super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Center(
child: AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: VideoPlayer(controller),
),
),
Positioned.fill(
child: GestureDetector(
onTap: () {
controller.value.isPlaying
? controller.pause()
: controller.play();
},
child: Container(
color: Colors.black54,
child: Center(
child: Icon(
controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white,
size: 64.0,
),
),
),
),
),
Positioned(
bottom: 10,
left: 10,
right: 10,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: Icon(
controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white,
),
onPressed: () {
controller.value.isPlaying
? controller.pause()
: controller.play();
},
),
IconButton(
icon: Icon(
Icons.volume_up,
color: Colors.white,
),
onPressed: () {
// Toggle volume logic can be added here
},
),
IconButton(
icon: Icon(
Icons.fullscreen_exit,
color: Colors.white,
),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
),
],
),
);
}
}
Step 3: Key Features Explained
- Multiple Video URLs: We store a list of video URLs and use
VideoPlayerController
to load the videos. The user can tap on a video thumbnail to switch to another video. - Full-Screen Mode: We toggle full-screen mode using the
SystemChrome.setEnabledSystemUIMode()
method, which hides system UI elements like the status bar and navigation bar when the video is in full-screen. - Play/Pause & Volume Control: The app allows users to play/pause videos by tapping on the screen, and toggle volume between 0 and 1 using a volume button.
- Responsive UI: The UI is designed to handle both portrait and landscape orientations, ensuring a smooth transition to full-screen mode.
Conclusion
This Flutter app is a great starting point to implement a video player with multiple video sources. With features like video switching, full-screen mode, and volume control, it provides an engaging and interactive experience. You can further customize this app by adding additional features like subtitles or background playback to enhance functionality.
Let me know if you have any questions or if you’d like to see more advanced features!