starting with #30

This commit is contained in:
otsmr 2025-02-13 23:50:09 +01:00
parent 3a9234740e
commit 9edb22e4f4
2 changed files with 209 additions and 2 deletions

View file

@ -0,0 +1,194 @@
import 'package:camerawesome/camerawesome_plugin.dart';
import 'package:flutter/material.dart';
class ZoomSelector extends StatefulWidget {
final CameraState state;
const ZoomSelector({
super.key,
required this.state,
});
@override
State<ZoomSelector> createState() => _ZoomSelectorState();
}
class _ZoomSelectorState extends State<ZoomSelector> {
double? minZoom;
double? maxZoom;
@override
void initState() {
super.initState();
initAsync();
}
initAsync() async {
minZoom = await CamerawesomePlugin.getMinZoom();
maxZoom = await CamerawesomePlugin.getMaxZoom();
}
@override
Widget build(BuildContext context) {
return StreamBuilder<SensorConfig>(
stream: widget.state.sensorConfig$,
builder: (context, sensorConfigSnapshot) {
initAsync();
if (sensorConfigSnapshot.data == null ||
minZoom == null ||
maxZoom == null) {
return const SizedBox.shrink();
}
return StreamBuilder<double>(
stream: sensorConfigSnapshot.requireData.zoom$,
builder: (context, snapshot) {
if (snapshot.hasData) {
return _ZoomIndicatorLayout(
zoom: snapshot.requireData,
min: minZoom!,
max: maxZoom!,
sensorConfig: widget.state.sensorConfig,
);
} else {
return const SizedBox.shrink();
}
},
);
},
);
}
}
class _ZoomIndicatorLayout extends StatelessWidget {
final double zoom;
final double min;
final double max;
final SensorConfig sensorConfig;
const _ZoomIndicatorLayout({
required this.zoom,
required this.min,
required this.max,
required this.sensorConfig,
});
@override
Widget build(BuildContext context) {
final displayZoom = (max - min) * zoom + min;
if (min == 1.0) {
return Container();
}
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Show 3 dots for zooming: min, 1.0X and max zoom. The closer one shows
// text, the other ones a dot.
_ZoomIndicator(
normalValue: 0.0,
zoom: zoom,
selected: displayZoom < 1.0,
min: min,
max: max,
sensorConfig: sensorConfig,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: _ZoomIndicator(
normalValue: 0.5,
zoom: zoom,
selected: !(displayZoom < 1.0 || displayZoom == max),
min: min,
max: max,
sensorConfig: sensorConfig,
),
),
_ZoomIndicator(
normalValue: 1.0,
zoom: zoom,
selected: displayZoom == max,
min: min,
max: max,
sensorConfig: sensorConfig,
),
],
);
}
}
class _ZoomIndicator extends StatelessWidget {
final double zoom;
final double min;
final double max;
final double normalValue;
final SensorConfig sensorConfig;
final bool selected;
const _ZoomIndicator({
required this.zoom,
required this.min,
required this.max,
required this.normalValue,
required this.sensorConfig,
required this.selected,
});
@override
Widget build(BuildContext context) {
final baseTheme = AwesomeThemeProvider.of(context).theme;
final baseButtonTheme = baseTheme.buttonTheme;
final displayZoom = (max - min) * zoom + min;
Widget content = AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
transitionBuilder: (child, anim) {
return ScaleTransition(scale: anim, child: child);
},
child: selected
? AwesomeBouncingWidget(
key: ValueKey("zoomIndicator_${normalValue}_selected"),
onTap: () {
sensorConfig.setZoom(normalValue);
},
child: Container(
color: Colors.transparent,
padding: const EdgeInsets.all(0.0),
child: AwesomeCircleWidget(
theme: baseTheme,
child: Text(
"${displayZoom.toStringAsFixed(1)}X",
style: const TextStyle(color: Colors.white, fontSize: 12),
),
),
),
)
: AwesomeBouncingWidget(
key: ValueKey("zoomIndicator_${normalValue}_unselected"),
onTap: () {
sensorConfig.setZoom(normalValue);
},
child: Container(
color: Colors.transparent,
padding: const EdgeInsets.all(16.0),
child: AwesomeCircleWidget(
theme: baseTheme.copyWith(
buttonTheme: baseButtonTheme.copyWith(
backgroundColor: baseButtonTheme.foregroundColor,
padding: EdgeInsets.zero,
),
),
child: const SizedBox(width: 6, height: 6),
),
),
),
);
// Same width for each dot to keep them in their position
return SizedBox(
width: 56,
child: Center(
child: content,
),
);
}
}

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:camerawesome/camerawesome_plugin.dart'; import 'package:camerawesome/camerawesome_plugin.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:twonly/src/components/zoom_selector.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/components/image_editor/action_button.dart'; import 'package:twonly/src/components/image_editor/action_button.dart';
import 'package:twonly/src/components/media_view_sizing.dart'; import 'package:twonly/src/components/media_view_sizing.dart';
@ -65,7 +66,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
previewAlignment: Alignment.topLeft, previewAlignment: Alignment.topLeft,
sensorConfig: SensorConfig.single( sensorConfig: SensorConfig.single(
aspectRatio: CameraAspectRatios.ratio_16_9, aspectRatio: CameraAspectRatios.ratio_16_9,
zoom: 0.07, zoom: 0.5,
), ),
previewFit: CameraPreviewFit.contain, previewFit: CameraPreviewFit.contain,
progressIndicator: Container(), progressIndicator: Container(),
@ -138,11 +139,19 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
Positioned.fill( Positioned.fill(
child: GestureDetector( child: GestureDetector(
onPanStart: (details) async { onPanStart: (details) async {
if (cameraState.sensorConfig.sensors.first.position ==
SensorPosition.front) {
return;
}
setState(() { setState(() {
_basePanY = details.localPosition.dy; _basePanY = details.localPosition.dy;
}); });
}, },
onPanUpdate: (details) async { onPanUpdate: (details) async {
if (cameraState.sensorConfig.sensors.first.position ==
SensorPosition.front) {
return;
}
var diff = _basePanY - details.localPosition.dy; var diff = _basePanY - details.localPosition.dy;
if (diff > 200) diff = 200; if (diff > 200) diff = 200;
if (diff < 0) diff = 0; if (diff < 0) diff = 0;
@ -156,9 +165,13 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
} }
}, },
onDoubleTap: () async { onDoubleTap: () async {
bool isFront =
cameraState.sensorConfig.sensors.first.position ==
SensorPosition.front;
cameraState.switchCameraSensor( cameraState.switchCameraSensor(
aspectRatio: CameraAspectRatios.ratio_16_9, aspectRatio: CameraAspectRatios.ratio_16_9,
flash: isFlashOn ? FlashMode.on : FlashMode.none, flash: isFlashOn ? FlashMode.on : FlashMode.none,
zoom: isFront ? 0.5 : 0,
); );
}, },
), ),
@ -222,7 +235,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: Column( child: Column(
children: [ children: [
AwesomeZoomSelector(state: cameraState), ZoomSelector(state: cameraState),
const SizedBox(height: 30), const SizedBox(height: 30),
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {