Flutter Camera recorded Video are incompatible with browsers - how to convert to .mp4? - flutter

I'm trying to capture a video and upload it to firebase storage.
The problem is, the recorded video format is .mov and this format seems to be incompatible with browsers.
How could I convert the recorded file to .mp4?
Below is the code I use to record and upload my videos.
import 'dart:io';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:video_player/video_player.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:path_provider/path_provider.dart';
class CameraTestPage extends StatefulWidget {
const CameraTestPage({Key? key}) : super(key: key);
#override
_CameraTestPageState createState() => _CameraTestPageState();
}
class _CameraTestPageState extends State<CameraTestPage> {
List<CameraDescription>? cameras;
CameraController? controller;
bool frontCamera = false;
XFile? videoFile;
VideoPlayerController? videoController;
VoidCallback? videoPlayerListener;
#override
void initState() {
initCamera();
super.initState();
}
initCamera() async {
if (cameras == null) {
cameras = await availableCameras();
}
if (controller?.value.isInitialized ?? false) {
controller!.dispose();
}
controller = CameraController(
cameras!.firstWhere(
(element) => element.lensDirection == (frontCamera ? CameraLensDirection.front : CameraLensDirection.back),
),
ResolutionPreset.medium,
enableAudio: false,
);
controller!.initialize().then((value) {
setState(() {});
});
}
void onVideoRecordButtonPressed() {
startVideoRecording().then((_) {
if (mounted) setState(() {});
});
}
void onStopButtonPressed() {
stopVideoRecording().then((file) {
if (mounted) setState(() {});
if (file != null) {
print('Video recorded to ${file.path}');
videoFile = file;
_startVideoPlayer();
}
});
}
Future<void> _startVideoPlayer() async {
if (videoFile == null) {
return;
}
final VideoPlayerController vController = VideoPlayerController.file(File(videoFile!.path));
videoPlayerListener = () {
if (videoController != null && videoController!.value.size != null) {
// Refreshing the state to update video player with the correct ratio.
if (mounted) setState(() {});
videoController!.removeListener(videoPlayerListener!);
}
};
vController.addListener(videoPlayerListener!);
await vController.setLooping(true);
await vController.initialize();
await videoController?.dispose();
if (mounted) {
setState(() {
videoController = vController;
});
}
await vController.play();
}
Future<void> startVideoRecording() async {
final CameraController? cameraController = controller;
if (cameraController == null || !cameraController.value.isInitialized) {
print('Error: select a camera first.');
return;
}
if (cameraController.value.isRecordingVideo) {
// A recording is already started, do nothing.
return;
}
try {
await cameraController.startVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
return;
}
}
Future<XFile?> stopVideoRecording() async {
final CameraController? cameraController = controller;
if (cameraController == null || !cameraController.value.isRecordingVideo) {
return null;
}
try {
return cameraController.stopVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
}
void _showCameraException(CameraException e) {
print('Error: ${e.code}\n${e.description}');
}
_uploadVideo() async {
Reference ref = FirebaseStorage.instance.ref("test/test.mov");
UploadTask uploadTask = ref.putFile(File(videoFile!.path), SettableMetadata(contentType: 'video/mov'));
print("uploading");
uploadTask.whenComplete(() async {
String downloadUrl = await ref.getDownloadURL();
print("download url: $downloadUrl");
});
}
Widget _thumbnailWidget() {
final VideoPlayerController? localVideoController = videoController;
return Expanded(
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
child: (localVideoController == null)
? Center(
child: Container(
child: Text("none"),
),
)
: Container(
child: AspectRatio(
aspectRatio: localVideoController.value.aspectRatio,
child: VideoPlayer(localVideoController),
),
decoration: BoxDecoration(
border: Border.all(color: Colors.pink),
),
),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Camera Test")),
body: Column(
children: [
if (controller != null)
Container(
child: CameraPreview(controller!),
height: 400,
),
Row(
children: [
ElevatedButton(
onPressed: () {
frontCamera = !frontCamera;
initCamera();
},
child: Text("Switch"),
),
ElevatedButton(
onPressed: () {
controller?.value.isRecordingVideo ?? false ? onStopButtonPressed() : onVideoRecordButtonPressed();
},
child: Text(controller?.value.isRecordingVideo ?? false ? "Stop" : "Record"),
),
ElevatedButton(
onPressed: videoFile != null ? _uploadVideo : null,
child: Text("Upload video"),
),
],
),
_thumbnailWidget(),
],
),
);
}
}

You can use the video_compress package to convert the video to mp4.
Here is your _uploadVideo() method updated to include this:
_uploadVideo() async {
Reference ref = FirebaseStorage.instance.ref("test/test.mp4");
MediaInfo? mediaInfo = await VideoCompress.compressVideo(
videoFile!.path,
quality: VideoQuality.DefaultQuality,
deleteOrigin: false, // It's false by default
);
UploadTask uploadTask = ref.putFile(
File(mediaInfo!.path!), SettableMetadata(contentType: 'video/mp4'));
print("uploading");
uploadTask.whenComplete(() async {
String downloadUrl = await ref.getDownloadURL();
print("download url: $downloadUrl");
});
}
Ensure you import the package by including this line:
import 'package:video_compress/video_compress.dart';

Related

Unhandled Exception: type 'Null' is not a subtype of type 'LocationDto'

I am using [background_locator_2][1] plugin, However when I run it with some modification I get this error
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'Null' is not a subtype of type 'LocationDto'
This is the code i am using
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ReceivePort port = ReceivePort();
String logStr = '';
bool? isRunning;
LocationDto? lastLocation;
#override
void initState() {
super.initState();
if (IsolateNameServer.lookupPortByName(
LocationServiceRepository.isolateName) !=
null) {
IsolateNameServer.removePortNameMapping(
LocationServiceRepository.isolateName);
}
IsolateNameServer.registerPortWithName(
port.sendPort, LocationServiceRepository.isolateName);
port.listen(
(dynamic data) async {
await updateUI(data);
},
);
initPlatformState();
}
#override
void dispose() {
super.dispose();
}
Future<void> updateUI(LocationDto data) async {
final log = await FileManager.readLogFile();
await _updateNotificationText(data);
setState(() {
lastLocation = data;
logStr = log;
});
}
Future<void> _updateNotificationText(LocationDto data) async {
if (data == null) {
return;
}
await BackgroundLocator.updateNotificationText(
title: "new location received",
msg: "${DateTime.now()}",
bigMsg: "${data.latitude}, ${data.longitude}");
}
Future<void> initPlatformState() async {
print('Initializing...');
await BackgroundLocator.initialize();
logStr = await FileManager.readLogFile();
print('Initialization done');
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
});
print('Running ${isRunning.toString()}');
}
#override
Widget build(BuildContext context) {
final start = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Start'),
onPressed: () {
_onStart();
},
),
);
final stop = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Stop'),
onPressed: () {
onStop();
},
),
);
final clear = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Clear Log'),
onPressed: () {
FileManager.clearLogFile();
setState(() {
logStr = '';
});
},
),
);
String msgStatus = "-";
if (isRunning != null) {
if (isRunning!) {
msgStatus = 'Is running';
} else {
msgStatus = 'Is not running';
}
}
final status = Text("Status: $msgStatus");
final log = Text(
logStr,
);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter background Locator'),
),
body: Container(
width: double.maxFinite,
padding: const EdgeInsets.all(22),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[start, stop, clear, status, log],
),
),
),
),
);
}
void onStop() async {
await BackgroundLocator.unRegisterLocationUpdate();
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
});
}
void _onStart() async {
//if (await isLocationAlwaysGranted()) {
await _startLocator();
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
lastLocation = null;
});
// } else {
// show error
}
}
Future<bool> isLocationAlwaysGranted() async =>
await Permission.locationAlways.isGranted;
/// Tries to ask for "location always" permissions from the user.
/// Returns `true` if successful, `false` othervise.
Future<bool> askForLocationAlwaysPermission() async {
bool granted = await Permission.locationAlways.isGranted;
if (!granted) {
granted =
await Permission.locationAlways.request() == PermissionStatus.granted;
}
return granted;
}
Future<void> _startLocator() async {
Map<String, dynamic> data = {'countInit': 1};
return await BackgroundLocator.registerLocationUpdate(
LocationCallbackHandler.callback,
initCallback: LocationCallbackHandler.initCallback,
initDataCallback: data,
disposeCallback: LocationCallbackHandler.disposeCallback,
iosSettings: IOSSettings(
accuracy: LocationAccuracy.NAVIGATION,
distanceFilter: 0,
stopWithTerminate: true),
autoStop: false,
androidSettings: AndroidSettings(
accuracy: LocationAccuracy.NAVIGATION,
interval: 5,
distanceFilter: 0,
client: LocationClient.google,
androidNotificationSettings: AndroidNotificationSettings(
notificationChannelName: 'Location tracking',
notificationTitle: 'Start Location Tracking',
notificationMsg: 'Track location in background',
notificationBigMsg:
'Background location is on to keep the app up-tp-date with your location. This is required for main features to work properly when the app is not running.',
notificationIconColor: Colors.grey,
notificationTapCallback:
LocationCallbackHandler.notificationCallback)));
}
The error is in this line under initState when I start or stop the plugin.
port.listen(
(dynamic data) async {
await updateUI(data);
},
);
The original code didn't have null safety so i tried to modify it. However it is pretty evident my knowledge is limited.
[1]: https://pub.dev/packages/background_locator_2
the data you are listening to from the port (dynamic data) can be null and you are sending it to the function updateUI which does not accept nullable type.
you can either check if the data is not null before calling await updateUI(data); or you can make the function updateUI accepts null, i.e (Future<void> updateUI(LocationDto? data) async {) and handle the case that the data is nullable inside the function

How to play video that picked from image_picker using video_player flutter

How to play video that is picked from image_picker using video_player flutter.
I created a method to pick a video and display a video But the problem is I don’t know how to display a video. But this is what I got so far. It always says that the imageFile is null. Is there any to fix the problem? Please take a look at the code it’s not long.
code:
String fileName1 = '';
String path1 = '';
File? imageFile;
#override
void initState() {
loadVideoPlayer();
super.initState();
}
loadVideoPlayer() {
videoController = VideoPlayerController.file(videoFile!);
videoController.addListener(() {
setState(() {});
});
videoController.initialize().then((value) {
setState(() {});
});
}
\\To display a video
AspectRatio(
aspectRatio: videoController.value.aspectRatio,
child:VideoPlayer(videoController),
),
//Pause And Play
IconButton(
onPressed: () {
if (videoController
.value.isPlaying) {
videoController.pause();
} else {
videoController.play();
}
setState(() {});
},
icon: Icon(videoController.value.isPlaying
? Icons.pause
: Icons.play_arrow)),
//To pick video
void selectVideo() async {
final XFile? results = await picker.pickVideo(source: ImageSource.gallery);
if (results != null) {
path1 = results.path;
fileName1 = results.name.replaceFirst("image_picker", "");
setState(() {});
print(fileName1);
} else {
print('No video picked');
}
setState(() {
videoFile = File(results!.path);
});
}
Do not call the loadVideoPlayer() inside the initState but reload the player every time a video is picked :
class Test extends StatefulWidget {
const Test({Key? key}) : super(key: key);
#override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
VideoPlayerController? _videoPlayerController;
loadVideoPlayer(File file) {
if(_videoPlayerController != null) {
_videoPlayerController!.dispose();
}
_videoPlayerController = VideoPlayerController.file(file);
_videoPlayerController!.initialize().then((value) {
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: selectVideo,
child: Icon(
Icons.add,
),
),
body: Center(
child: Stack(
children: [
if (_videoPlayerController != null) ...[
AspectRatio(
aspectRatio: _videoPlayerController!.value.aspectRatio,
child: VideoPlayer(_videoPlayerController!),
),
]
],
),
),
);
}
void selectVideo() async {
final XFile? results =
await ImagePicker().pickVideo(source: ImageSource.gallery);
if (results != null) {
setState(() {
File file = File(results.path);
loadVideoPlayer(file);
});
} else {
print('No video picked');
}
}
}

flutter - how to create voice record in popup when click mic image using flutter?

mic image code
Center(
child: Padding(
padding: const EdgeInsets.only(top: 1),
child: IconButton(
iconSize: 45,
icon: Ink.image(
image: const AssetImage('assets/mic.png'),
),
onPressed: () {
// do something when the button is pressed
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Recorder12Screen()),
);
// RecorderDialogScreen(context);
},
),
),
),
when click this mic image I want display voice recording page as a pop up window.
recording page image... my output
But I wanna like this
I my voice recording function has 2 pages
and this
2nd page is then the click pause button then display playback page
both pages I wanna display as pop up windows
1st page
2nd page
1st page code
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:kathana/screens/voiceRecord/voiceRecord12/recorder/feature_buttons_view.dart';
import 'package:provider/provider.dart';
import '../../../provider/sign_in_provider.dart';
class Recorder12Screen extends StatefulWidget {
#override
State<Recorder12Screen> createState() => _Recorder12ScreenState();
}
class _Recorder12ScreenState extends State<Recorder12Screen> {
String? downloadURL;
List<Reference> references = [];
#override
void initState() {
super.initState();
_onUploadComplete();
}
//snack bar for showing error
showSnackBar(String snackText, Duration d) {
final snackBar = SnackBar(content: Text(snackText), duration: d);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
#override
Widget build(BuildContext context) {
final sp = context.read<SignInProvider>();
return Scaffold(
body: Column(
children: [
Expanded(
flex: 2,
child: FeatureButtonsView(
onUploadComplete: _onUploadComplete,
),
),
Text(
"${sp.uid}",
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
),
],
),
);
}
Future<void> _onUploadComplete() async {
final sp = context.read<SignInProvider>();
FirebaseStorage firebaseStorage = FirebaseStorage.instance;
ListResult listResult = await firebaseStorage
.ref()
.child("${sp.uid}/records")
.child("voices")
.list();
setState(() {
references = listResult.items;
});
}
}
2nd page code
import 'dart:io';
import 'package:audioplayers/audioplayers.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_audio_recorder2/flutter_audio_recorder2.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import '../../../../provider/sign_in_provider.dart';
class FeatureButtonsView extends StatefulWidget {
final Function onUploadComplete;
FeatureButtonsView({
Key? key,
required this.onUploadComplete,
}) : super(key: key);
#override
_FeatureButtonsViewState createState() => _FeatureButtonsViewState();
String? userId;
}
class _FeatureButtonsViewState extends State<FeatureButtonsView> {
late bool _isPlaying;
late bool _isUploading;
late bool _isRecorded;
late bool _isRecording;
late AudioPlayer _audioPlayer;
late String _filePath;
late FlutterAudioRecorder2 _audioRecorder;
Future getData() async {
final sp = context.read<SignInProvider>();
sp.getDataFromSharedPreferences();
}
String downloadUrl = '';
Future<void> onsend() async {
//uploading to cloudfirestore
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
final sp = context.read<SignInProvider>();
await firebaseFirestore
.collection("users")
.doc("${sp.uid}")
.collection("reco")
.add({'downloadURL': downloadUrl}).whenComplete(() =>
showSnackBar("Voice uploaded successful", Duration(seconds: 2)));
}
//snackbar for showing error
showSnackBar(String snackText, Duration d) {
final snackBar = SnackBar(content: Text(snackText), duration: d);
}
#override
void initState() {
super.initState();
_isPlaying = false;
_isUploading = false;
_isRecorded = false;
_isRecording = false;
_audioPlayer = AudioPlayer();
final sp = context.read<SignInProvider>();
FirebaseFirestore.instance
.collection("users")
.doc(sp.uid)
.get()
.then((value) {
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return Center(
child: _isRecorded
? _isUploading
? Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: LinearProgressIndicator()),
Text('Uplaoding to Firebase'),
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(
icon: Icon(Icons.replay),
onPressed: _onRecordAgainButtonPressed,
),
IconButton(
icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow),
onPressed: _onPlayButtonPressed,
),
IconButton(
icon: Icon(Icons.upload_file, color: Colors.green),
onPressed: _onFileUploadButtonPressed,
),
],
)
: IconButton(
icon: _isRecording
? Icon(Icons.pause)
: Icon(Icons.fiber_manual_record),
onPressed: _onRecordButtonPressed,
),
);
}
Future<void> _onFileUploadButtonPressed() async {
FirebaseStorage firebaseStorage = FirebaseStorage.instance;
setState(() {
_isUploading = true;
});
try {
final sp = context.read<SignInProvider>();
Reference ref = firebaseStorage.ref().child("${sp.uid}/records1").child(
_filePath.substring(_filePath.lastIndexOf('/'), _filePath.length));
TaskSnapshot uploadedFile = await ref.putFile(File(_filePath));
if (uploadedFile.state == TaskState.success) {
downloadUrl = await ref.getDownloadURL();
}
widget.onUploadComplete();
onsend(); //send downloadURL after get it
} catch (error) {
print('Error occured while uplaoding to Firebase ${error.toString()}');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error occured while uplaoding'),
),
);
} finally {
setState(() {
_isUploading = false;
});
}
}
void _onRecordAgainButtonPressed() {
setState(() {
_isRecorded = false;
});
}
Future<void> _onRecordButtonPressed() async {
if (_isRecording) {
_audioRecorder.stop();
_isRecording = false;
_isRecorded = true;
} else {
_isRecorded = false;
_isRecording = true;
await _startRecording();
}
setState(() {});
}
void _onPlayButtonPressed() {
if (!_isPlaying) {
_isPlaying = true;
_audioPlayer.play(_filePath, isLocal: true);
_audioPlayer.onPlayerCompletion.listen((duration) {
setState(() {
_isPlaying = false;
});
});
} else {
_audioPlayer.pause();
_isPlaying = false;
}
setState(() {});
}
Future<void> _startRecording() async {
final bool? hasRecordingPermission =
await FlutterAudioRecorder2.hasPermissions;
if (hasRecordingPermission ?? false) {
Directory directory = await getApplicationDocumentsDirectory();
String filepath = directory.path +
'/' +
DateTime.now().millisecondsSinceEpoch.toString() +
'.aac';
_audioRecorder =
FlutterAudioRecorder2(filepath, audioFormat: AudioFormat.AAC);
await _audioRecorder.initialized;
_audioRecorder.start();
_filePath = filepath;
setState(() {});
} else {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Center(child: Text('Please enable recording permission'))));
}
}
}
Those 2 pages, how convert to like as a popup?
My voice recording functions work perfectly I'm asking to convert those two material pages to like popup windows.
You can use this package for recording the voice :
dependencies:
flutter_sound: ^9.2.13

how to automatically trigger record

I have a audio recording page which starts when I tap in the button and stop when i tap the button once again
import 'package:flutter_sound/flutter_sound.dart';
import 'package:permission_handler/permission_handler.dart';
class AudioWidget extends StatefulWidget {
#override
State<AudioWidget> createState() => _AudioWidgetState();
}
``class _AudioWidgetState extends State<AudioWidget> {
final FirebaseAuth _auth = FirebaseAuth.instance;
String AudioUrl = '';
final recorder = FlutterSoundRecorder();
bool isRecorderReady = false;
#override
void initState() {
super.initState();
initRecorder();
record();
}
#override
void dispose() {
recorder.closeRecorder();
super.dispose();
}
Future initRecorder() async {
final status = await Permission.microphone.request();
if (status != PermissionStatus.granted) {
throw 'Microphone permission not granted';
}
await recorder.openRecorder();
isRecorderReady = true;
recorder.setSubscriptionDuration(const Duration(milliseconds: 500));
}
Future record() async {
if (!isRecorderReady) {
return;
}
await recorder.startRecorder(toFile: 'audio');
}
Future stop() async {
if (!isRecorderReady) {
return;
}
final path = await recorder.stopRecorder();
final audioFile = File(path!);
print('Recorder audio: $audioFile');
final ref = FirebaseStorage.instance
.ref()
.child('Audio')
.child(DateTime.now().toIso8601String() + ".mp3");
await ref.putData(audioFile.readAsBytesSync());
AudioUrl = await ref.getDownloadURL();
FirebaseFirestore.instance.collection('Audio').add({
'AudioUrl': AudioUrl,
'userId': _auth.currentUser!.email,
'createdAt': DateTime.now(),
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Audio"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
StreamBuilder<RecordingDisposition>(
stream: recorder.onProgress,
builder: (context, snapshot) {
final duration = snapshot.hasData
? snapshot.data!.duration
: Duration.zero;
String twoDigits(int n) => n.toString().padLeft(2, '0');
final twoDigitMinutes =
twoDigits(duration.inMinutes.remainder(60));
final twoDigitSeconds =
twoDigits(duration.inSeconds.remainder(60));
if (twoDigitSeconds ==
FFAppState().AudioMaxDuration.toString()) {
stop();
}
return Text('$twoDigitMinutes:$twoDigitSeconds',
style: const TextStyle(
fontSize: 80,
fontWeight: FontWeight.bold,
));
}),
const SizedBox(height: 32),
ElevatedButton(
child: Icon(
recorder.isRecording ? Icons.stop : Icons.mic,
size: 80,
),
onPressed: () async {
if (recorder.isRecording) {
await stop();
context.pop();
} else {
await record();
}
setState(() {});
},
),
],
)),
),
);
}
}
how can I automatically start recording once I open the audio recording page and tap it to stop and tap again to start?
I tried to put the record function in initstate but it didn't work
void initState() {
super.initState();
initRecorder();
record();
}

Flutter - The getter 'value' was called on null

I'm trying to save to SharedPreferences the current camera selected. When the app restarts, I would like the same camera to be used initially. If the user toggles to another camera, that camera gets saved.
When I have the following code in the initState, I get the "The getter 'value' was called on null." error message. Also the red/yellow error messages show up and disappear quickly before the camera starts working.
#override
void initState() {
try {
SharedPreferencesHelper prefs = SharedPreferencesHelper();
prefs.getCameraSelected().then((String answer) {
if (answer != null) {
if (answer == 'back') {
onCameraSelected(widget.cameras[0]);
} else {
onCameraSelected(widget.cameras[1]);
}
} else {
print('answer is null');
onCameraSelected(widget.cameras[1]);
}
});
} catch (e) {
print(e.toString());
}
super.initState();
}
If I replace the initState back to the original state, everything works as expected
#override
void initState() {
try {
onCameraSelected(widget.cameras[0]);
} catch (e) {
print(e.toString());
}
super.initState();
}
Below is the full code for the CameraHomeScreen.dart file:
import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class CameraHomeScreen extends StatefulWidget {
List<CameraDescription> cameras;
CameraHomeScreen(this.cameras);
#override
State<StatefulWidget> createState() {
return _CameraHomeScreenState();
}
}
class _CameraHomeScreenState extends State<CameraHomeScreen> {
String imagePath;
bool _toggleCamera = false;
String _currentCamera = 'back';
CameraController controller;
#override
void initState() {
try {
SharedPreferencesHelper prefs = SharedPreferencesHelper();
prefs.getCameraSelected().then((String answer) {
if (answer != null) {
print('here');
print(answer);
if (answer == 'back') {
onCameraSelected(widget.cameras[0]);
} else {
onCameraSelected(widget.cameras[1]);
}
} else {
print('answer is null');
onCameraSelected(widget.cameras[1]);
}
});
} catch (e) {
print(e.toString());
}
super.initState();
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
if (widget.cameras.isEmpty) {
return Container(
alignment: Alignment.center,
padding: EdgeInsets.all(16.0),
child: Text(
'No Camera Found',
style: TextStyle(
fontSize: 16.0,
color: Colors.white,
),
),
);
}
if (!controller.value.isInitialized) {
return Container();
}
return AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: Container(
child: Stack(
children: <Widget>[
CameraPreview(controller),
Align(
alignment: Alignment.bottomCenter,
child: Container(
width: double.infinity,
height: 120.0,
padding: EdgeInsets.all(20.0),
color: Color.fromRGBO(00, 00, 00, 0.7),
child: Stack(
children: <Widget>[
Align(
alignment: Alignment.center,
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.all(Radius.circular(50.0)),
onTap: () {
_captureImage();
},
child: Container(
padding: EdgeInsets.all(4.0),
child: Image.asset(
'assets/images/ic_shutter_1.png',
width: 72.0,
height: 72.0,
),
),
),
),
),
Align(
alignment: Alignment.centerRight,
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.all(Radius.circular(50.0)),
onTap: () {
if (!_toggleCamera) {
SharedPreferencesHelper prefs =
SharedPreferencesHelper();
prefs.setCameraSelected('front');
onCameraSelected(widget.cameras[1]);
setState(() {
_toggleCamera = true;
});
} else {
SharedPreferencesHelper prefs =
SharedPreferencesHelper();
prefs.setCameraSelected('back');
onCameraSelected(widget.cameras[0]);
setState(() {
_toggleCamera = false;
});
}
},
child: Container(
padding: EdgeInsets.all(4.0),
child: Image.asset(
'assets/images/ic_switch_camera_3.png',
color: Colors.grey[200],
width: 42.0,
height: 42.0,
),
),
),
),
),
],
),
),
),
],
),
),
);
}
void onCameraSelected(CameraDescription cameraDescription) async {
if (controller != null) await controller.dispose();
controller = CameraController(cameraDescription, ResolutionPreset.medium);
controller.addListener(() {
if (mounted) setState(() {});
if (controller.value.hasError) {
showMessage('Camera Error: ${controller.value.errorDescription}');
}
});
try {
await controller.initialize();
} on CameraException catch (e) {
showException(e);
}
if (mounted) setState(() {});
}
String timestamp() => new DateTime.now().millisecondsSinceEpoch.toString();
void _captureImage() {
takePicture().then((String filePath) {
if (mounted) {
setState(() {
imagePath = filePath;
});
if (filePath != null) {
showMessage('Picture saved to $filePath');
setCameraResult();
}
}
});
}
void setCameraResult() {
Navigator.pop(context, imagePath);
}
Future<String> takePicture() async {
if (!controller.value.isInitialized) {
showMessage('Error: select a camera first.');
return null;
}
final Directory extDir = await getApplicationDocumentsDirectory();
final String dirPath = '${extDir.path}/FlutterDevs/Camera/Images';
await new Directory(dirPath).create(recursive: true);
final String filePath = '$dirPath/${timestamp()}.jpg';
if (controller.value.isTakingPicture) {
// A capture is already pending, do nothing.
return null;
}
try {
await controller.takePicture(filePath);
} on CameraException catch (e) {
showException(e);
return null;
}
return filePath;
}
void showException(CameraException e) {
logError(e.code, e.description);
showMessage('Error: ${e.code}\n${e.description}');
}
void showMessage(String message) {
print(message);
}
void logError(String code, String message) =>
print('Error: $code\nMessage: $message');
}
class SharedPreferencesHelper {
///
/// Instantiation of the SharedPreferences library
///
final String _nameKey = "cameraSelected";
/// ------------------------------------------------------------
/// Method that returns the user decision on sorting order
/// ------------------------------------------------------------
Future<String> getCameraSelected() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString(_nameKey) ?? 'name';
}
/// ----------------------------------------------------------
/// Method that saves the user decision on sorting order
/// ----------------------------------------------------------
Future<bool> setCameraSelected(String value) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.setString(_nameKey, value);
}
}
You can copy paste run full code below
Use SharedPreferences before runApp(MyApp());
and class CameraHomeScreen add a parameter for initCamera
and use it like this CameraHomeScreen(cameras, initCamera),
code snippet
String initCamera;
List<CameraDescription> cameras;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
cameras = await availableCameras();
SharedPreferences pre = await SharedPreferences.getInstance();
//await prefs.setString("cameraSelected","front");
initCamera = await pre.getString("cameraSelected");
print('initCamera ${initCamera}');
runApp(MyApp());
}
...
class CameraHomeScreen extends StatefulWidget {
List<CameraDescription> cameras;
String initCamera;
CameraHomeScreen(this.cameras, this.initCamera);
...
void initState() {
print( 'widget.initCamera ${widget.initCamera}' );
if (widget.initCamera == 'back') {
onCameraSelected(widget.cameras[0]);
} else {
onCameraSelected(widget.cameras[1]);
}
super.initState();
}
demo
full code
import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
String initCamera;
List<CameraDescription> cameras;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
cameras = await availableCameras();
SharedPreferences pre = await SharedPreferences.getInstance();
//await prefs.setString("cameraSelected","front");
initCamera = await pre.getString("cameraSelected");
print('initCamera ${initCamera}');
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: CameraHomeScreen(cameras, initCamera),
);
}
}
class CameraHomeScreen extends StatefulWidget {
List<CameraDescription> cameras;
String initCamera;
CameraHomeScreen(this.cameras, this.initCamera);
#override
State<StatefulWidget> createState() {
return _CameraHomeScreenState();
}
}
class _CameraHomeScreenState extends State<CameraHomeScreen> {
String imagePath;
bool _toggleCamera = false;
String _currentCamera = 'back';
CameraController controller;
#override
void initState() {
print( 'widget.initCamera ${widget.initCamera}' );
if (widget.initCamera == 'back') {
onCameraSelected(widget.cameras[0]);
} else {
onCameraSelected(widget.cameras[1]);
}
super.initState();
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
if (widget.cameras.isEmpty) {
return Container(
alignment: Alignment.center,
padding: EdgeInsets.all(16.0),
child: Text(
'No Camera Found',
style: TextStyle(
fontSize: 16.0,
color: Colors.white,
),
),
);
}
if (!controller.value.isInitialized) {
return Container();
}
return AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: Container(
child: Stack(
children: <Widget>[
CameraPreview(controller),
Align(
alignment: Alignment.bottomCenter,
child: Container(
width: double.infinity,
height: 120.0,
padding: EdgeInsets.all(20.0),
color: Color.fromRGBO(00, 00, 00, 0.7),
child: Stack(
children: <Widget>[
Align(
alignment: Alignment.center,
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.all(Radius.circular(50.0)),
onTap: () {
_captureImage();
},
child: Container(
padding: EdgeInsets.all(4.0),
child: Image.asset(
'assets/images/ic_shutter_1.png',
width: 72.0,
height: 72.0,
),
),
),
),
),
Align(
alignment: Alignment.centerRight,
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.all(Radius.circular(50.0)),
onTap: () {
if (!_toggleCamera) {
SharedPreferencesHelper prefs =
SharedPreferencesHelper();
prefs.setCameraSelected('front');
print("front");
onCameraSelected(widget.cameras[1]);
setState(() {
_toggleCamera = true;
});
} else {
SharedPreferencesHelper prefs =
SharedPreferencesHelper();
prefs.setCameraSelected('back');
print("back");
onCameraSelected(widget.cameras[0]);
setState(() {
_toggleCamera = false;
});
}
},
child: Container(
padding: EdgeInsets.all(4.0),
child: Image.asset(
'assets/images/ic_switch_camera_3.png',
color: Colors.grey[200],
width: 42.0,
height: 42.0,
),
),
),
),
),
],
),
),
),
],
),
),
);
}
void onCameraSelected(CameraDescription cameraDescription) async {
if (controller != null) await controller.dispose();
controller = CameraController(cameraDescription, ResolutionPreset.medium);
controller.addListener(() {
if (mounted) setState(() {});
if (controller.value.hasError) {
showMessage('Camera Error: ${controller.value.errorDescription}');
}
});
try {
await controller.initialize();
} on CameraException catch (e) {
showException(e);
}
if (mounted) setState(() {});
}
String timestamp() => new DateTime.now().millisecondsSinceEpoch.toString();
void _captureImage() {
takePicture().then((String filePath) {
if (mounted) {
setState(() {
imagePath = filePath;
});
if (filePath != null) {
showMessage('Picture saved to $filePath');
setCameraResult();
}
}
});
}
void setCameraResult() {
Navigator.pop(context, imagePath);
}
Future<String> takePicture() async {
if (!controller.value.isInitialized) {
showMessage('Error: select a camera first.');
return null;
}
final Directory extDir = await getApplicationDocumentsDirectory();
final String dirPath = '${extDir.path}/FlutterDevs/Camera/Images';
await new Directory(dirPath).create(recursive: true);
final String filePath = '$dirPath/${timestamp()}.jpg';
if (controller.value.isTakingPicture) {
// A capture is already pending, do nothing.
return null;
}
try {
await controller.takePicture(filePath);
} on CameraException catch (e) {
showException(e);
return null;
}
return filePath;
}
void showException(CameraException e) {
logError(e.code, e.description);
showMessage('Error: ${e.code}\n${e.description}');
}
void showMessage(String message) {
print(message);
}
void logError(String code, String message) =>
print('Error: $code\nMessage: $message');
}
class SharedPreferencesHelper {
///
/// Instantiation of the SharedPreferences library
///
final String _nameKey = "cameraSelected";
/// ------------------------------------------------------------
/// Method that returns the user decision on sorting order
/// ------------------------------------------------------------
Future<String> getCameraSelected() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString(_nameKey) ?? 'name';
}
/// ----------------------------------------------------------
/// Method that saves the user decision on sorting order
/// ----------------------------------------------------------
Future<bool> setCameraSelected(String value) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.setString(_nameKey, value);
}
}