Related
I'm using Google's ML-Kit barcode scanning plugin along with Flutter's default camera plugin for my project. From my testing, I can see that if I scan a QR code with the current implementation that navigates you to another screen, then navigate back, the whole navigation process is triggered again. Using logging, I've found that it's probably caused because the parseImageForQRCode() function is triggered very fast between _processCameraImage() calls made by the camera's image stream. This in turn doesn't give enough time to the parseImageForQRCode() function to update the _isBusy variable and basically lock the function like a mutex. Is there a recommended way for avoiding such race cases?
camera_screen.dart:
class CameraComponent extends StatefulWidget {
const CameraComponent({Key? key}) : super(key: key);
#override
State<CameraComponent> createState() => _CameraComponentState();
}
class _CameraComponentState extends State<CameraComponent>
with WidgetsBindingObserver {
CameraCubit get _cubit => context.read<CameraCubit>();
CameraController? _controller;
final BarcodeScanner _barcodeScanner = BarcodeScanner();
bool _canProcess = true;
bool _isBusy = false;
CameraDescription? _camera;
final ArtworkRepository _artworkRepository = getIt<ArtworkRepository>();
Future<CameraDescription> getCamera() async {
// Obtain a list of the available cameras on the device.
final List<CameraDescription> cameras = await availableCameras();
// Get a specific camera from the list of available cameras.
return cameras.first;
}
void _navigateToArtworkDetailsById({required int artworkId}) {
context.navigator.pushNamed(
ArtworkViewScreen.path,
arguments: <String, dynamic>{
'artworkId': artworkId,
'source': ArtworkViewSourceScreen.QRScan.name
},
);
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_cubit.init();
startCameraSetup();
}
Future<void> startCameraSetup() async {
_canProcess = true;
_camera = await getCamera();
//todo:sp handle camera being null
if (_camera == null) {
log('camera was null!');
displayErrorDialog('Camera Error',
'Looks like the camera is busy or unavailable! Please try again later.');
return;
}
_controller = CameraController(
_camera!,
ResolutionPreset.high,
enableAudio: false,
);
_controller?.initialize().then((_) {
if (!mounted) {
return;
}
_controller?.startImageStream(_processCameraImage);
setState(() {});
});
}
Future _processCameraImage(CameraImage image) async {
final WriteBuffer allBytes = WriteBuffer();
for (final Plane plane in image.planes) {
allBytes.putUint8List(plane.bytes);
}
final Uint8List bytes = allBytes.done().buffer.asUint8List();
final Size imageSize =
Size(image.width.toDouble(), image.height.toDouble());
final InputImageRotation? imageRotation =
InputImageRotationValue.fromRawValue(_camera!.sensorOrientation);
if (imageRotation == null) {
return;
}
final InputImageFormat? inputImageFormat =
InputImageFormatValue.fromRawValue(image.format.raw);
if (inputImageFormat == null) {
return;
}
final List<InputImagePlaneMetadata> planeData = image.planes.map(
(Plane plane) {
return InputImagePlaneMetadata(
bytesPerRow: plane.bytesPerRow,
height: plane.height,
width: plane.width,
);
},
).toList();
final InputImageData inputImageData = InputImageData(
size: imageSize,
imageRotation: imageRotation,
inputImageFormat: inputImageFormat,
planeData: planeData,
);
final InputImage inputImage =
InputImage.fromBytes(bytes: bytes, inputImageData: inputImageData);
parseImageForQRCode(inputImage);
}
Future parseImageForQRCode(InputImage inputImage) async {
if (!_canProcess) {
return;
}
if (_isBusy) {
return;
}
setState(() {
_isBusy = true;
});
final List<Barcode> barcodes =
await _barcodeScanner.processImage(inputImage);
if (barcodes.isNotEmpty) {
final Barcode qrcode = barcodes[0];
if (qrcode.rawValue != null) {
try {
final int artworkId = int.parse(qrcode.rawValue!.split('_')[1]);
_controller!.stopImageStream();
Future.delayed(const Duration(milliseconds: 100), () async {
_navigateToArtworkDetailsById(
artworkId: artworkId,
);
await _artworkRepository.scanArtwork(id: artworkId);
});
} catch (e) {
print(e);
displayErrorDialog('Oops',
'Looks like something went wrong! Please make sure you\'re using a valid QR code & try again.');
}
}
}
_isBusy = false;
if (mounted) {
setState(() {});
}
}
void displayErrorDialog(String title, String msg) {
showDialog<void>(
context: context,
builder: (BuildContext ctx) => AlertDialog(
title: Text(
title,
textAlign: TextAlign.center,
),
content: Text(msg),
actions: [
TextButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: const Text('Ok'),
),
],
),
);
}
#override
void dispose() {
cleanup();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
Future cleanup() async {
_canProcess = false;
_isBusy = false;
_barcodeScanner.close();
if (_controller != null && _controller!.value.isInitialized) {
try {
await _controller?.stopImageStream();
} catch (e) {
print(e);
}
}
await _controller?.dispose();
_controller = null;
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
log(state.name);
// final CameraController? cameraController = _controller;
// // App state changed before we got the chance to initialize.
// if (cameraController == null || !cameraController.value.isInitialized) {
// return;
// }
if (state == AppLifecycleState.inactive) {
log('inactive being called');
cleanup();
} else if (state == AppLifecycleState.resumed) {
log('on resume called');
startCameraSetup();
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: BlocConsumer<CameraCubit, CameraState>(
listener: (BuildContext context, CameraState state) {
state.maybeWhen(
initCamera: (List<CameraDescription> cameras) {},
orElse: () {},
);
},
builder: (BuildContext context, CameraState state) {
return state.maybeWhen(
initCamera: (_) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(
'Take a picture',
style: montserratRegular15.copyWith(color: AppColors.black),
),
),
body: Stack(
children: [
if (_controller?.value.isInitialized == true)
CameraPreview(_controller!)
else
const Center(
child: CircularProgressIndicator(
color: AppColors.red,
),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(
bottom: 130,
),
child: Text(
'Scan a QR code or take a photo of\nthe artwork',
textAlign: TextAlign.center,
style: montserratRegular15.copyWith(
color: AppColors.white,
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(bottom: 30),
child: GestureDetector(
onTap: () async {
// Ensure that the camera is initialized.
if (!_controller!.value.isInitialized) {
displayErrorDialog('Camera Error',
'Looks like the camera is busy or unavailable. Please try again later.');
return;
}
await _controller!.stopImageStream();
final XFile image =
await _controller!.takePicture();
final Uint8List bytes = await image.readAsBytes();
final String base64Image = base64Encode(bytes);
final int artworkId =
await _cubit.imageSearchByBase64(
base64Image: base64Image,
);
if (_cubit.state is ArtworkIdFound$) {
_navigateToArtworkDetailsById(
artworkId: artworkId,
);
_cubit.init();
}
},
child: Image.asset(
'assets/icons/shutter_icon.png',
height: 72,
width: 72,
),
),
),
),
Align(
child: Padding(
padding: const EdgeInsets.all(50),
child: Image.asset('assets/icons/b_logo_grey.png'),
),
),
],
),
);
},
loading: () => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Center(
child: CircularProgressIndicator(
color: AppColors.red,
),
),
SizedBox(
height: 10,
),
Text(
'Performing image recognition...',
style: montserratRegular15,
)
],
),
initial: () => const Center(
child: CircularProgressIndicator(
color: AppColors.red,
),
),
artworkIdError: () {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Failed to recognize artwork!',
style: montserratRegular15,
),
const SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 100),
child: AppButton(
onTap: () {
_cubit.init();
setState(() {});
},
text: 'Retry',
),
),
],
);
},
orElse: () {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Center(
child: CircularProgressIndicator(
color: AppColors.red,
),
),
SizedBox(
height: 10,
),
Text(
'Performing image recognition...',
style: montserratRegular15,
)
],
);
},
);
},
),
);
}
}
I have a flutter app that opens the camera preview with a FutureBuilder. When the app opens for the first time, the app asks for permission. (This is triggered automatically). When I press ok I get a red screen:
I need to wait for the response, and why is dispose called?
I checked this: https://pub.dev/packages/permission_handler
and tested this:
if (await Permission.contacts.request().isGranted) {
// Either the permission was already granted before or the user just granted it.
}
But I got false and was never asked to approve anything. In contrast to the description.
Any ideas?
Here is the code:
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:name/Model/FileIO/file_io.dart';
import 'package:name/Views/main_view.dart';
import 'package:name/main.dart';
class CameraView extends StatefulWidget {
const CameraView({Key? key}) : super(key: key);
#override
State<CameraView> createState() {
return _CameraViewState();
}
}
class _CameraViewState extends State<CameraView> with WidgetsBindingObserver {
CameraController? controller;
late Future<void> _initializeController;
List<CameraDescription> rearCameras = List.empty(growable: true);
int _currentCameraIndex = 0;
#override
void initState() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
DeviceOrientation.portraitUp
]);
_ambiguate(WidgetsBinding.instance)?.addObserver(this);
for (var description in cameras) {
if (description.lensDirection == CameraLensDirection.back) {
rearCameras.add(description);
}
}
onNewCameraSelected(rearCameras[_currentCameraIndex]);
super.initState();
}
#override
void dispose() {
_ambiguate(WidgetsBinding.instance)?.removeObserver(this);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
super.dispose();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
final CameraController? cameraController = controller;
if (cameraController == null || cameraController.value.isInitialized) {
return;
}
if (state == AppLifecycleState.inactive) {
cameraController.dispose();
}
if (state == AppLifecycleState.resumed) {
onNewCameraSelected(rearCameras[_currentCameraIndex]);
}
}
Future<void> onNewCameraSelected(CameraDescription description) async {
if (controller != null) {
await controller!.dispose();
}
final CameraController cameraController = CameraController(
description,
ResolutionPreset.max,
enableAudio: false,
imageFormatGroup: ImageFormatGroup.jpeg,
);
controller = cameraController;
cameraController.addListener(() {
if (mounted) {
setState(() {});
}
if (cameraController.value.hasError) {
showInSnackbar(
"Camera error: ${cameraController.value.errorDescription}");
}
});
try {
_initializeController = cameraController.initialize();
} on CameraException catch (e) {
print('Error initializing camera: $e');
}
if (mounted) {
setState(() {});
}
}
Future<void> _onCameraSwitch() async {
if (controller == null) {
return;
}
_currentCameraIndex = (_currentCameraIndex + 1) % rearCameras.length;
final CameraDescription cameraDescription =
rearCameras[_currentCameraIndex];
onNewCameraSelected(cameraDescription);
}
void showInSnackbar(String message) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(message)));
}
bool isRecording() {
return controller != null && controller!.value.isRecordingVideo;
}
Widget _cameraPreviewWidget(DeviceOrientation orientation) {
if (controller != null) {
final size = MediaQuery.of(context).size;
final deviceRatio = size.width / size.height;
controller!.lockCaptureOrientation(orientation);
return Center(
child: AspectRatio(
aspectRatio: deviceRatio,
child: CameraPreview(controller!),
),
);
}
return const Text("Camera not ready or available");
}
Widget _captureRowWidget() {
final CameraController? cameraController = controller;
return Container(
color: const Color.fromARGB(50, 255, 255, 255),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: const Icon(Icons.videocam),
color: Colors.blue,
onPressed: cameraController != null &&
cameraController.value.isInitialized &&
!cameraController.value.isRecordingVideo
? onVideoRecordButtonPressed
: null,
),
IconButton(
onPressed: isRecording() ? onStopRecordingButtonPressed : null,
icon: const Icon(Icons.stop),
),
TextButton(
onPressed: _onCameraSwitch, child: const Text("Switch Camera"))
],
),
);
}
void onVideoRecordButtonPressed() {
startVideoRecording().then((_) => {
if (mounted) {setState(() {})}
});
}
void onStopRecordingButtonPressed() {
stopVideoRecording().then((video) async {
if (video != null) {
final path = await localPath;
final file = File('$path/video/${timestamp() + ".mp4"}');
await file.create(recursive: true);
video.saveTo(file.path);
}
});
}
Future<void> startVideoRecording() async {
final CameraController? cameraController = controller;
if (cameraController == null || !cameraController.value.isInitialized) {
showInSnackbar("Error: Camera unavailable");
return;
}
if (cameraController.value.isRecordingVideo) {
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.isInitialized) {
return null;
}
try {
return cameraController.stopVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
}
OrientationBuilder getBody() {
return OrientationBuilder(
builder: (context, orientation) {
var deviceOrientation = DeviceOrientation.landscapeLeft;
if (orientation == Orientation.portrait) {
deviceOrientation = DeviceOrientation.portraitUp;
}
return Stack(
children: [
_cameraPreviewWidget(deviceOrientation),
Positioned(
bottom: 2,
left: 2,
right: 2,
child: _captureRowWidget(),
),
],
);
},
);
}
void _showCameraException(CameraException e) {
_logError(e.code, e.description);
showInSnackbar('Error: ${e.code}\n${e.description}');
}
void _navigateBack() {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) {
return const MainView();
},
),
(Route<dynamic> route) => false,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Video Recorder"),
leading: GestureDetector(
onTap: () {
_navigateBack();
},
child: const Icon(
Icons.arrow_back, // add custom icons also
),
),
),
body: SafeArea(
child: FutureBuilder<void>(
future: _initializeController,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return getBody();
} else {
return const Center(child: CircularProgressIndicator());
}
}),
),
);
}
T? _ambiguate<T>(T? value) => value;
void _logError(String code, String? message) {
if (message != null) {
print('Error: $code\nError Message: $message');
} else {
print('Error: $code');
}
}
String timestamp() {
return DateTime.now().microsecondsSinceEpoch.toString();
}
}
I do double check after grant permission, and if it still not granted it will show a dialog to open setting menu
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:permission_handler/permission_handler.dart';
enum CaptureMode { IDENTITY, SELFIE }
class CameraCapture extends StatefulWidget {
Function onCaptured;
CaptureMode captureMode;
CameraCapture({required this.onCaptured, required this.captureMode});
#override
State<CameraCapture> createState() => _CameraCaptureState();
}
class _CameraCaptureState extends State<CameraCapture> {
List<CameraDescription>? cameras;
CameraController? cameraController;
bool isPopupPermissionShow = false;
_openSettingDialog(BuildContext context) => AlertDialog(
title: const Text("Camera permission not granted"),
content: SingleChildScrollView(
child: ListBody(
children: [
GestureDetector(
child: const Text("Open Setting"),
onTap: () async {
Navigator.pop(context, null);
cameraController?.dispose();
await openAppSettings();
setState(() {});
},
),
const Padding(padding: EdgeInsets.all(10)),
GestureDetector(
child: const Text("Cancel"),
onTap: () async {
Navigator.pop(context, null);
return;
},
),
],
),
),
);
checkPermission(BuildContext context) async {
try {
bool isCameraGranted = await Permission.camera.request().isGranted;
if (!isCameraGranted) {
if (!isPopupPermissionShow) {
isPopupPermissionShow = true;
await showDialog(
context: context,
builder: (BuildContext context) {
return _openSettingDialog(context);
});
}
isPopupPermissionShow = false;
}
} catch (e) {
debugPrint("camera error: " + e.toString());
}
}
Future<bool> setupCamera(BuildContext context) async {
if (cameras == null) {
cameras = await availableCameras();
}
if (cameraController == null || !(cameraController!.value.isInitialized)) {
if (cameras != null && cameras!.length > 0) {
cameraController = CameraController(
(widget.captureMode == CaptureMode.IDENTITY)
? cameras!.first
: cameras![1],
ResolutionPreset.medium);
try {
await cameraController?.initialize();
} catch (e) {
bool isCameraGranted = await Permission.camera.request().isGranted;
if (!isCameraGranted) {
checkPermission(context);
} else {
debugPrint("camera error " + (e.toString()));
}
}
} else {
bool isCameraGranted = await Permission.camera.request().isGranted;
if (!isCameraGranted) {
checkPermission(context);
} else {
debugPrint("camera not available");
}
}
}
return false;
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: null,
child: Scaffold(
backgroundColor: Colors.black,
resizeToAvoidBottomInset: false,
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.close, color: Colors.black),
onPressed: () {
cameraController?.dispose();
widget.onCaptured("cancel");
}),
),
body: Stack(
children: [
FutureBuilder(
future: setupCamera(context),
builder: (_, snapshot) {
return (snapshot.connectionState == ConnectionState.done)
? CameraPreview(
cameraController!,
)
: Container(
// child: LoadingProgress(),
width: double.infinity,
height: double.infinity,
color: Colors.black,
);
}),
Container(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
//put camera mask here
children: [
Container(
padding: EdgeInsets.only(
top: 16, left: 32, right: 32, bottom: 32),
child: _shutterButton(() async {
final image = await cameraController?.takePicture();
String imgPath = (image?.path ?? "");
cameraController?.dispose();
}),
),
],
),
),
],
),
),
);
}
_shutterButton(Function onShot) => Stack(
alignment: Alignment.center,
children: [
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40), color: Colors.white),
),
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40), color: Colors.black),
),
InkWell(
onTap: () {
onShot();
},
borderRadius: BorderRadius.circular(40),
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40), color: Colors.white),
),
),
],
);
}
I found the error, it was an incorrect if-statement.
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
final CameraController? cameraController = controller;
if (cameraController == null || !cameraController.value.isInitialized) <-- the ! is missing in the OP {
return;
}
if (state == AppLifecycleState.inactive) {
cameraController.dispose();
}
if (state == AppLifecycleState.resumed) {
onNewCameraSelected(rearCameras[_currentCameraIndex]);
}
}
I am working on a app were I get data from the ESP32 and display it on a simple page. Here is my code for my sensorpage:
import 'dart:async';
import 'dart:convert' show utf8;
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
class sensorpage extends StatefulWidget {
const sensorpage({Key? key, required this.device}) : super(key: key);
final BluetoothDevice device;
#override
_sensorpageState createState() => _sensorpageState();
}
class _sensorpageState extends State<sensorpage> {
final String SERVICE_UUID = "edb91e04-3e19-11ec-9bbc-0242ac130002";
final String CHARACTERISTIC_UUID = "edb920c0-3e19-11ec-9bbc-0242ac130002";
late bool isReady;
//String val1 = "";
//int pot1 = 0;
FlutterBlue flutterBlue = FlutterBlue.instance;
//late StreamSubscription<ScanResult> scanSubScription;
//late BluetoothDevice targetDevice;
late Stream<List<int>> stream;
#override
void initState() {
super.initState();
isReady = false;
connectToDevice();
}
connectToDevice() async {
if (widget.device == null) {
_Pop();
return;
}
Timer(const Duration(seconds: 15), () {
if (!isReady) {
disconnectFromDevice();
_Pop();
}
});
await widget.device.connect();
discoverServices();
}
disconnectFromDevice() {
if (widget.device == null) {
_Pop();
return;
}
widget.device.disconnect();
}
discoverServices() async {
if (widget.device == null) {
_Pop();
return;
}
List<BluetoothService> services = await widget.device.discoverServices();
services.forEach((service) {
if (service.uuid.toString() == SERVICE_UUID) {
service.characteristics.forEach((characteristic) {
if (characteristic.uuid.toString() == CHARACTERISTIC_UUID) {
characteristic.setNotifyValue(!characteristic.isNotifying);
stream = characteristic.value;
setState(() {
isReady = true;
});
}
});
}
});
if (!isReady) {
_Pop();
}
}
Future<bool> _onWillPop() async {
bool shouldPop = false;
await showDialog(
context: context,
builder: (context) =>
AlertDialog(
title: const Text('Are you sure?'),
content: const Text('Do you want to disconnect device and go back?'),
actions: <Widget>[
ElevatedButton(
onPressed: () {
// shouldPop is already false
},
child: const Text('No')),
ElevatedButton(
onPressed: () async {
await disconnectFromDevice();
Navigator.of(context).pop();
shouldPop = true;
},
child: const Text('Yes')),
],
));
return shouldPop;
}
_Pop() {
Navigator.of(context).pop(true);
}
String _dataParser( List<int> dataFromDevice) {
return utf8.decode(dataFromDevice);
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
appBar: AppBar(
title: const Text('Test'),
),
body: Container(
child: !isReady
? const Center(
child: Text(
"Waiting...",
style: TextStyle(fontSize: 24, color: Colors.red),
),
)
: Container(
child: StreamBuilder<List<int>>(
stream: stream,
builder: (BuildContext context,
AsyncSnapshot<List<int>> snapshot) {
if (snapshot.hasError){
return Text('Error: ${snapshot.error}');
}
if (snapshot.connectionState == ConnectionState.active) {
var currentValue = _dataParser (snapshot.data!);
//val1 = currentValue.split(',')[0];
//pot1 = int.parse(val1);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Current value',
style: TextStyle(fontSize: 14)),
Text('${currentValue} jawool',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24))
])
);
} else {
return const Text('Check the stream');
}
},
)),
)
));
}
}´
My Problem is that my second container where I display my data is shown as unnecessary but I don´t know why.
I assume you mean this piece of code?
body: Container(
child: !isReady
? const Center(
child: Text(
"Waiting...",
style: TextStyle(fontSize: 24, color: Colors.red),
),
)
: Container(
child: StreamBuilder<List<int>>(
If isReady is true you return Container(child: Container(child: SteamBuilder));
You should change it to this and it should be fine:
body: Container(
child: !isReady
? const Center(
child: Text(
"Waiting...",
style: TextStyle(fontSize: 24, color: Colors.red),
),
)
: StreamBuilder<List<int>>(
The first Container is just wrapping the content based on the boolean and you don't need it. According to the flutter team:
Wrapping a widget in Container with no other parameters set has no effect and makes code needlessly more complex
So instead you can directly do:
body: !isReady ? buildSomething() : Container(....more code);
I am trying to build an app with the camera plugin (camera: ^0.8.1+3) in flutter, In this app in am trying to click a picture while the torch is on, but the problem is that it works properly in some of the Oneplus devices but in any other device the torch restarts when I try to click a picture.I am using flutter 2.
import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
class CameraExampleHome extends StatefulWidget {
#override
_CameraExampleHomeState createState() {
return _CameraExampleHomeState();
}
}
/// Returns a suitable camera icon for [direction].
IconData getCameraLensIcon(CameraLensDirection direction) {
switch (direction) {
case CameraLensDirection.back:
return Icons.camera_rear;
case CameraLensDirection.front:
return Icons.camera_front;
case CameraLensDirection.external:
return Icons.camera;
default:
throw ArgumentError('Unknown lens direction');
}
}
void logError(String code, String? message) {
if (message != null) {
print('Error: $code\nError Message: $message');
} else {
print('Error: $code');
}
}
class _CameraExampleHomeState extends State<CameraExampleHome>
with WidgetsBindingObserver, TickerProviderStateMixin {
CameraController? controller;
XFile? imageFile;
XFile? videoFile;
VideoPlayerController? videoController;
VoidCallback? videoPlayerListener;
bool enableAudio = true;
double _minAvailableExposureOffset = 0.0;
double _maxAvailableExposureOffset = 0.0;
double _currentExposureOffset = 0.0;
late AnimationController _flashModeControlRowAnimationController;
late Animation<double> _flashModeControlRowAnimation;
late AnimationController _exposureModeControlRowAnimationController;
late Animation<double> _exposureModeControlRowAnimation;
late AnimationController _focusModeControlRowAnimationController;
late Animation<double> _focusModeControlRowAnimation;
double _minAvailableZoom = 1.0;
double _maxAvailableZoom = 1.0;
double _currentScale = 1.0;
double _baseScale = 1.0;
// Counting pointers (number of user fingers on screen)
int _pointers = 0;
#override
void initState() {
super.initState();
WidgetsBinding.instance?.addObserver(this);
_flashModeControlRowAnimationController = AnimationController(
duration: const Duration(milliseconds: 10),
vsync: this,
);
_flashModeControlRowAnimation = CurvedAnimation(
parent: _flashModeControlRowAnimationController,
curve: Curves.easeInCubic,
);
_exposureModeControlRowAnimationController = AnimationController(
duration: const Duration(milliseconds: 10),
vsync: this,
);
_exposureModeControlRowAnimation = CurvedAnimation(
parent: _exposureModeControlRowAnimationController,
curve: Curves.easeInCubic,
);
_focusModeControlRowAnimationController = AnimationController(
duration: const Duration(milliseconds: 10),
vsync: this,
);
_focusModeControlRowAnimation = CurvedAnimation(
parent: _focusModeControlRowAnimationController,
curve: Curves.easeInCubic,
);
}
#override
void dispose() {
WidgetsBinding.instance?.removeObserver(this);
_flashModeControlRowAnimationController.dispose();
_exposureModeControlRowAnimationController.dispose();
super.dispose();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
final CameraController? cameraController = controller;
// App state changed before we got the chance to initialize.
if (cameraController == null || !cameraController.value.isInitialized) {
return;
}
if (state == AppLifecycleState.inactive) {
cameraController.dispose();
} else if (state == AppLifecycleState.resumed) {
onNewCameraSelected(cameraController.description);
}
}
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: const Text('Camera example'),
),
body: Column(
children: <Widget>[
Expanded(
child: Container(
child: Padding(
padding: const EdgeInsets.all(1.0),
child: Center(
child: _cameraPreviewWidget(),
),
),
decoration: BoxDecoration(
color: Colors.black,
border: Border.all(
color:
controller != null && controller!.value.isRecordingVideo
? Colors.redAccent
: Colors.grey,
width: 3.0,
),
),
),
),
_captureControlRowWidget(),
_modeControlRowWidget(),
Padding(
padding: const EdgeInsets.all(5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
_cameraTogglesRowWidget(),
_thumbnailWidget(),
],
),
),
],
),
);
}
/// Display the preview from the camera (or a message if the preview is not available).
Widget _cameraPreviewWidget() {
final CameraController? cameraController = controller;
if (cameraController == null || !cameraController.value.isInitialized) {
return const Text(
'Tap a camera',
style: TextStyle(
color: Colors.white,
fontSize: 24.0,
fontWeight: FontWeight.w900,
),
);
} else {
return Listener(
onPointerDown: (_) => _pointers++,
onPointerUp: (_) => _pointers--,
child: CameraPreview(
controller!,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onScaleStart: _handleScaleStart,
onScaleUpdate: _handleScaleUpdate,
onTapDown: (details) => onViewFinderTap(details, constraints),
);
}),
),
);
}
}
void _handleScaleStart(ScaleStartDetails details) {
_baseScale = _currentScale;
}
Future<void> _handleScaleUpdate(ScaleUpdateDetails details) async {
// When there are not exactly two fingers on screen don't scale
if (controller == null || _pointers != 2) {
return;
}
_currentScale = (_baseScale * details.scale)
.clamp(_minAvailableZoom, _maxAvailableZoom);
await controller!.setZoomLevel(_currentScale);
}
/// Display the thumbnail of the captured image or video.
Widget _thumbnailWidget() {
final VideoPlayerController? localVideoController = videoController;
return Expanded(
child: Align(
alignment: Alignment.centerRight,
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
localVideoController == null && imageFile == null
? Container()
: SizedBox(
child: (localVideoController == null)
? Container(child: Image.file(File(imageFile!.path)))
: Container(
child: Center(
child: AspectRatio(
aspectRatio:
localVideoController.value.size != null
? localVideoController
.value.aspectRatio
: 1.0,
child: VideoPlayer(localVideoController)),
),
decoration: BoxDecoration(
border: Border.all(color: Colors.pink)),
),
width: 64.0,
height: 64.0,
),
],
),
),
);
}
/// Display a bar with buttons to change the flash and exposure modes
Widget _modeControlRowWidget() {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: Icon(Icons.flash_on),
color: Colors.blue,
onPressed: controller != null ? onFlashModeButtonPressed : null,
),
IconButton(
icon: Icon(Icons.exposure),
color: Colors.blue,
onPressed:
controller != null ? onExposureModeButtonPressed : null,
),
IconButton(
icon: Icon(Icons.filter_center_focus),
color: Colors.blue,
onPressed: controller != null ? onFocusModeButtonPressed : null,
),
IconButton(
icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute),
color: Colors.blue,
onPressed: controller != null ? onAudioModeButtonPressed : null,
),
IconButton(
icon: Icon(controller?.value.isCaptureOrientationLocked ?? false
? Icons.screen_lock_rotation
: Icons.screen_rotation),
color: Colors.blue,
onPressed: controller != null
? onCaptureOrientationLockButtonPressed
: null,
),
],
),
_flashModeControlRowWidget(),
_exposureModeControlRowWidget(),
_focusModeControlRowWidget(),
],
);
}
Widget _flashModeControlRowWidget() {
return SizeTransition(
sizeFactor: _flashModeControlRowAnimation,
child: ClipRect(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: [
IconButton(
icon: Icon(Icons.flash_off),
color: controller?.value.flashMode == FlashMode.off
? Colors.orange
: Colors.blue,
onPressed: controller != null
? () => onSetFlashModeButtonPressed(FlashMode.off)
: null,
),
IconButton(
icon: Icon(Icons.flash_auto),
color: controller?.value.flashMode == FlashMode.auto
? Colors.orange
: Colors.blue,
onPressed: controller != null
? () => onSetFlashModeButtonPressed(FlashMode.auto)
: null,
),
IconButton(
icon: Icon(Icons.flash_on),
color: controller?.value.flashMode == FlashMode.always
? Colors.orange
: Colors.blue,
onPressed: controller != null
? () => onSetFlashModeButtonPressed(FlashMode.always)
: null,
),
IconButton(
icon: Icon(Icons.highlight),
color: controller?.value.flashMode == FlashMode.torch
? Colors.orange
: Colors.blue,
onPressed: controller != null
? () => onSetFlashModeButtonPressed(FlashMode.torch)
: null,
),
],
),
),
);
}
Widget _exposureModeControlRowWidget() {
final ButtonStyle styleAuto = TextButton.styleFrom(
primary: controller?.value.exposureMode == ExposureMode.auto
? Colors.orange
: Colors.blue,
);
final ButtonStyle styleLocked = TextButton.styleFrom(
primary: controller?.value.exposureMode == ExposureMode.locked
? Colors.orange
: Colors.blue,
);
return SizeTransition(
sizeFactor: _exposureModeControlRowAnimation,
child: ClipRect(
child: Container(
color: Colors.grey.shade50,
child: Column(
children: [
Center(
child: Text("Exposure Mode"),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: [
TextButton(
child: Text('AUTO'),
style: styleAuto,
onPressed: controller != null
? () =>
onSetExposureModeButtonPressed(ExposureMode.auto)
: null,
onLongPress: () {
if (controller != null) {
controller!.setExposurePoint(null);
showInSnackBar('Resetting exposure point');
}
},
),
TextButton(
child: Text('LOCKED'),
style: styleLocked,
onPressed: controller != null
? () =>
onSetExposureModeButtonPressed(ExposureMode.locked)
: null,
),
],
),
Center(
child: Text("Exposure Offset"),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: [
Text(_minAvailableExposureOffset.toString()),
Slider(
value: _currentExposureOffset,
min: _minAvailableExposureOffset,
max: _maxAvailableExposureOffset,
label: _currentExposureOffset.toString(),
onChanged: _minAvailableExposureOffset ==
_maxAvailableExposureOffset
? null
: setExposureOffset,
),
Text(_maxAvailableExposureOffset.toString()),
],
),
],
),
),
),
);
}
Widget _focusModeControlRowWidget() {
final ButtonStyle styleAuto = TextButton.styleFrom(
primary: controller?.value.focusMode == FocusMode.auto
? Colors.orange
: Colors.blue,
);
final ButtonStyle styleLocked = TextButton.styleFrom(
primary: controller?.value.focusMode == FocusMode.locked
? Colors.orange
: Colors.blue,
);
return SizeTransition(
sizeFactor: _focusModeControlRowAnimation,
child: ClipRect(
child: Container(
color: Colors.grey.shade50,
child: Column(
children: [
Center(
child: Text("Focus Mode"),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: [
TextButton(
child: Text('AUTO'),
style: styleAuto,
onPressed: controller != null
? () => onSetFocusModeButtonPressed(FocusMode.auto)
: null,
onLongPress: () {
if (controller != null) controller!.setFocusPoint(null);
showInSnackBar('Resetting focus point');
},
),
TextButton(
child: Text('LOCKED'),
style: styleLocked,
onPressed: controller != null
? () => onSetFocusModeButtonPressed(FocusMode.locked)
: null,
),
],
),
],
),
),
),
);
}
/// Display the control bar with buttons to take pictures and record videos.
Widget _captureControlRowWidget() {
final CameraController? cameraController = controller;
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: const Icon(Icons.camera_alt),
color: Colors.blue,
onPressed: cameraController != null &&
cameraController.value.isInitialized
// !cameraController.value.isRecordingVideo
? onTakePictureButtonPressed
: null,
),
IconButton(
icon: const Icon(Icons.videocam),
color: Colors.blue,
onPressed: cameraController != null &&
cameraController.value.isInitialized &&
!cameraController.value.isRecordingVideo
? onVideoRecordButtonPressed
: null,
),
IconButton(
icon: cameraController != null &&
cameraController.value.isRecordingPaused
? Icon(Icons.play_arrow)
: Icon(Icons.pause),
color: Colors.blue,
onPressed: cameraController != null &&
cameraController.value.isInitialized &&
cameraController.value.isRecordingVideo
? (cameraController.value.isRecordingPaused)
? onResumeButtonPressed
: onPauseButtonPressed
: null,
),
IconButton(
icon: const Icon(Icons.stop),
color: Colors.red,
onPressed: cameraController != null &&
cameraController.value.isInitialized &&
cameraController.value.isRecordingVideo
? onStopButtonPressed
: null,
)
],
);
}
/// Display a row of toggle to select the camera (or a message if no camera is available).
Widget _cameraTogglesRowWidget() {
final List<Widget> toggles = <Widget>[];
final onChanged = (CameraDescription? description) {
if (description == null) {
return;
}
onNewCameraSelected(description);
};
if (cameras.isEmpty) {
return const Text('No camera found');
} else {
for (CameraDescription cameraDescription in cameras) {
toggles.add(
SizedBox(
width: 90.0,
child: RadioListTile<CameraDescription>(
title: Icon(getCameraLensIcon(cameraDescription.lensDirection)),
groupValue: controller?.description,
value: cameraDescription,
onChanged:
controller != null && controller!.value.isRecordingVideo
? null
: onChanged,
),
),
);
}
}
return Row(children: toggles);
}
String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();
void showInSnackBar(String message) {
// ignore: deprecated_member_use
_scaffoldKey.currentState?.showSnackBar(SnackBar(content: Text(message)));
}
void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) {
if (controller == null) {
return;
}
final CameraController cameraController = controller!;
final offset = Offset(
details.localPosition.dx / constraints.maxWidth,
details.localPosition.dy / constraints.maxHeight,
);
cameraController.setExposurePoint(offset);
cameraController.setFocusPoint(offset);
}
void onNewCameraSelected(CameraDescription cameraDescription) async {
if (controller != null) {
await controller!.dispose();
}
final CameraController cameraController = CameraController(
cameraDescription,
ResolutionPreset.medium,
enableAudio: enableAudio,
imageFormatGroup: ImageFormatGroup.jpeg,
);
controller = cameraController;
// If the controller is updated then update the UI.
cameraController.addListener(() {
if (mounted) setState(() {});
if (cameraController.value.hasError) {
showInSnackBar(
'Camera error ${cameraController.value.errorDescription}');
}
});
try {
await cameraController.initialize();
await Future.wait([
cameraController
.getMinExposureOffset()
.then((value) => _minAvailableExposureOffset = value),
cameraController
.getMaxExposureOffset()
.then((value) => _maxAvailableExposureOffset = value),
cameraController
.getMaxZoomLevel()
.then((value) => _maxAvailableZoom = value),
cameraController
.getMinZoomLevel()
.then((value) => _minAvailableZoom = value),
]);
} on CameraException catch (e) {
_showCameraException(e);
}
if (mounted) {
setState(() {});
}
}
void onTakePictureButtonPressed() {
takePicture().then((XFile? file) {
if (mounted) {
setState(() {
imageFile = file;
videoController?.dispose();
videoController = null;
controller?.value.flashMode == FlashMode.torch;
});
if (file != null) showInSnackBar('Picture saved to ${file.path}');
}
});
}
void onFlashModeButtonPressed() {
if (_flashModeControlRowAnimationController.value == 1) {
_flashModeControlRowAnimationController.reverse();
} else {
_flashModeControlRowAnimationController.forward();
_exposureModeControlRowAnimationController.reverse();
_focusModeControlRowAnimationController.reverse();
}
}
void onExposureModeButtonPressed() {
if (_exposureModeControlRowAnimationController.value == 1) {
_exposureModeControlRowAnimationController.reverse();
} else {
_exposureModeControlRowAnimationController.forward();
_flashModeControlRowAnimationController.reverse();
_focusModeControlRowAnimationController.reverse();
}
}
void onFocusModeButtonPressed() {
if (_focusModeControlRowAnimationController.value == 1) {
_focusModeControlRowAnimationController.reverse();
} else {
_focusModeControlRowAnimationController.forward();
_flashModeControlRowAnimationController.reverse();
_exposureModeControlRowAnimationController.reverse();
}
}
void onAudioModeButtonPressed() {
enableAudio = !enableAudio;
if (controller != null) {
onNewCameraSelected(controller!.description);
}
}
void onCaptureOrientationLockButtonPressed() async {
if (controller != null) {
final CameraController cameraController = controller!;
if (cameraController.value.isCaptureOrientationLocked) {
await cameraController.unlockCaptureOrientation();
showInSnackBar('Capture orientation unlocked');
} else {
await cameraController.lockCaptureOrientation();
showInSnackBar(
'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}');
}
}
}
void onSetFlashModeButtonPressed(FlashMode mode) {
setFlashMode(mode).then((_) {
if (mounted) setState(() {});
showInSnackBar('Flash mode set to ${mode.toString().split('.').last}');
});
}
void onSetExposureModeButtonPressed(ExposureMode mode) {
setExposureMode(mode).then((_) {
if (mounted) setState(() {});
showInSnackBar('Exposure mode set to ${mode.toString().split('.').last}');
});
}
void onSetFocusModeButtonPressed(FocusMode mode) {
setFocusMode(mode).then((_) {
if (mounted) setState(() {});
showInSnackBar('Focus mode set to ${mode.toString().split('.').last}');
});
}
void onVideoRecordButtonPressed() {
startVideoRecording().then((_) {
if (mounted) setState(() {});
});
}
void onStopButtonPressed() {
stopVideoRecording().then((file) {
if (mounted) setState(() {});
if (file != null) {
showInSnackBar('Video recorded to ${file.path}');
videoFile = file;
_startVideoPlayer();
}
});
}
void onPauseButtonPressed() {
pauseVideoRecording().then((_) {
if (mounted) setState(() {});
showInSnackBar('Video recording paused');
});
}
void onResumeButtonPressed() {
resumeVideoRecording().then((_) {
if (mounted) setState(() {});
showInSnackBar('Video recording resumed');
});
}
Future<void> startVideoRecording() async {
final CameraController? cameraController = controller;
if (cameraController == null || !cameraController.value.isInitialized) {
showInSnackBar('Error: select a camera first.');
return;
}
if (cameraController.value.isRecordingVideo) {
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;
}
}
Future<void> pauseVideoRecording() async {
final CameraController? cameraController = controller;
if (cameraController == null || !cameraController.value.isRecordingVideo) {
return null;
}
try {
await cameraController.pauseVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
rethrow;
}
}
Future<void> resumeVideoRecording() async {
final CameraController? cameraController = controller;
if (cameraController == null || !cameraController.value.isRecordingVideo) {
return null;
}
try {
await cameraController.resumeVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
rethrow;
}
}
Future<void> setFlashMode(FlashMode mode) async {
if (controller == null) {
return;
}
try {
await controller!.setFlashMode(mode);
} on CameraException catch (e) {
_showCameraException(e);
rethrow;
}
}
Future<void> setExposureMode(ExposureMode mode) async {
if (controller == null) {
return;
}
try {
await controller!.setExposureMode(mode);
} on CameraException catch (e) {
_showCameraException(e);
rethrow;
}
}
Future<void> setExposureOffset(double offset) async {
if (controller == null) {
return;
}
setState(() {
_currentExposureOffset = offset;
});
try {
offset = await controller!.setExposureOffset(offset);
} on CameraException catch (e) {
_showCameraException(e);
rethrow;
}
}
Future<void> setFocusMode(FocusMode mode) async {
if (controller == null) {
return;
}
try {
await controller!.setFocusMode(mode);
} on CameraException catch (e) {
_showCameraException(e);
rethrow;
}
}
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(() {
imageFile = null;
videoController = vController;
});
}
await vController.play();
}
Future<XFile?> takePicture() async {
final CameraController? cameraController = controller;
if (cameraController == null || !cameraController.value.isInitialized) {
showInSnackBar('Error: select a camera first.');
return null;
}
if (cameraController.value.isTakingPicture) {
return null;
}
try {
XFile file = await cameraController.takePicture();
return file;
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
}
void _showCameraException(CameraException e) {
logError(e.code, e.description);
showInSnackBar('Error: ${e.code}\n${e.description}');
}
}
class CameraApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: CameraExampleHome(),
);
}
}
List<CameraDescription> cameras = [];
Future<void> main() async {
try {
WidgetsFlutterBinding.ensureInitialized();
cameras = await availableCameras();
} on CameraException catch (e) {
logError(e.code, e.description);
}
runApp(CameraApp());
}
I am a new developer and have just started using flutter. I was trying to make a camera app and I followed this tutorial:
https://www.youtube.com/watch?v=_nS00ZKnINQ
I am getting an error: Instance member 'takePicture' can't be accessed using static access.
You can view the entire file in this code block. Please don't hesitate to ask if you need the whole project directory.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
class CameraScreen extends StatefulWidget {
#override
_CameraScreenState createState() => _CameraScreenState();
}
class _CameraScreenState extends State<CameraScreen> {
CameraController cameraController;
List cameras;
int selectedCameraIndex;
String imgPath;
Future _initCameraController(CameraDescription cameraDescription) async {
if (cameraController != null) {
await cameraController.dispose();
}
cameraController =
CameraController(cameraDescription, ResolutionPreset.high);
cameraController.addListener(() {
if (mounted) {
setState(() {});
}
});
if (cameraController.value.hasError) {
print('!!!Camera error!!! ${cameraController.value.errorDescription}');
}
try {
await cameraController.initialize();
} on CameraException catch (e) {
_showCameraException(e);
}
if (mounted) {
setState(() {});
}
}
/// Display Camera Preview
Widget _cameraPreviewWidget() {
if (cameraController == null || cameraController.value.isInitialized) {
return const Text(
'Loading...',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.w900,
),
);
}
return AspectRatio(
aspectRatio: cameraController.value.aspectRatio,
child: CameraPreview(cameraController),
);
}
/// Display control bar with buttons to take picture
Widget _cameraControlWidget(context) {
return Expanded(
child: Align(
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
FloatingActionButton(
child: Icon(
Icons.camera,
color: Colors.black,
),
backgroundColor: Colors.white,
onPressed: () {
_onCapturePressed(context);
})
],
),
));
}
/// Display a row of toggles to select the camera
Widget _cameraToggleRowWidget() {
if (cameras == null || cameras.isEmpty) {
return Spacer();
}
CameraDescription selectedCamera = cameras[selectedCameraIndex];
CameraLensDirection lensDirection = selectedCamera.lensDirection;
return Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: FlatButton.icon(
onPressed: _onSwitchCamera,
icon: Icon(
_getCameraLensIcon(lensDirection),
color: Colors.white,
size: 24,
),
label: Text(
'${lensDirection.toString().substring(lensDirection.toString().indexOf('.') + 1).toUpperCase()}',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
),
)),
));
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return null;
}
void _onSwitchCamera() {
selectedCameraIndex =
selectedCameraIndex < cameras.length - 1 ? selectedCameraIndex + 1 : 0;
CameraDescription selectedCamera = cameras[selectedCameraIndex];
_initCameraController(selectedCamera);
}
IconData _getCameraLensIcon(CameraLensDirection lensDirection) {
switch (lensDirection) {
case CameraLensDirection.back:
return CupertinoIcons.switch_camera;
case CameraLensDirection.front:
return CupertinoIcons.switch_camera_solid;
case CameraLensDirection.external:
return CupertinoIcons.photo_camera;
default:
return Icons.device_unknown;
}
}
}
void _onCapturePressed(context) async {
try {
final path =
join((await getTemporaryDirectory()).path, '${DateTime.now()}.png)');
await CameraController.takePicture(path);
} catch (e) {
_showCameraException(e);
}
}
void _showCameraException(CameraException e) {
String errorText = 'Error: ${e.code} \n Error Message: ${e.description}';
print(errorText);
}
I am using VS Code with Flutter on Windows 10
Do not call takePicture as a static method. You have to use your initialised controller:
cameraController.takePicture(path);
To access it, move your method _onCapturePressed into your class body. The working code from your tutorial can be found here.