feat: v0.5.0

This commit is contained in:
Chen Asraf
2022-08-08 02:26:27 +03:00
parent 888ce626ee
commit bf752b0281
10 changed files with 222 additions and 172 deletions

8
.editorconfig Normal file
View File

@@ -0,0 +1,8 @@
[*]
tab_width = 2
indent_size = 2
indent_style = space
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

15
.prettierrc Normal file
View File

@@ -0,0 +1,15 @@
{
"printWidth": 100,
"semi": false,
"singleQuote": true,
"trailingComma": "all",
"overrides": [
{
"files": "*.md",
"options": {
"printWidth": 100,
"proseWrap": "always"
}
}
]
}

View File

@@ -1,10 +1,31 @@
## [0.5.0] - Aug 8, 2022
- General code improvement + finalize null safety support
## [0.0.7-nullsafety] - Jul 10, 2021
- No changes
## [0.0.6-nullsafety] - Jul 30, 2021
- Updated the package for null-safety
## [0.0.6] - 22 Mar, 2019
* Added style customization options
- Added style customization options
## [0.0.5] - 22 Mar, 2019
* Added finger fling physics to control
- Added finger fling physics to control
## [0.0.3-0.0.4] - 10 Mar, 2019
* Bugfixes
- Bugfixes
## [0.0.2] - 10 Mar, 2019
* Added `minMaxLabelBuilder`
- Added `minMaxLabelBuilder`
## [0.0.1] - 10 Mar, 2019
* Initial release
- Initial release

21
LICENSE
View File

@@ -1,9 +1,20 @@
Copyright 2019 Chen Asraf
Copyright 2019-2022 Chen Asraf
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
1. Redistributions of source code must retain the above copyright notice, this list of conditions
and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,2 +1,2 @@
sdk.dir=/usr/local/Cellar/android-sdk/24.4.1_1
flutter.sdk=/Users/chenasraf/.flutter.src
sdk.dir=/Users/chen/Library/Android/sdk
flutter.sdk=/Users/chen/.flutter-src

View File

@@ -0,0 +1,13 @@
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/chen/.flutter-src"
export "FLUTTER_APPLICATION_PATH=/Users/chen/Dev/wheel_spinner"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_TARGET=lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "FLUTTER_BUILD_NAME=0.0.7"
export "FLUTTER_BUILD_NUMBER=0.0.7"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.dart_tool/package_config.json"

View File

@@ -1,4 +1,5 @@
import 'dart:math';
/// clamps [value] to [min] and [max]
double clamp<T extends num>(T number, T low, T high) =>
max(low * 1.0, min(number * 1.0, high * 1.0));

View File

@@ -1,21 +1,19 @@
library wheel_spinner;
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:wheel_spinner/utils.dart';
typedef Widget ValueBuilder(double value);
typedef String ValueStringBuilder(double value);
typedef ValueBuilder = Widget Function(double value);
typedef ValueStringBuilder = String Function(double value);
/// Shows a "dial" spinner that can be dragged up or down, either unlimited or
/// restricted by [max] and [min].
class WheelSpinner extends StatefulWidget {
/// Callback for when the user drags the slider
final Function(double value) onSlideUpdate;
final Function(double value)? onSlideUpdate;
/// Callback for when the user lets go of the slider
final Function(double value) onSlideDone;
final Function(double value)? onSlideDone;
/// The widget [width]
final double width;
@@ -33,47 +31,47 @@ class WheelSpinner extends StatefulWidget {
final double max;
/// Builder for children of the slider.
final ValueBuilder childBuilder;
final ValueBuilder? childBuilder;
/// Allows overriding the format of the left top and bottom labels for the [min]/[max] values
final ValueStringBuilder minMaxLabelBuilder;
final ValueStringBuilder? minMaxLabelBuilder;
/// Allows to override style of labels
final TextStyle labelStyle;
final TextStyle? labelStyle;
/// Override box decoration for the control
final BoxDecoration boxDecoration;
final BoxDecoration? boxDecoration;
/// Override box border for the control's [boxDecoration].
/// If [boxDecoration] is specified, it overrides this property.
final Border border;
final Border? border;
/// Override border radius for the control's [boxDecoration].
/// If [boxDecoration] is specified, it overrides this property.
final BorderRadius borderRadius;
final BorderRadius? borderRadius;
/// Override background color for the control's [boxDecoration].
/// If [boxDecoration] is specified, it overrides this property.
final Color color;
final Color? color;
/// Override background gradient for the control's [boxDecoration].
/// If [boxDecoration] is specified, it overrides this property.
final Gradient gradient;
final Gradient? gradient;
/// Amount of divisions to show on the knob
final int dividerCount;
/// Color of the lines dividing the control.
final Color dividerColor;
final Color? dividerColor;
///
/// The drag speed factor
final double _dragSpeedFactor = 1.0;
static ValueStringBuilder defaultMinMaxLabelBuilder =
(v) => v.toStringAsFixed(2);
/// The default min/max label builder.
static ValueStringBuilder defaultMinMaxLabelBuilder = (v) => v.toStringAsFixed(2);
const WheelSpinner(
{Key key,
{Key? key,
this.onSlideUpdate,
this.onSlideDone,
this.width = 60,
@@ -94,23 +92,23 @@ class WheelSpinner extends StatefulWidget {
: super(key: key);
@override
// ignore: library_private_types_in_public_api
_WheelSpinnerState createState() => _WheelSpinnerState();
}
class _WheelSpinnerState extends State<WheelSpinner>
with TickerProviderStateMixin {
double value;
double dragStartValue;
Offset dragStartOffset;
AnimationController flingController;
Animation<double> flingAnimation;
void Function() currentFlingListener;
class _WheelSpinnerState extends State<WheelSpinner> with SingleTickerProviderStateMixin {
late double value;
late double dragStartValue;
Offset? dragStartOffset;
late AnimationController flingController;
late Animation<double> flingAnimation;
late void Function() currentFlingListener;
_WheelSpinnerState({this.value});
_WheelSpinnerState();
@override
void initState() {
flingAnimation = AlwaysStoppedAnimation(0.0);
flingAnimation = const AlwaysStoppedAnimation(0.0);
flingController = AnimationController(vsync: this);
value = widget.value;
super.initState();
@@ -118,18 +116,14 @@ class _WheelSpinnerState extends State<WheelSpinner>
@override
Widget build(BuildContext context) {
ValueStringBuilder minMaxBuilder =
widget.minMaxLabelBuilder ?? WheelSpinner.defaultMinMaxLabelBuilder;
double labelFontSize = Theme.of(context).textTheme.body1.fontSize * 0.75;
TextStyle labelStyle =
TextStyle(fontSize: labelFontSize).merge(widget.labelStyle);
final minMaxBuilder = widget.minMaxLabelBuilder ?? WheelSpinner.defaultMinMaxLabelBuilder;
final labelFontSize = Theme.of(context).textTheme.bodyText2!.fontSize! * 0.75;
final labelStyle = TextStyle(fontSize: labelFontSize).merge(widget.labelStyle);
String minText =
widget.max < double.infinity ? minMaxBuilder(widget.max) : null;
String maxText =
widget.min > double.negativeInfinity ? minMaxBuilder(widget.min) : null;
final minText = widget.max < double.infinity ? minMaxBuilder(widget.max) : null;
final maxText = widget.min > double.negativeInfinity ? minMaxBuilder(widget.min) : null;
return Container(
return SizedBox(
height: widget.height,
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -141,12 +135,8 @@ class _WheelSpinnerState extends State<WheelSpinner>
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
minText != null
? Text(minText, style: labelStyle)
: Container(),
maxText != null
? Text(maxText, style: labelStyle)
: Container(),
minText != null ? Text(minText, style: labelStyle) : Container(),
maxText != null ? Text(maxText, style: labelStyle) : Container(),
],
),
),
@@ -154,42 +144,36 @@ class _WheelSpinnerState extends State<WheelSpinner>
AnimatedBuilder(
animation: flingAnimation,
builder: (context, child) => GestureDetector(
onVerticalDragStart: onDragStart,
onVerticalDragUpdate: onDragUpdate,
onVerticalDragEnd: onDragDone,
child: SizedBox.fromSize(
size:
Size(widget.width.toDouble(), widget.height.toDouble()),
child: Container(
child: Stack(
children: List<Widget>.generate(
widget.dividerCount + 1,
(i) {
var top =
lineTopPos(value, i, flingAnimation.value);
return Positioned.fromRect(
rect: Rect.fromLTWH(
0.0,
top,
widget.width.toDouble(),
0,
),
child: Divider(
color:
widget.dividerColor ?? Colors.grey[600],
),
);
},
).toList() +
(widget.childBuilder != null
? [widget.childBuilder(value)]
: []),
),
decoration: widget.boxDecoration ??
_defaultBoxDecorationBuilder(),
),
onVerticalDragStart: onDragStart,
onVerticalDragUpdate: onDragUpdate,
onVerticalDragEnd: onDragDone,
child: SizedBox.fromSize(
size: Size(widget.width.toDouble(), widget.height.toDouble()),
child: Container(
decoration: widget.boxDecoration ?? _boxDecorationBuilder(),
child: Stack(
children: List<Widget>.generate(
widget.dividerCount + 1,
(i) {
final top = lineTopPos(value, i, flingAnimation.value);
return Positioned.fromRect(
rect: Rect.fromLTWH(
0.0,
top,
widget.width.toDouble(),
0,
),
child: Divider(
color: widget.dividerColor ?? Colors.grey[600],
),
);
},
).toList() +
(widget.childBuilder != null ? [widget.childBuilder!(value)] : []),
),
),
),
),
),
Expanded(
child: Padding(
@@ -209,20 +193,15 @@ class _WheelSpinnerState extends State<WheelSpinner>
);
}
BoxDecoration _defaultBoxDecorationBuilder() {
double shadowOffset = 0.2;
var decoration = BoxDecoration(
gradient: widget.gradient ?? widget.color == null
BoxDecoration _boxDecorationBuilder() {
const shadowOffset = 0.2;
return BoxDecoration(
gradient: (widget.gradient ?? widget.color) == null
? LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: [0.0, shadowOffset, 1.0 - shadowOffset, 1.0],
colors: [
Colors.grey[350],
Colors.grey[50],
Colors.grey[50],
Colors.grey[350]
],
stops: const [0.0, shadowOffset, 1.0 - shadowOffset, 1.0],
colors: [Colors.grey[350]!, Colors.grey[50]!, Colors.grey[50]!, Colors.grey[350]!],
)
: null,
color: widget.color,
@@ -230,23 +209,22 @@ class _WheelSpinnerState extends State<WheelSpinner>
Border.all(
width: 1,
style: BorderStyle.solid,
color: Colors.grey[600],
color: Colors.grey[600]!,
),
borderRadius: widget.borderRadius ?? BorderRadius.circular(3.5),
);
return decoration;
}
double lineTopPos(double value, int i, double fling) {
double valueFraction = (value.ceil() - value) * widget.dividerCount;
double indexedTop = (widget.height / widget.dividerCount * i);
double top = indexedTop + valueFraction;
final valueFraction = (value.ceil() - value) * widget.dividerCount;
final indexedTop = (widget.height / widget.dividerCount * i);
final top = indexedTop + valueFraction;
return top;
}
void onDragStart(details) {
flingController.stop();
flingAnimation = AlwaysStoppedAnimation(0.0);
flingAnimation = const AlwaysStoppedAnimation(0.0);
setState(() {
dragStartOffset = details.globalPosition;
dragStartValue = value;
@@ -255,40 +233,34 @@ class _WheelSpinnerState extends State<WheelSpinner>
void onDragUpdate(details) {
flingController.stop();
var newValue = clamp(
final newValue = clamp(
dragStartValue -
(details.globalPosition - dragStartOffset).dy /
(20.0 / widget._dragSpeedFactor),
(details.globalPosition - dragStartOffset).dy / (20.0 / widget._dragSpeedFactor),
widget.min,
widget.max);
setState(() {
value = newValue;
});
if (widget.onSlideUpdate != null) {
widget.onSlideUpdate(value);
}
widget.onSlideUpdate?.call(value);
}
void onDragDone(DragEndDetails details) {
setState(() {
dragStartOffset = null;
});
double velocity = details.primaryVelocity;
final velocity = details.primaryVelocity!;
if (velocity.abs() == 0) {
if (widget.onSlideDone != null) {
widget.onSlideDone(value);
}
widget.onSlideDone?.call(value);
return;
}
double originalValue = value;
final originalValue = value;
currentFlingListener = flingListener(originalValue);
flingController.duration = Duration(milliseconds: velocity.abs().toInt());
flingAnimation =
Tween(begin: 0.0, end: velocity / 100).animate(CurvedAnimation(
flingAnimation = Tween(begin: 0.0, end: velocity / 100).animate(CurvedAnimation(
curve: Curves.decelerate,
parent: flingController,
))
..addListener(currentFlingListener);
..addListener(currentFlingListener);
flingController
..reset()
..forward();
@@ -296,20 +268,15 @@ class _WheelSpinnerState extends State<WheelSpinner>
flingListener(double originalValue) {
return () {
double newValue =
clamp(originalValue - flingAnimation.value, widget.min, widget.max);
final newValue = clamp(originalValue - flingAnimation.value, widget.min, widget.max);
if (newValue != value) {
setState(() {
value = newValue;
});
if (flingAnimation.value == flingController.upperBound) {
if (widget.onSlideDone != null) {
widget.onSlideDone(value);
}
widget.onSlideDone?.call(value);
} else {
if (widget.onSlideUpdate != null) {
widget.onSlideUpdate(value);
}
widget.onSlideUpdate?.call(value);
}
}
};

View File

@@ -1,5 +1,5 @@
# Generated by pub
# See https://www.dartlang.org/tools/pub/glossary#lockfile
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
@@ -7,73 +7,94 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
version: "2.9.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
charcode:
version: "2.1.0"
characters:
dependency: transitive
description:
name: charcode
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
version: "1.2.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.14.11"
version: "1.16.0"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.3+1"
version: "0.12.12"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.6"
version: "1.8.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.2"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.0"
quiver:
dependency: transitive
description:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
version: "1.8.2"
sky_engine:
dependency: transitive
description: flutter
@@ -85,55 +106,48 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.5"
version: "1.9.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.3"
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.8"
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "1.1.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.2"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.6"
version: "0.4.12"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
version: "2.1.2"
sdks:
dart: ">=2.2.0 <3.0.0"
dart: ">=2.17.0 <3.0.0"

View File

@@ -1,17 +1,17 @@
name: wheel_spinner
description: A simple Flutter widget for updating a number using a pitch bender-like spinner
version: 0.0.6
author: Chen Asraf
version: 0.5.0
homepage: https://github.com/chenasraf/wheel_spinner
environment:
sdk: ">=2.1.0 <3.0.0"
sdk: '>=2.17.0 <3.0.0'
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_lints: ^2.0.1
flutter_test:
sdk: flutter