Open apalala-dev opened 1 year ago
Plugin looking good but without a controller it's not the right plugin for our usecase.
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(),
);
}
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);
}
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.
Need to use the camera without any imports package:flutter/widgets.dart
or classes that extends the widget
class.
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.
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),
));
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 thestate
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:
state
.CamerAwesomeBuilder.awesome
constructor, they may want to use the UI as is but the need of registering thestate
would force them to implement abuilder
.Cons:
Like (👍) this issue if you think it's worth implementing or dislike (👎) if you don't think it's worth it.