mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 12:48:41 +00:00
starting with #30
This commit is contained in:
parent
3a9234740e
commit
9edb22e4f4
2 changed files with 209 additions and 2 deletions
194
lib/src/components/zoom_selector.dart
Normal file
194
lib/src/components/zoom_selector.dart
Normal 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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:camerawesome/camerawesome_plugin.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.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/components/image_editor/action_button.dart';
|
||||
import 'package:twonly/src/components/media_view_sizing.dart';
|
||||
|
|
@ -65,7 +66,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
previewAlignment: Alignment.topLeft,
|
||||
sensorConfig: SensorConfig.single(
|
||||
aspectRatio: CameraAspectRatios.ratio_16_9,
|
||||
zoom: 0.07,
|
||||
zoom: 0.5,
|
||||
),
|
||||
previewFit: CameraPreviewFit.contain,
|
||||
progressIndicator: Container(),
|
||||
|
|
@ -138,11 +139,19 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
Positioned.fill(
|
||||
child: GestureDetector(
|
||||
onPanStart: (details) async {
|
||||
if (cameraState.sensorConfig.sensors.first.position ==
|
||||
SensorPosition.front) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_basePanY = details.localPosition.dy;
|
||||
});
|
||||
},
|
||||
onPanUpdate: (details) async {
|
||||
if (cameraState.sensorConfig.sensors.first.position ==
|
||||
SensorPosition.front) {
|
||||
return;
|
||||
}
|
||||
var diff = _basePanY - details.localPosition.dy;
|
||||
if (diff > 200) diff = 200;
|
||||
if (diff < 0) diff = 0;
|
||||
|
|
@ -156,9 +165,13 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
}
|
||||
},
|
||||
onDoubleTap: () async {
|
||||
bool isFront =
|
||||
cameraState.sensorConfig.sensors.first.position ==
|
||||
SensorPosition.front;
|
||||
cameraState.switchCameraSensor(
|
||||
aspectRatio: CameraAspectRatios.ratio_16_9,
|
||||
flash: isFlashOn ? FlashMode.on : FlashMode.none,
|
||||
zoom: isFront ? 0.5 : 0,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -222,7 +235,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
alignment: Alignment.bottomCenter,
|
||||
child: Column(
|
||||
children: [
|
||||
AwesomeZoomSelector(state: cameraState),
|
||||
ZoomSelector(state: cameraState),
|
||||
const SizedBox(height: 30),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
|
|
|
|||
Loading…
Reference in a new issue