Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Discussion] Should there be a global controller / global key? #319

Open
apalala-dev opened this issue Apr 17, 2023 · 7 comments
Open

[Discussion] Should there be a global controller / global key? #319

apalala-dev opened this issue Apr 17, 2023 · 7 comments

Comments

@apalala-dev
Copy link
Contributor

apalala-dev commented Apr 17, 2023

Proposal

This proposal follows #313.

CameraAwesomeBuilder doesn't take any controller or global key as parameter.
If an user wants to control the camera API outside of the builders functions, they need to first register the state in the builder callback in order to be able to use it later.

A traditional way to work with such workflows is to provide a controller or a global key in order to access properties & methods of a given widget.

This issue is here to discuss what properties and methods should be accessibles, and once it's more clear on that area, we might provide a better way to access them.

Pros:

  • Manual control of the camera API would be provided in a proper way and would not rely on builders to register the state.
    • builders would then be only responsible of displaying the UI (which seems right)
    • if one is using the CamerAwesomeBuilder.awesome constructor, they may want to use the UI as is but the need of registering the state would force them to implement a builder.

Cons:

  • It might add more complexity to the plugin.
  • The camera API might be used in a wrong way if let up to the user. It might lead to crashes or unpredictable behaviour.

Like (👍) this issue if you think it's worth implementing or dislike (👎) if you don't think it's worth it.

@pinpong
Copy link

pinpong commented Apr 19, 2023

Plugin looking good but without a controller it's not the right plugin for our usecase.

@apalala-dev
Copy link
Contributor Author

Plugin looking good but without a controller it's not the right plugin for our usecase.

Could you share what exactly you expect from a controller? If you could include a list of features, that would be a great insight 💪

Also, remember that the controller is probably just an other way of getting the CameraState that one can already access through CameraAwesomeBuilder builders.

E.g.:

CameraAwesomeBuilder.awesome(
    previewDecoratorBuilder: (state, previewSize, previewRect) {
          // You could save the current state in a variable, but don't forget to refresh it every time this method is called
          _currentState = state;
         
          // Return a widget, just return an empty SizedBox if you don't want to show anything
          return SizedBox();
        },
)

Then in an other method, you could have something like this:

void myOtherMethod() {
    _currentState.when(
                onPhotoMode: (photoState) => photoState.takePhoto(),
                onVideoMode: (videoState) => videoState.startRecording(),
                onVideoRecordingMode: (videoState) =>
                    videoState.stopRecording(),
     );
}

@pinpong
Copy link

pinpong commented Apr 20, 2023

For example with camera plugin you are able to stream images from camera without any widgets.

here is a basic example how we want to use the controller.
With the camerasValuesStream you are able to build your ui with the state management of your choice.

  late final _cameraValuesSubject = behaviorSubject<List<CameraValue>>();

  Stream<List<CameraValue>> get camerasValuesStream =>
      _cameraValuesSubject.stream;

    late List<CameraDescription> _cameras;

    List<CameraController> cameraControllers = [];

  void initCameras() async {
    _cameras = await availableCameras();

    for (final camera in _cameras) {
      final CameraController controller = CameraController(
        cameraDescription,
        ResolutionPreset.medium,
        enableAudio: true,
        imageFormatGroup: ImageFormatGroup.jpeg,
      );

      cameraControllers.add(controller);

      controller.addListener(() {
        updateCameraValues();
      });

      await controller.initialize();
      controller.startImageStream(...);
    }
  }

  void updateCameraValues() {
    List<CameraValue> values = [];
    for (final controller in cameraControllers) {
      values.add(controller.value);
    }
    _cameraValuesSubject.sink.add(values);
  }

@apalala-dev
Copy link
Contributor Author

An alternative for this use case would be to use CameraAwesomeBuilder.analysisOnly() and either return your UI or an empty SizedBox from the builder method.

Here is an example:

CameraAwesomeBuilder.analysisOnly(
        aspectRatio: CameraAspectRatios.ratio_1_1,
        sensor: Sensors.front,
        onImageForAnalysis: (img) async => _imageStreamController.add(img),
        imageAnalysisConfig: AnalysisConfig(
          androidOptions: const AndroidAnalysisOptions.yuv420(
            width: 150,
          ),
          maxFramesPerSecond: 30,
        ),
        builder: (state, previewSize, previewRect) {
          // Return an empty widget if you don't want to show anything
          return SizedBox();
          // Or return your UI with the state as parameter
          // return MyAnalyticsUI(state: state);
        },
      )

You could pass the state to your UI (like for MyAnalyticsUI) or even the state.analysisController if you only want to start/stop the analytics stream manually.

@pinpong
Copy link

pinpong commented Apr 20, 2023

Need to use the camera without any imports package:flutter/widgets.dart or classes that extends the widget class.

@pitriq
Copy link

pitriq commented May 4, 2023

In our case, we are subscribing to state to add some custom behaviour when a picture is taken.

With the current implementation, we need to set this up in builder and check for the existence of a previous subscription on every state change, which feels pretty bad.

return CameraAwesomeBuilder.custom(
  // ...
  builder: (state, _, __) {
    _setUpCaptureStateSubscription(state);

    // Custom UI
    // ...
  },
);

void _setUpCaptureStateSubscription(CameraState state) {
  if (_cameraFilesSubscription != null) return;
  _cameraFilesSubscription =
      state.captureState$.listen((event) async {
    if (event == null) return;
    if (event.status == MediaCaptureStatus.capturing) {
      _isCapturing = true;
      if (event.isPicture) _showBarrier();
      return;
    }
    if (event.status == MediaCaptureStatus.success) {
      await widget.onMediaSaved?.call(event.filePath);
    }
    _isCapturing = false;
  });
}

A controller/key would allow us to set this subscription up somewhere else, resulting in a cleaner implementation. I agree with the previously stated in this issue, builders should only be responsible of displaying the UI.

@live9080
Copy link

live9080 commented Sep 15, 2023

In my case, I need an automatic photo taken every few seconds. So, I have a Timer in the Bloc. Currently, I have to pass the cameraState Blob when building.

CameraAwesomeBuilder.awesome(
  saveConfig: SaveConfig.photo(),
  previewFit: CameraPreviewFit.contain,
  topActionsBuilder: (CameraState cameraState) {
    blob.add(SetCameraStateEvent(cameraState));
    return AwesomeTopActions(state: cameraState);
  },
)

I also tried the static CamerawesomePlugin.takePhoto(). It works but feel weird. // no matter which sensor was pass, It will be omitted(current preview view is be adoptive).

// These two method had the same behavior.
await CamerawesomePlugin.takePhoto(SingleCaptureRequest(
  imagePath,
  Sensor.position(SensorPosition.back),
));
await CamerawesomePlugin.takePhoto(SingleCaptureRequest(
  imagePath,
  Sensor.position(SensorPosition.front),
));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants