CamerAwesome
CamerAwesome copied to clipboard
How to take photo in built-in ui
import 'dart:io';
import 'package:camerawesome/camerawesome_plugin.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:cross_file/cross_file.dart';
import 'package:better_open_file/better_open_file.dart';
import '../utils/file_utils.dart';
class CameraAwesomeApp extends StatelessWidget {
const CameraAwesomeApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Custom CamerAwesome UI',
home: CameraPage(),
);
}
}
class CameraPage extends StatelessWidget {
const CameraPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: CameraAwesomeBuilder.awesome(
saveConfig: SaveConfig.photo(pathBuilder: (sensors) async {
final Directory extDir = await getTemporaryDirectory();
final testDir =
await Directory('${extDir.path}/test').create(recursive: true);
const String fileExtension ='jpg' ;
final String filePath =
'${testDir.path}/${DateTime.now().millisecondsSinceEpoch}.$fileExtension';
return SingleCaptureRequest(filePath, sensors.first);
}),
sensorConfig: SensorConfig.single(
sensor: Sensor.position(SensorPosition.back),
aspectRatio: CameraAspectRatios.ratio_1_1,
),
previewFit: CameraPreviewFit.contain,
previewPadding: const EdgeInsets.only(left: 150, top: 100),
previewAlignment: Alignment.topRight,
// Buttons of CamerAwesome UI will use this theme
theme: AwesomeTheme(
bottomActionsBackgroundColor: Colors.cyan.withOpacity(0.5),
buttonTheme: AwesomeButtonTheme(
backgroundColor: Colors.cyan.withOpacity(0.5),
iconSize: 20,
foregroundColor: Colors.white,
padding: const EdgeInsets.all(16),
// Tap visual feedback (ripple, bounce...)
buttonBuilder: (child, onTap) {
return ClipOval(
child: Material(
color: Colors.transparent,
shape: const CircleBorder(),
child: InkWell(
splashColor: Colors.cyan,
highlightColor: Colors.cyan.withOpacity(0.5),
onTap: onTap,
child: child,
),
),
);
},
),
),
topActionsBuilder: (state) => AwesomeTopActions(
padding: EdgeInsets.zero,
state: state,
children: [
Expanded(
child: AwesomeLocationButton(
state: state as PhotoCameraState,
),
),
],
),
middleContentBuilder: (state) {
return Column(
children: [
const Spacer(),
Builder(builder: (context) {
return Container(
color: AwesomeThemeProvider.of(context)
.theme
.bottomActionsBackgroundColor,
child: const Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: EdgeInsets.only(bottom: 10, top: 10),
child: Text(
"Take your best shot!",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic,
),
),
),
),
);
}),
],
);
},
bottomActionsBuilder: (state) => AwesomeBottomActions(
state: state,
left: AwesomeFlashButton(
state: state,
),
right: AwesomeCameraSwitchButton(
state: state,
scale: 1.0,
onSwitchTap: (state) {
state.switchCameraSensor(
aspectRatio: state.sensorConfig.aspectRatio,
);
},
),
),
),
);
}
}
I don't know how to take photo path when capture button taped
Hi, could you detail more about what you are trying to achieve?
Hi, could you detail more about what you are trying to achieve?
I want to obtain the picture file after taking a photo, so that I can submit it to the server. However, in the current demo, I did not see any method to get the picture. My current usage is shown in the code above.
lib/ui/views/camera/camera_view.dart:22:32: Error: The getter 'filePath' isn't defined for the class 'MediaCapture'.
- 'MediaCapture' is from 'package:camerawesome/src/orchestrator/models/media_capture.dart' ('../../../../.pub-cache/hosted/pub.dev/camerawesome-2.0.0+1/lib/src/orchestrator/models/media_capture.dart').
Same here, the filePath getter isn't defined when I want to obtain the file path.
Here is an example on how to do this
The build method
CameraAwesomeBuilder.custom(
builder: (state, preview) {
return AwesomeCameraLayout(
state: state,
topActions: const SizedBox.shrink(),
bottomActions: CameraBottomActions(
state: state,
onVideo: (video) => onVideo(ref, video),
onVideoFailed: (videoError) {
// TODO show error
},
onPhoto: (photo) => onPhoto(state, ref, photo),
),
middleContent: const SizedBox.shrink(),
);
},
saveConfig: SaveConfig.photoAndVideo(
videoPathBuilder: _videoPathBuilder,
photoPathBuilder: _imagePathBuilder,
// ignore: avoid_redundant_argument_values
initialCaptureMode: CaptureMode.video,
videoOptions: VideoOptions(
enableAudio: true,
ios: CupertinoVideoOptions(
fps: 30,
),
android: AndroidVideoOptions(
bitrate: 6000000,
fallbackStrategy: QualityFallbackStrategy.lower,
),
),
),
....
),
The on video (This is where you can do this)
void onVideo(WidgetRef ref, CaptureRequest request) {
final path = request.when(
single: (single) => single.file!.path,
multiple: (multiple) => multiple.fileBySensor.values.first!.path,
);
// Do what you need here <<<<<<<<
}
this should help you doing what you want @zhaoxiaohai @difafadhilj
Here is an example on how to do this
The build method
CameraAwesomeBuilder.custom( builder: (state, preview) { return AwesomeCameraLayout( state: state, topActions: const SizedBox.shrink(), bottomActions: CameraBottomActions( state: state, onVideo: (video) => onVideo(ref, video), onVideoFailed: (videoError) { // TODO show error }, onPhoto: (photo) => onPhoto(state, ref, photo), ), middleContent: const SizedBox.shrink(), ); }, saveConfig: SaveConfig.photoAndVideo( videoPathBuilder: _videoPathBuilder, photoPathBuilder: _imagePathBuilder, // ignore: avoid_redundant_argument_values initialCaptureMode: CaptureMode.video, videoOptions: VideoOptions( enableAudio: true, ios: CupertinoVideoOptions( fps: 30, ), android: AndroidVideoOptions( bitrate: 6000000, fallbackStrategy: QualityFallbackStrategy.lower, ), ), ), .... ),
The on video (This is where you can do this)
void onVideo(WidgetRef ref, CaptureRequest request) { final path = request.when( single: (single) => single.file!.path, multiple: (multiple) => multiple.fileBySensor.values.first!.path, ); // Do what you need here <<<<<<<< }
this should help you doing what you want @zhaoxiaohai @difafadhilj
@g-apparence I can't find the CameraBottomActions
widget in the master branch. However, I did notice AwesomeBottomActions
and AwesomeCaptureButton
, although neither includes the onPhoto
or onVideo
callbacks.
@ps9310 Sorry I wrote this too fast. This is a current wip in the main branch to make this more intuitive. I'm not completely satisfied with it yet.
Either you can do this with the old way
state.captureState$.listen(
(event) {
if (event?.status == MediaCaptureStatus.success) {
event?.captureRequest.when(single: (single) {
yourImageFunctionToProcessTheFile(single.file!.path);
});
}
},
);
Where the state can be accessed within builder command in custom factory or any topActionsBuilder, bottomActionsBuilder... in awesome factory.
This is not very intuitive so I'm looking to improve it.
It's not published on pub.dev yet but here is the new callback event
onMediaCaptureEvent: (event) {
switch ((event.status, event.isPicture, event.isVideo)) {
case (MediaCaptureStatus.capturing, true, false):
debugPrint('Capturing picture...');
case (MediaCaptureStatus.success, true, false):
event.captureRequest.when(
single: (single) {
debugPrint('Picture saved: ${single.file?.path}');
},
multiple: (multiple) {
multiple.fileBySensor.forEach((key, value) {
debugPrint('multiple image taken: $key ${value?.path}');
});
},
);
case (MediaCaptureStatus.failure, true, false):
debugPrint('Failed to capture picture: ${event.exception}');
case (MediaCaptureStatus.capturing, false, true):
debugPrint('Capturing video...');
case (MediaCaptureStatus.success, false, true):
event.captureRequest.when(
single: (single) {
debugPrint('Video saved: ${single.file?.path}');
},
multiple: (multiple) {
multiple.fileBySensor.forEach((key, value) {
debugPrint('multiple video taken: $key ${value?.path}');
});
},
);
case (MediaCaptureStatus.failure, false, true):
debugPrint('Failed to capture video: ${event.exception}');
default:
debugPrint('Unknown event: $event');
}
},
Should invest in rewriting the MediaCapture with Dart3 sealed class later
@g-apparence onmedia capture event is not defined
onMediaCaptureEvent:(event) { switch ((event.status, event.isPicture, event.isVideo)) { case (MediaCaptureStatus.capturing, true, false): debugPrint('Capturing picture...'); case (MediaCaptureStatus.success, true, false): event.captureRequest.when( single: (single) { debugPrint('Picture saved: ${single.file?.path}'); }, multiple: (multiple) { multiple.fileBySensor.forEach((key, value) { debugPrint('multiple image taken: $key ${value?.path}'); }); }, ); case (MediaCaptureStatus.failure, true, false): debugPrint('Failed to capture picture: ${event.exception}'); case (MediaCaptureStatus.capturing, false, true): debugPrint('Capturing video...'); case (MediaCaptureStatus.success, false, true): event.captureRequest.when( single: (single) { debugPrint('Video saved: ${single.file?.path}'); }, multiple: (multiple) { multiple.fileBySensor.forEach((key, value) { debugPrint('multiple video taken: $key ${value?.path}'); }); }, ); case (MediaCaptureStatus.failure, false, true): debugPrint('Failed to capture video: ${event.exception}'); default: debugPrint('Unknown event: $event'); } },
It's not published on pub.dev yet but here is the new callback event
Awesome! This is the correct answer for customizing saving of images or videos, just have to remember to check event.status == MediaCaptureStatus.success