From 1f83814f4b12af16037d0c28369e7e39c11ce2e4 Mon Sep 17 00:00:00 2001 From: MaddinMade Date: Sat, 12 Mar 2022 17:52:04 +0100 Subject: [PATCH] version 0.3.0 --- CHANGELOG.md | 5 + lib/src/action_slider_widget.dart | 164 +++++++++++++++++------------- lib/src/mode.dart | 26 ++++- pubspec.yaml | 2 +- 4 files changed, 123 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaca61e..ff46eed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.3.0 [2022-03-12] +- adds onTap parameter +- adds jump method to ActionSliderController +- minor gesture detection improvements + ## 0.2.1 [2022-02-28] - changes README.md diff --git a/lib/src/action_slider_widget.dart b/lib/src/action_slider_widget.dart index 6ba5682..1086d2c 100644 --- a/lib/src/action_slider_widget.dart +++ b/lib/src/action_slider_widget.dart @@ -4,6 +4,7 @@ import 'package:action_slider/action_slider.dart'; import 'package:action_slider/src/cross_fade.dart'; import 'package:action_slider/src/mode.dart'; import 'package:action_slider/src/state.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; enum SliderBehavior { move, stretch } @@ -13,9 +14,14 @@ typedef BackgroundBuilder = Widget Function( typedef ForegroundBuilder = Widget Function( BuildContext, ActionSliderState, Widget?); typedef SlideCallback = Function(ActionSliderController controller); +typedef TapCallback = Function(ActionSliderController controller); -class ActionSliderController extends ValueNotifier { - ActionSliderController() : super(SliderMode.standard); +class ActionSliderController extends ChangeNotifier + implements ValueListenable { + SliderMode _value = SliderMode.standard; + + @override + SliderMode get value => _value; ///Sets the state to success void success() => _setMode(SliderMode.success); @@ -29,12 +35,19 @@ class ActionSliderController extends ValueNotifier { ///Sets the state to loading void loading() => _setMode(SliderMode.loading); + ///The Toggle jumps to [pos] which should be between 0.0 and 1.0. + void jump([double pos = 0.3]) => _setMode(SliderMode.jump(pos)); + ///Allows to define custom [SliderMode]s. ///This is useful for other results like success or failure. ///You get this modes in the [foregroundBuilder] of [ConfirmationSlider.custom] or in the [customForegroundBuilder] of [ConfirmationSlider.standard]. void custom(SliderMode mode) => _setMode(mode); - void _setMode(SliderMode mode) => value = mode; + void _setMode(SliderMode mode, {bool notify = true}) { + if (value == mode) return; + _value = mode; + if (notify) notifyListeners(); + } } class ActionSlider extends StatefulWidget { @@ -89,10 +102,17 @@ class ActionSlider extends StatefulWidget { final BorderRadius backgroundBorderRadius; ///Callback for sliding completely to the right. - ///Here you should call the loading, success and failure methods of the [controller] for controlling the further behaviour/animations of the slider. - ///Optionally [onSlide] can be a [SlideCallback] for using the widget without an external controller. + ///Here you should call the loading, success and failure methods of the + ///[controller] for controlling the further behaviour/animations of the + ///slider. final SlideCallback? onSlide; + ///Callback for tapping on the [ActionSlider]. Defaults to (c) => c.jump(). + ///Is only called if the toggle is currently not dragged. + ///If you want onTap to be called in any case, you should wrap ActionSlider + ///in a GestureDetector. + final TapCallback? onTap; + ///Controller for controlling the widget from everywhere. final ActionSliderController? controller; @@ -130,8 +150,11 @@ class ActionSlider extends StatefulWidget { ) ], this.sliderBehavior = SliderBehavior.move, + this.onTap = _defaultOnTap, }) : super(key: key); + static _defaultOnTap(ActionSliderController c) => c.jump(); + ///Standard constructor for creating a Slider. ///If [customForegroundBuilder] is not null, the values of [successIcon], [failureIcon], [loadingIcon] and [icon] are ignored. ///This is useful if you use your own [SliderMode]s. @@ -151,6 +174,7 @@ class ActionSlider extends StatefulWidget { double circleRadius = 25.0, bool rolling = false, SlideCallback? onSlide, + TapCallback? onTap = _defaultOnTap, ActionSliderController? controller, double? width, Duration slideAnimationDuration = const Duration(milliseconds: 250), @@ -197,6 +221,7 @@ class ActionSlider extends StatefulWidget { toggleHeight: circleRadius * 2, backgroundColor: backgroundColor, onSlide: onSlide, + onTap: onTap, controller: controller, width: width, slideAnimationDuration: slideAnimationDuration, @@ -281,7 +306,7 @@ class ActionSlider extends StatefulWidget { child = successIcon; } else if (mode == SliderMode.failure) { child = failureIcon; - } else if (mode == SliderMode.standard) { + } else if (mode == SliderMode.standard || mode.isJump) { child = rotating ? Transform.rotate( angle: state.position * state.size.width / radius, @@ -385,8 +410,14 @@ class _ActionSliderState extends State if (_controller.value.expanded) { _slideAnimationController.reverse(); _loadingAnimationController.reverse(); - setState(() => state = - state.copyWith(releasePosition: 1.0, state: SlidingState.released)); + if (_controller.value.jumpPosition > 0.0) { + _controller._setMode(SliderMode.standard, notify: false); + state = state.copyWith(releasePosition: 0.3); + _slideAnimationController.forward(); + } else { + setState(() => state = + state.copyWith(releasePosition: 1.0, state: SlidingState.released)); + } } else { _loadingAnimationController.forward(); setState(() => state = @@ -429,49 +460,9 @@ class _ActionSliderState extends State } return GestureDetector( - onHorizontalDragStart: (details) { - final x = details.localPosition.dx; - if (x >= togglePosition && x <= toggleWidth) { - setState(() { - state = SliderState( - position: ((details.localPosition.dx - - frame - - widget.toggleWidth / 2) / - backgroundWidth) - .clamp(0.0, 1.0), - state: SlidingState.dragged, - ); - }); - } - }, - onHorizontalDragUpdate: (details) { - if (state.state == SlidingState.dragged) { - double newPosition = ((details.localPosition.dx - - frame - - widget.toggleWidth / 2) / - backgroundWidth) - .clamp(0.0, 1.0); - setState(() { - state = SliderState( - position: newPosition, - state: newPosition < 1.0 - ? SlidingState.dragged - : SlidingState.released, - ); - }); - if (state.state == SlidingState.released) _onSlide(); - } - }, - onHorizontalDragEnd: (details) => setState(() { - state = state.copyWith( - state: SlidingState.released, - releasePosition: state.position); - _slideAnimationController.reverse(from: 1.0); - }), onTap: () { if (state.state != SlidingState.released) return; - state = state.copyWith(releasePosition: 0.3); - _slideAnimationController.forward(); + widget.onTap?.call(_controller); }, child: Container( width: width, @@ -508,26 +499,63 @@ class _ActionSliderState extends State left: togglePosition, width: toggleWidth, height: widget.toggleHeight, - child: MouseRegion( - cursor: state.state == SlidingState.loading - ? MouseCursor.defer - : (state.state == SlidingState.released - ? SystemMouseCursors.grab - : SystemMouseCursors.grabbing), - child: Builder( - builder: (context) => widget.foregroundBuilder( - context, - ActionSliderState( - position: state.position, - size: Size(width, widget.height), - standardSize: Size(maxWidth, widget.height), - slidingState: state.state, - sliderMode: _controller.value, - releasePosition: state.releasePosition, - toggleSize: - Size(toggleWidth, widget.toggleHeight), + child: GestureDetector( + onHorizontalDragStart: (details) { + setState(() { + state = SliderState( + position: ((details.localPosition.dx - + widget.toggleWidth / 2) / + backgroundWidth) + .clamp(0.0, 1.0), + state: SlidingState.dragged, + ); + }); + }, + onHorizontalDragUpdate: (details) { + if (state.state == SlidingState.dragged) { + double newPosition = ((details.localPosition.dx - + widget.toggleWidth / 2) / + backgroundWidth) + .clamp(0.0, 1.0); + setState(() { + state = SliderState( + position: newPosition, + state: newPosition < 1.0 + ? SlidingState.dragged + : SlidingState.released, + ); + }); + if (state.state == SlidingState.released) + _onSlide(); + } + }, + onHorizontalDragEnd: (details) => setState(() { + state = state.copyWith( + state: SlidingState.released, + releasePosition: state.position); + _slideAnimationController.reverse(from: 1.0); + }), + child: MouseRegion( + cursor: state.state == SlidingState.loading + ? MouseCursor.defer + : (state.state == SlidingState.released + ? SystemMouseCursors.grab + : SystemMouseCursors.grabbing), + child: Builder( + builder: (context) => widget.foregroundBuilder( + context, + ActionSliderState( + position: state.position, + size: Size(width, widget.height), + standardSize: Size(maxWidth, widget.height), + slidingState: state.state, + sliderMode: _controller.value, + releasePosition: state.releasePosition, + toggleSize: + Size(toggleWidth, widget.toggleHeight), + ), + widget.foregroundChild, ), - widget.foregroundChild, ), ), ), diff --git a/lib/src/mode.dart b/lib/src/mode.dart index 3124a28..2e0c1a8 100644 --- a/lib/src/mode.dart +++ b/lib/src/mode.dart @@ -2,11 +2,17 @@ class SliderMode { final dynamic key; final bool expanded; final bool custom; + final double jumpPosition; - const SliderMode({required this.key, this.expanded = false}) : custom = true; + const SliderMode({required this.key, this.expanded = false}) + : jumpPosition = 0.0, + custom = true; - const SliderMode._internal({required this.key, this.expanded = false}) - : custom = false; + const SliderMode._internal({ + required this.key, + this.expanded = false, + this.jumpPosition = 0.0, + }) : custom = false; static const loading = SliderMode._internal(key: _SliderModeKey('loading')); static const success = SliderMode._internal(key: _SliderModeKey('success')); @@ -16,16 +22,26 @@ class SliderMode { expanded: true, ); + bool get isJump => key == 'jump' && !custom; + + const SliderMode.jump(double pos) + : this._internal( + key: 'jump', + expanded: true, + jumpPosition: pos, + ); + @override bool operator ==(Object other) => identical(this, other) || other is SliderMode && runtimeType == other.runtimeType && key == other.key && - custom == other.custom; + custom == other.custom && + jumpPosition == other.jumpPosition; @override - int get hashCode => key.hashCode ^ custom.hashCode; + int get hashCode => key.hashCode ^ custom.hashCode ^ jumpPosition.hashCode; } class _SliderModeKey { diff --git a/pubspec.yaml b/pubspec.yaml index 49aedce..233b5c9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: action_slider description: A slider to confirm actions and provide feedback on the success of these after subsequent loading. -version: 0.2.1 +version: 0.3.0 repository: https://github.com/SplashByte/action_slider environment: