CamerAwesome icon indicating copy to clipboard operation
CamerAwesome copied to clipboard

How to take photo in built-in ui

Open zhaoxiaohai opened this issue 1 year ago • 9 comments

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

zhaoxiaohai avatar Jan 20 '24 08:01 zhaoxiaohai

Hi, could you detail more about what you are trying to achieve?

g-apparence avatar Jan 23 '24 22:01 g-apparence

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.

zhaoxiaohai avatar Jan 25 '24 09:01 zhaoxiaohai

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.

difafadhilj avatar Feb 08 '24 13:02 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 avatar Feb 09 '24 10:02 g-apparence

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 avatar Feb 12 '24 22:02 ps9310

@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.

g-apparence avatar Feb 18 '24 11:02 g-apparence

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 avatar Feb 22 '24 14:02 g-apparence

@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'); } },

Honelign avatar May 07 '24 23:05 Honelign

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

GTechMicah avatar Aug 14 '24 12:08 GTechMicah