m3_lightmeter/lib/screens/shared/centered_slider/widget_slider_centered.dart

181 lines
5.1 KiB
Dart
Raw Normal View History

import 'package:flutter/material.dart';
import 'package:lightmeter/res/dimens.dart';
class CenteredSlider extends StatefulWidget {
final Icon? icon;
final double value;
final double min;
final double max;
final ValueChanged<double> onChanged;
final bool isVertical;
const CenteredSlider({
this.icon,
required this.value,
required this.min,
required this.max,
required this.onChanged,
this.isVertical = false,
super.key,
});
@override
State<CenteredSlider> createState() => _CenteredSliderState();
}
class _CenteredSliderState extends State<CenteredSlider> {
double relativeValue = 0.0;
@override
void initState() {
super.initState();
relativeValue = (widget.value - widget.min) / (widget.max - widget.min);
}
@override
void didUpdateWidget(CenteredSlider oldWidget) {
super.didUpdateWidget(oldWidget);
relativeValue = (widget.value - widget.min) / (widget.max - widget.min);
}
@override
Widget build(BuildContext context) {
return SizedBox(
2024-03-22 17:11:12 +00:00
height: widget.isVertical ? double.maxFinite : Dimens.cameraSliderHandleArea,
width: !widget.isVertical ? double.maxFinite : Dimens.cameraSliderHandleArea,
child: LayoutBuilder(
builder: (context, constraints) {
final biggestSize = widget.isVertical ? constraints.maxHeight : constraints.maxWidth;
2024-03-22 17:11:12 +00:00
final handleDistance = biggestSize - Dimens.cameraSliderHandleArea;
return RotatedBox(
quarterTurns: widget.isVertical ? -1 : 0,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
2023-01-26 15:03:48 +00:00
onTapUp: (details) => _updateHandlePosition(
details.localPosition.dx,
handleDistance,
),
onHorizontalDragUpdate: (details) => _updateHandlePosition(
details.localPosition.dx,
handleDistance,
),
child: SizedBox(
2024-03-22 17:11:12 +00:00
height: Dimens.cameraSliderHandleArea,
width: biggestSize,
child: _Slider(
handleDistance: handleDistance,
2024-03-22 17:11:12 +00:00
handleSize: Dimens.cameraSliderHandleArea,
trackThickness: Dimens.cameraSliderTrackHeight,
value: relativeValue,
icon: RotatedBox(
quarterTurns: widget.isVertical ? 1 : 0,
child: widget.icon,
),
),
),
),
);
},
),
);
}
void _updateHandlePosition(double offset, double handleDistance) {
2024-03-22 17:11:12 +00:00
if (offset <= Dimens.cameraSliderHandleArea / 2) {
relativeValue = 0;
2024-03-22 17:11:12 +00:00
} else if (offset >= handleDistance + Dimens.cameraSliderHandleArea / 2) {
relativeValue = 1;
} else {
2024-03-22 17:11:12 +00:00
relativeValue = (offset - Dimens.cameraSliderHandleArea / 2) / handleDistance;
}
setState(() {});
2023-01-26 15:03:48 +00:00
widget.onChanged(relativeValue * (widget.max - widget.min) + widget.min);
}
}
class _Slider extends StatelessWidget {
final double handleSize;
final double trackThickness;
final double handleDistance;
final double value;
final Widget icon;
const _Slider({
required this.handleSize,
required this.trackThickness,
required this.handleDistance,
required this.value,
required this.icon,
});
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
Positioned(
height: trackThickness,
2023-01-26 15:03:48 +00:00
/// add thickness to maintain radius overlap with handle
width: handleDistance + trackThickness,
child: ClipRRect(
borderRadius: BorderRadius.circular(trackThickness / 2),
child: ColoredBox(color: Theme.of(context).colorScheme.surfaceVariant),
),
),
AnimatedPositioned.fromRect(
2024-03-22 17:11:12 +00:00
duration: Dimens.durationS,
rect: Rect.fromCenter(
center: Offset(
handleSize / 2 + handleDistance * value,
handleSize / 2,
),
width: handleSize,
height: handleSize,
),
2024-03-22 17:11:12 +00:00
child: Center(
child: _Handle(
color: Theme.of(context).colorScheme.primary,
size: Dimens.cameraSliderHandleSize,
child: IconTheme(
data: Theme.of(context).iconTheme.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
size: Dimens.cameraSliderHandleIconSize,
),
child: icon,
),
),
),
),
],
);
}
}
class _Handle extends StatelessWidget {
final Color color;
final double size;
final Widget? child;
const _Handle({
required this.color,
required this.size,
this.child,
});
@override
Widget build(BuildContext context) {
2024-03-22 17:11:12 +00:00
return SizedBox(
height: size,
width: size,
child: DecoratedBox(
decoration: BoxDecoration(
color: color,
2024-03-22 17:11:12 +00:00
shape: BoxShape.circle,
),
2024-03-22 17:11:12 +00:00
child: child,
),
);
}
}