Skip to content

Commit

Permalink
Finish thumb
Browse files Browse the repository at this point in the history
  • Loading branch information
Pante committed Aug 14, 2024
1 parent 3c2991b commit 12e4bfc
Show file tree
Hide file tree
Showing 5 changed files with 325 additions and 126 deletions.
144 changes: 70 additions & 74 deletions forui/lib/src/widgets/slider/bar.dart
Original file line number Diff line number Diff line change
@@ -1,147 +1,143 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:forui/forui.dart';
import 'package:forui/src/widgets/slider/slider.dart';
import 'package:meta/meta.dart';

@internal
class Bar extends StatelessWidget {
final FSliderController controller;
final FSliderStyle style;
final LayoutDirection direction;
final List<FSliderMark> marks;

const Bar({
required this.controller,
required this.style,
required this.direction,
required this.marks,
super.key,
});
const Bar({super.key});

@override
Widget build(BuildContext context) {
final InheritedData(
:controller,
style: FSliderStyle(:activeColor, :inactiveColor, :borderRadius, :crossAxisExtent, :markStyle, :thumbStyle),
:direction,
:marks,
:enabled,
) = InheritedData.of(context);

late final double height;
late final double width;
late final double Function(TapDownDetails) translate;
late final Widget Function(double, Widget) marker;
late final ValueWidgetBuilder<FSliderData> active;

final half = style.thumbStyle.dimension / 2;
// We use the thumb style's dimension as the bar's padding.
final half = thumbStyle.dimension / 2;

switch (direction) {
case LayoutDirection.ltr:
height = style.crossAxisExtent;
width = controller.value.extent.total + style.thumbStyle.dimension;
height = crossAxisExtent;
width = controller.value.extent.total + thumbStyle.dimension;
translate = (details) => details.localPosition.dx - half;
marker = (offset, marker) => Positioned(left: offset, child: marker);
active = (context, active, child) => Positioned(
left: active.offset.min,
child: SizedBox(
height: style.crossAxisExtent,
height: crossAxisExtent,
width: active.extent.current + half,
child: child!,
),
);

case LayoutDirection.rtl:
height = style.crossAxisExtent;
width = controller.value.extent.total + style.thumbStyle.dimension;
height = crossAxisExtent;
width = controller.value.extent.total + thumbStyle.dimension;
translate = (details) => controller.value.extent.total + half - details.localPosition.dx;
marker = (offset, marker) => Positioned(right: offset, child: marker);
active = (context, active, child) => Positioned(
right: active.offset.min,
child: SizedBox(
height: style.crossAxisExtent,
height: crossAxisExtent,
width: active.extent.current + half,
child: child!,
),
);

case LayoutDirection.ttb:
height = controller.value.extent.total + style.thumbStyle.dimension;
width = style.crossAxisExtent;
height = controller.value.extent.total + thumbStyle.dimension;
width = crossAxisExtent;
translate = (details) => details.localPosition.dy - half;
marker = (offset, marker) => Positioned(top: offset, child: marker);
active = (context, active, child) => Positioned(
top: active.offset.min,
child: SizedBox(
height: active.extent.current + half,
width: style.crossAxisExtent,
width: crossAxisExtent,
child: child!,
),
);

case LayoutDirection.btt:
height = controller.value.extent.total + style.thumbStyle.dimension;
width = style.crossAxisExtent;
height = controller.value.extent.total + thumbStyle.dimension;
width = crossAxisExtent;
translate = (details) => controller.value.extent.total + half - details.localPosition.dy;
marker = (offset, marker) => Positioned(bottom: offset, child: marker);
active = (context, active, child) => Positioned(
bottom: active.offset.min,
child: SizedBox(
height: active.extent.current + half,
width: style.crossAxisExtent,
width: crossAxisExtent,
child: child!,
),
);
}

return GestureDetector(
onTapDown: (details) => controller.tap(
switch (translate(details)) {
< 0 => 0,
final translated when controller.value.extent.total < translated => controller.value.extent.total,
final translated => translated,
},
Widget bar = DecoratedBox(
decoration: BoxDecoration(
borderRadius: borderRadius,
color: inactiveColor,
),
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: style.borderRadius,
color: style.inactiveColor,
),
child: SizedBox(
height: height,
width: width,
child: Stack(
alignment: Alignment.center,
children: [
for (final FSliderMark(:style, :percentage, :visible) in marks)
if (visible)
marker(
percentage * controller.value.extent.total + half - ((style ?? this.style.markStyle).dimension / 2),
DecoratedBox(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: (style ?? this.style.markStyle).color,
),
child: SizedBox.square(
dimension: (style ?? this.style.markStyle).dimension,
),
child: SizedBox(
height: height,
width: width,
child: Stack(
alignment: Alignment.center,
children: [
for (final FSliderMark(:style, :percentage, :visible) in marks)
if (visible)
marker(
percentage * controller.value.extent.total + half - ((style ?? markStyle).dimension / 2),
DecoratedBox(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: (style ?? markStyle).color,
),
child: SizedBox.square(
dimension: (style ?? markStyle).dimension,
),
),
ValueListenableBuilder(
valueListenable: controller,
builder: active,
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: style.borderRadius,
color: style.activeColor,
),
),
ValueListenableBuilder(
valueListenable: controller,
builder: active,
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: borderRadius,
color: activeColor,
),
),
],
),
),
],
),
),
);
}

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('controller', controller))
..add(DiagnosticsProperty('style', style))
..add(EnumProperty('direction', direction))
..add(IterableProperty('marks', marks));
if (enabled) {
bar = GestureDetector(
onTapDown: (details) => controller.tap(
switch (translate(details)) {
< 0 => 0,
final translated when controller.value.extent.total < translated => controller.value.extent.total,
final translated => translated,
},
),
child: bar,
);
}

return bar;
}
}
44 changes: 44 additions & 0 deletions forui/lib/src/widgets/slider/slider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,50 @@ import 'package:forui/forui.dart';
import 'package:forui/src/widgets/slider/slider_mark.dart';
import 'package:meta/meta.dart';

@internal
class InheritedData extends InheritedWidget {
final FSliderController controller;
final FSliderStyle style;
final LayoutDirection direction;
final List<FSliderMark> marks;
final bool enabled;

static InheritedData of(BuildContext context) {
final InheritedData? result = context.dependOnInheritedWidgetOfExactType<InheritedData>();
assert(result != null, 'No InheritedData found in context');
return result!;
}

const InheritedData({
required this.controller,
required this.style,
required this.direction,
required this.marks,
required this.enabled,
required super.child,
super.key,
});

@override
bool updateShouldNotify(InheritedData old) =>
controller != old.controller ||
style != old.style ||
direction != old.direction ||
marks != old.marks ||
enabled != old.enabled;

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('controller', controller))
..add(DiagnosticsProperty('style', style))
..add(EnumProperty('direction', direction))
..add(IterableProperty('marks', marks))
..add(FlagProperty('enabled', value: enabled, ifTrue: 'enabled'));
}
}

/// A slider's styles.
final class FSliderStyles with Diagnosticable {
/// The enabled slider's style.
Expand Down
12 changes: 8 additions & 4 deletions forui/lib/src/widgets/slider/slider_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@ import 'package:flutter/widgets.dart';
import 'package:forui/forui.dart';

abstract class FSliderController extends ValueNotifier<FSliderData> {
/// Whether the slider is extendable at the min and max sides.
final ({bool min, bool max}) extendable;

/// True if the slider has continuous values, and false if it has discrete values.
final bool continuous;

factory FSliderController(FSliderData value) = _Stub;

FSliderController.value(super.value);
FSliderController.value(super._value, {required this.extendable, required this.continuous});

void drag(double delta);

void tap(double offset);

({bool min, bool max}) get extendable;
}

// TODO: remove
final class _Stub extends FSliderController {
_Stub(super.value) : super.value();
_Stub(super.value) : super.value(extendable: (min: true, max: true), continuous: true);

@override
void drag(double delta) {
Expand Down
Loading

0 comments on commit 12e4bfc

Please sign in to comment.