- Home ›
- Categories ›
- Flutter
Tutorial: Video Recording and Replay with Flutter
Recording videos and preview the result can be easily done in Flutter. This tutorial shows how to create a record and replay flow using the camera
and video_player
packages.
Introduction
In this tutorial, we will use the camera
and the video_player
package to create a Flutter app flow to record and replay videos. The recorded video could then be used to – for example – upload it to a server.
First, we will start by creating a page which allows us to view the camera input and record a video.
Once the video has been recorded, we will open another page replaying the recorded video and allow the user to either accept or dismiss the video.
The source code of this tutorial is available on GitHub: https://github.com/bettercoding-dev/flutter-video ⇗.
Prerequisites
We first start with creating a new Flutter project.
Have a look at the steps I make for every Flutter app: 5 Things I do when starting a new Flutter project.
Next, we clean up our pubspec.yaml
file and add the necessary dependencies. After that, the content of pubspec.yaml
should look like this:
name: flutter_video
description: Flutter Video Flow
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: ">=2.12.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
camera: ^0.9.4+3
video_player: ^2.2.6
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.0
flutter:
uses-material-design: true
Code language: YAML (yaml)
You can see that we included the camera
and the video_player
packages.
iOS-specific prerequisites
To make the packages work in iOS we need to insert following code into ios/Runner/Info.plist
:
<key>NSCameraUsageDescription</key>
<string>Needed for recording videos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Needed for recording videos.</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Code language: HTML, XML (xml)
Make sure to put this code within the existing dict
block (before </dict>
).
Android-specific prerequisites
We also need to modify one of the Android-specific files. To make the plugins work on Android, it is necessary to increase the minSdkVersion
to 21
. This can be done in the android/app/build.gradle
file. Just search for minSdkVersion
and replace the value. (Make sure you are not confusing android/build.gradle
with android/app/build.gradle
)
Don’t for get to run pub get
after altering the pubspec.yaml
file.
For our main.dart
file, we will replace old its content with this code:
import 'package:flutter/material.dart';
import 'package:flutter_video/camera_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: CameraPage(),
);
}
}
Code language: Dart (dart)
Note that CameraPage
does not exist yet, but we will create it in the next step.
Viewing Camera Input using the camera
Plugin
Next, we are going to implement CameraPage
that will show the input of the camera and lets us record a video. For this, let’s create a file called camera_page.dart
and create a StatefulWidget
. The stub of this file should look like this:
import 'package:flutter/material.dart';
class CameraPage extends StatefulWidget {
const CameraPage({Key? key}) : super(key: key);
@override
_CameraPageState createState() => _CameraPageState();
}
class _CameraPageState extends State<CameraPage> {
@override
Widget build(BuildContext context) {
return Container();
}
}
Code language: Dart (dart)
Because there is some initialization going on before we can access the camera, we need to display some loading state while the initialization is in progress.
For this, we create a variable _isLoading
in our _CameraPageState
and check the state in the build
function.
class _CameraPageState extends State<CameraPage> {
bool _isLoading = true;
late CameraController _cameraController;
@override
void dispose() {
_cameraController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return Container(
color: Colors.white,
child: const Center(
child: CircularProgressIndicator(),
),
);
} else {
return CameraPreview(_cameraController);
}
}
}
Code language: Dart (dart)
We also added a CameraController
variable and show a CameraPreview
once the loading is done. Since this won’t work unless the _cameraController
has been initialized, we continue with this step.
Initializing the CameraController
First, let’s create a function called _initCamera
.
_initCamera() async {
final cameras = await availableCameras();
final front = cameras.firstWhere((camera) => camera.lensDirection == CameraLensDirection.front);
_cameraController = CameraController(front, ResolutionPreset.max);
await _cameraController.initialize();
setState(() => _isLoading = false);
}
Code language: Dart (dart)
- Line 2: Request all available cameras from the
camera
plugin. - Line 3: Selecting the front-facing camera.
- Line 4: Create an instance of
CameraController
. We are using theCameraDescription
of the front camera and setting the resolution of the video to the maximum. - Line 5: Initialize the controller with the set parameters.
- Line 6: After the initialization, set the
_isLoading
state to false.
Finally, we call the _initCamera()
function in initState()
.
@override
void initState() {
super.initState();
_initCamera();
}
Code language: CSS (css)
When you now start the app, you should be able to see a preview of your front camera.
Recording Video
To start recording the video, we will add a record button. It should overlay the video preview, so we will put the CameraPreview
and the button into a Stack
widget.
Simply replace the content of the else
branch with this code:
return Center(
child: Stack(
alignment: Alignment.bottomCenter,
children: [
CameraPreview(_cameraController),
Padding(
padding: const EdgeInsets.all(25),
child: FloatingActionButton(
backgroundColor: Colors.red,
child: Icon(_isRecording ? Icons.stop : Icons.circle),
onPressed: () => _recordVideo(),
),
),
],
),
);
Code language: Dart (dart)
Now, we need to create a new state variable _isRecording
and set it to false.
bool _isRecording = false;
Code language: Dart (dart)
Finally, let’s create a _recordVideo()
function to handle the button click. This function should look like this:
_recordVideo() async {
if (_isRecording) {
final file = await _cameraController.stopVideoRecording();
setState(() => _isRecording = false);
final route = MaterialPageRoute(
fullscreenDialog: true,
builder: (_) => VideoPage(filePath: file.path),
);
Navigator.push(context, route);
} else {
await _cameraController.prepareForVideoRecording();
await _cameraController.startVideoRecording();
setState(() => _isRecording = true);
}
}
Code language: Dart (dart)
- Line 2: We need to handle both, start and stop recording actions. Therefore, we need to check if the video is currently recording.
- Line 3: If the video is recording, we will stop it. The
stopVideoRecording()
function returns a file containing the recorded video. - Line 4: We update the state and set
_isRecording
to false. - Lines 5-9: Finally, with the recorded file, we will open the
VideoPage
to let the user check on the recorded video. (Note:VideoPage
does not exist yet. It will be added in the next step) - Line 11: If the video is not yet recording, we tell the
CameraController
to prepare for recording. - Line 12: Once the preparation is done, we start the actual recording.
- Line 13: Finally, we set the current state to
_isRecording = true
.
Replaying the Recorded Video
For replaying the recorded video, create a new file called video_page.dart
and add a StatefulWidget
, like we did for the CameraPage
.
The VideoPage
needs to accept a path to the recorded file. So, we add a new attribute filePath
:
class VideoPage extends StatefulWidget {
final String filePath;
const VideoPage({Key? key, required this.filePath}) : super(key: key);
@override
_VideoPageState createState() => _VideoPageState();
}
Code language: Dart (dart)
For the _VideoPageState
class, we start by creating a VideoController
and a function to initialize it.
class _VideoPageState extends State<VideoPage> {
late VideoPlayerController _videoPlayerController;
@override
void dispose() {
_videoPlayerController.dispose();
super.dispose();
}
Future _initVideoPlayer() async {
_videoPlayerController = VideoPlayerController.file(File(widget.filePath));
await _videoPlayerController.initialize();
await _videoPlayerController.setLooping(true);
await _videoPlayerController.play();
}
@override
Widget build(BuildContext context) {
return Container();
}
}
Code language: Dart (dart)
- Line 2: We add a new
_videoPlayerController
variable. - Lines 4-8: Don’t forget to dispose the
VideoPlayerController
once theState
gets disposed. - Line 11: Create a new
VideoPlayerController
from the file path that we passed to this widget. - Line 12: Initialize the
VideoPlayerController
before we can start it. - Line 13: To play the video over and over again, we enable looping.
- Line 14: Finally, we start the video.
As the last step, let’s define our UI. It should have an AppBar
to accept or dismiss the video. And – of course – contain a VideoPlayer
widget to replay the video. The build
function for this widget could look like this:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Preview'),
elevation: 0,
backgroundColor: Colors.black26,
actions: [
IconButton(
icon: const Icon(Icons.check),
onPressed: () {
print('do something with the file');
},
)
],
),
extendBodyBehindAppBar: true,
body: FutureBuilder(
future: _initVideoPlayer(),
builder: (context, state) {
if (state.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else {
return VideoPlayer(_videoPlayerController);
}
},
),
);
}
Code language: PHP (php)
- Line 3: We use a
Scaffold
to create our page. - Lines 4-16: Add an
AppBar
that is transparent and includes an additional “OK” action. There, you could implement additional functionality to e.g. forward the video to a server. - Line 17: This parameter is set, to stretch the video behind the
AppBar
. - Lines 18-27: We use a
FutureBuilder
to show aCircularProgressIndicator
as long as the initialization takes place. As soon as the initialization is done, theVideoPlayer
is shown, replaying the video.
And, we are all done.
You can now start the app to record and replay videos.
Summary
In this tutorial, we used the camera
and video_player
packages to create a flow recording and replaying a video with Flutter.
The source code of this tutorial is available on GitHub: https://github.com/bettercoding-dev/flutter-video ⇗.
Comments
Hi Sir,
This tutorial is clear and understandable.
Thank You for this helpful thing.
thanks a lot!
The most useful and organised and easy to understand tutorial.
Thanks a lot for your efforts.
Thanks a lot
the only tutorial which explained the code clearly and fast
Hi Stefan ,
Very detailed and clear.
Thank you very much, it helped me a lot ..