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,
)
],
);
},
);
},
),
);
}
}
Related
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 have been searching for a long time about how to enable users to select a group of images and upload them to the database through the image_picker.I was able to find my request in the following topic:
enter link description here
And other similar topics on the site.
I tried to upload images to the database through the http package and php file. The code works and the attempt is successful, but the problem is that only one image is uploaded. How to solve this problem I need to upload all the images at once.
Code:
void main() {
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: '',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: PickImages(),
);
}
}
class PickImages extends StatefulWidget {
#override
_PickImagesState createState() => _PickImagesState();
}
class _PickImagesState extends State<PickImages> {
static final String uploadEndPoint = 'https://****************.php';
List<Object> images = List<Object>();
Future<File> _imageFile;
bool _isVisible = false;
String base64Image;
Future _onAddImageClick(int index, int type) async {
if (images != null)
setState(() {
// ignore: deprecated_member_use
_imageFile = ImagePicker.pickImage(
source: type == 1 ? ImageSource.camera : ImageSource.gallery,
imageQuality: 50,
);
getFileImage(index);
});
}
void getFileImage(int index) async {
_imageFile.then((file) async {
setState(() {
ImageUploadModel imageUpload = new ImageUploadModel();
imageUpload.imageFile = file;
images.replaceRange(index, index + 1, [imageUpload]);
});
});
}
void showImageBox() {
setState(() {
_isVisible = !_isVisible;
});
}
#override
void initState() {
super.initState();
setState(() {
images.add("Add Image");
images.add("Add Image");
images.add("Add Image");
images.add("Add Image");
images.add("Add Image");
images.add("Add Image");
});
}
startUpload() {
String NameImage = DateTime.now().millisecondsSinceEpoch.toString();
{
http.post(uploadEndPoint, body: {
"image": base64Image,
"NameImage": NameImage,
}).then((result) {
if (result.statusCode == 200) {
print('dddddddddddddddd ${result.statusCode}');
} else {
}
}).catchError((error) {
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Post Images'),
),
body: Column(
children: <Widget>[
Container(
//padding: EdgeInsets.only(right: 5),
child: Card(
elevation: 5,
child: ListTile(
trailing: Icon(Icons.attachment),
title: Text('Attachments'),
onTap: () {
showImageBox();
},
),
),
),
Visibility(
visible: _isVisible,
child: Padding(
padding: const EdgeInsets.only(top: 5.0, right: 5.0),
child: GridView.count(
shrinkWrap: true,
crossAxisCount: 3,
childAspectRatio: 1,
children: List.generate(images.length, (index) {
if (images[index] is ImageUploadModel) {
ImageUploadModel uploadModel = images[index];
//base64 image
List<int> imageBytes = uploadModel.imageFile.readAsBytesSync();
// base64Image = base64Encode(snapshot.data.readAsBytesSync());
base64Image = base64Encode(imageBytes); //'base64Image' holds the base64 image string
return Card(
clipBehavior: Clip.antiAlias,
child: Stack(
children: <Widget>[
Image.file(
uploadModel.imageFile,
fit: BoxFit.cover,
width: 300,
height: 300,
),
Positioned(
right: 5,
top: 5,
child: InkWell(
child: Icon(
Icons.remove_circle,
size: 20,
color: Colors.red,
),
onTap: () {
setState(() {
images.replaceRange(
index, index + 1, ['Add Image']);
});
},
),
),
],
),
);
} else {
return Card(
child: IconButton(
icon: Icon(Icons.camera_alt),
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return SafeArea(
child: Container(
child: new Wrap(
children: <Widget>[
new ListTile(
leading: new Icon(Icons.photo_camera),
title: new Text('Camera'),
onTap: () {
_onAddImageClick(index, 1);
Navigator.of(context).pop();
},
),
new ListTile(
leading:
new Icon(Icons.photo_library),
title: new Text('Gallery'),
onTap: () {
_onAddImageClick(index, 2);
Navigator.of(context).pop();
}),
],
),
),
);
},
);
},
),
);
}
}),
),
),
),
RaisedButton(
child: Text('send'),
onPressed: () {
startUpload();
},
),
],
),
);
}
}
class ImageUploadModel {
File imageFile;
ImageUploadModel({
this.imageFile,
});
}
<?php
$image = $_POST['image'];
$NameImage = $_POST['NameImage'];
include 'connt.php';
$path = "ImageCat/$NameImage.png";
$actualpath = "https://*****.com/*****/$path";
$sql = "INSERT INTO Cate (image) VALUES (?)";
$stmt = $con->prepare($sql);
$stmt->bind_param("s",$actualpath);
$stmt->execute();
$result = $stmt->get_result();
// if(mysqli_query($conn,$sql)){
file_put_contents($path,base64_decode($image));
echo "Successfully Uploaded";
// }
$stmt->close();
?>
You can use HTTP and MultipartFile
try {
var request = http.MultipartRequest(
"POST", Uri.parse("url"));
var photo1 = await http.MultipartFile.fromPath("photo1", _imageFile1.path);
var photo2 = await http.MultipartFile.fromPath("photo2", _imageFile2.path);
request.files.add(photo1);
request.files.add(photo2);
request.fields["additional_data"] = "data";
var response = await request.send();
response.stream.asBroadcastStream();
if (response.statusCode == 200) {
response.stream.transform(utf8.decoder).listen((value) async {
Map<String, dynamic> result = json.decode(value);
print(result);
});
} else {
print("error");
}
} catch (e) {
print(e);
}
I am trying to add an option to take images and pictures and save them to local device. I have managed to add the Camera and video. whenever I click on the take picture or video button I end up getting the error
I/flutter ( 4827): NoSuchMethodError: The getter 'name' was called on
null. Receiver: null.Tried
calling: name . NoSuchMethodError: The getter 'name'
was called on null. Receiver: null Tried calling: name
Am using the flutter camera library. What am I doing wrong? Thank you in advance
my camera code
List<CameraDescription> cameras = [];
void logError(String code, String message) =>
print('Error: $code\nError Message: $message');
Future<void> main() async {
// Fetch the available cameras before initializing the app.
try {
WidgetsFlutterBinding.ensureInitialized();
cameras = await availableCameras();
} on CameraException catch (e) {
logError(e.code, e.description);
}
final camera = cameras.first;
runApp(TakePicturePage(camera: camera));
}
class TakePicturePage extends StatefulWidget {
final CameraDescription camera;
TakePicturePage({#required this.camera});
#override
_TakePicturePageState createState() => _TakePicturePageState();
}
class _TakePicturePageState extends State<TakePicturePage> {
final fileName = DateTime.now().millisecondsSinceEpoch.toString();
var vidPath;
CameraController _cameraController;
Future<void> _initializeCameraControllerFuture;
int _selectedIndex = 0;
bool _start = false;
bool _isRec = false;
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
if (_selectedIndex == 1) {
_start = !_start;
}
});
}
#override
void initState() {
super.initState();
_initApp();
_cameraController =
CameraController(widget.camera, ResolutionPreset.medium);
_initializeCameraControllerFuture = _cameraController.initialize();
_fileInit();
}
_initApp() async {
final cameras = await availableCameras();
final camera = cameras.first;
_cameraController = CameraController(
camera, ResolutionPreset.max,
);
}
void _fileInit() async {
vidPath = join((await getTemporaryDirectory()).path, '${fileName}.mp4');
}
void _takePicture(BuildContext context) async {
try {
await _initializeCameraControllerFuture;
if (_selectedIndex == 0) {
final imgPath =
join((await getTemporaryDirectory()).path, '${fileName}.png');
await _cameraController.takePicture(imgPath);
Navigator.pop(context, imgPath);
} else {
if (_start) {
await _cameraController.startVideoRecording(vidPath);
setState(() {
_start = !_start;
_isRec = !_isRec;
});
} else {
_cameraController.stopVideoRecording();
setState(() {
_isRec = !_isRec;
});
Navigator.pop(context, vidPath);
}
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
FutureBuilder(
future: _initializeCameraControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return CameraPreview(_cameraController);
} else {
return Center(child: CircularProgressIndicator(backgroundColor: Colors.green,));
}
},
),
SafeArea(
child: Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: FloatingActionButton(
backgroundColor: Colors.green,
child: _selectedIndex == 1 ? _isRec == true?Icon(Icons.pause, color: Colors.white):Icon(Icons.play_arrow, color: Colors.white) : Icon(Icons.camera, color: Colors.white),
onPressed: () {
_takePicture(context);
},
),
),
),
),
_isRec == true
? SafeArea(
child: Container(
height: 30,
// alignment: Alignment.topLeft,
decoration: BoxDecoration(
color: Color(0xFFEE4400),
borderRadius: BorderRadius.circular(10),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"REC",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
color: Color(0xFFFAFAFA)),
),
),
),
)
: SizedBox(
height: 0,
)
],
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.camera),
title: Text('Picture'),
),
BottomNavigationBarItem(
icon: Icon(Icons.videocam),
title: Text('Video'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.green,
onTap: _onItemTapped,
),
);
}
#override
void dispose() {
_cameraController.dispose();
super.dispose();
}
}
the camera is not being opened
I am working in paint project were, I need to display the list of videos, which works fine. But when the video is playing it is showing something codec does not support config priority (err -2147483648) in console,
Here I am using ImagePicker to record video from the camera and passing to VideoPlayerController.
class VideoScaling extends StatefulWidget {
#override
_VideoScalingState createState() => new _VideoScalingState();
}
class _VideoScalingState extends State<VideoScaling> {
VideoPlayerController videoController;
bool _isPlaying = false;
var _videoPath;
String _videoP;
VoidCallback videoPlayerListener;
List<VideoPlayerController> _listOfVideos = [];
void _video() async {
_videoPath =
await ImagePicker.pickVideo(source: ImageSource.camera).then((p) {
_videoP = p.path;
});
_startVideoPlayer();
}
VideoPlayerController vcontroller;
Future<void> _startVideoPlayer() async {
// print('path video: ${_videoP}');
vcontroller = new VideoPlayerController.file(new File(_videoP));
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;
_listOfVideos.add(videoController);
});
}
await vcontroller.play();
}
void _pause() async {
await vcontroller.play();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: new Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
videoController != null
? Column(
children: _listOfVideos
.map((p) => InkWell(
onTap: () {},
child: Padding(
padding: EdgeInsets.all(10.0),
child: Container(
height: 200.0,
width: 200.0,
child: VideoPlayer(p)),
),
))
.toList(growable: false),
)
: Container(
color: Colors.red,
),
_recordVideo(context),
],
),
),
);
}
Widget _recordVideo(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
onPressed: () {
_startVideoPlayer();
},
icon: Icon(Icons.play_arrow),
),
IconButton(
onPressed: () {
setState(() {
videoController = null;
});
_video();
},
icon: Icon(Icons.add_circle),
),
IconButton(
onPressed: () {
_pause();
},
icon: Icon(Icons.stop),
),
],
);
}
}
Add '/' correctly in video paths
Then :
Tools->Flutter->Flutter Clean