From 5a40f8d37d7ce9e5d768b8d6851540aa46956a2b Mon Sep 17 00:00:00 2001 From: sthefano Date: Wed, 27 Jul 2022 21:46:58 -0300 Subject: [PATCH 1/2] feat(MultiSlider): use RawGestureDetector instead of GestureDetector to fix when slider is inside a listview --- example/lib/main.dart | 84 ++++++++++++++++------------ lib/flutter_multi_slider.dart | 7 ++- lib/src/custom_gesture_detector.dart | 72 ++++++++++++++++++++++++ lib/src/multi_slider.dart | 17 +++--- 4 files changed, 135 insertions(+), 45 deletions(-) create mode 100644 lib/src/custom_gesture_detector.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 2fb4dc4..0549dd7 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -18,6 +18,7 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { + List t = [0.1, 0.2]; List monday = [0.1, 0.2]; List tuesday = [0.1, 0.2, 0.4, 0.5]; List wednesday = [0.1, 0.2]; @@ -35,42 +36,55 @@ class _MyHomePageState extends State { appBar: AppBar( title: Text('MultiSlider'), ), - body: ListView( - children: [ - WeekDaySchedule( - weekDay: 'Monday', - values: monday, - onChanged: (value) => setState(() => monday = value), - enabled: mondayEnabled, - onToggle: (value) => setState(() => mondayEnabled = value), - ), - WeekDaySchedule( - weekDay: 'Tuesday', - values: tuesday, - onChanged: (value) => setState(() => tuesday = value), - enabled: tuesdayEnabled, - onToggle: (value) => setState(() => tuesdayEnabled = value), - ), - WeekDaySchedule( - weekDay: 'Wednesday', - values: wednesday, - onChanged: (value) => setState(() => wednesday = value), - enabled: wednesdayEnabled, - onToggle: (value) => setState(() => wednesdayEnabled = value), - ), - WeekDaySchedule( - weekDay: 'Thursday', - values: thursday, - onChanged: (value) => setState(() => thursday = value), - enabled: thursdayEnabled, - onToggle: (value) => setState(() => thursdayEnabled = value), + body: Column( + children: [ + Padding( + padding: const EdgeInsets.only(left: 50), + child: MultiSlider( + values: t, + onChanged: (value) => setState(() => t = value), + ), ), - WeekDaySchedule( - weekDay: 'Friday', - values: friday, - onChanged: (value) => setState(() => friday = value), - enabled: fridayEnabled, - onToggle: (value) => setState(() => fridayEnabled = value), + Expanded( + child: ListView( + children: [ + WeekDaySchedule( + weekDay: 'Monday', + values: monday, + onChanged: (value) => setState(() => monday = value), + enabled: mondayEnabled, + onToggle: (value) => setState(() => mondayEnabled = value), + ), + WeekDaySchedule( + weekDay: 'Tuesday', + values: tuesday, + onChanged: (value) => setState(() => tuesday = value), + enabled: tuesdayEnabled, + onToggle: (value) => setState(() => tuesdayEnabled = value), + ), + WeekDaySchedule( + weekDay: 'Wednesday', + values: wednesday, + onChanged: (value) => setState(() => wednesday = value), + enabled: wednesdayEnabled, + onToggle: (value) => setState(() => wednesdayEnabled = value), + ), + WeekDaySchedule( + weekDay: 'Thursday', + values: thursday, + onChanged: (value) => setState(() => thursday = value), + enabled: thursdayEnabled, + onToggle: (value) => setState(() => thursdayEnabled = value), + ), + WeekDaySchedule( + weekDay: 'Friday', + values: friday, + onChanged: (value) => setState(() => friday = value), + enabled: fridayEnabled, + onToggle: (value) => setState(() => fridayEnabled = value), + ), + ], + ), ), ], ), // This trailing comma makes auto-formatting nicer for build methods. diff --git a/lib/flutter_multi_slider.dart b/lib/flutter_multi_slider.dart index 53a4c92..be7accc 100644 --- a/lib/flutter_multi_slider.dart +++ b/lib/flutter_multi_slider.dart @@ -1,3 +1,8 @@ library flutter_multi_slider; -export './src/multi_slider.dart' show MultiSlider; +import 'dart:math' as math; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; + +part 'src/custom_gesture_detector.dart'; +part 'src/multi_slider.dart'; diff --git a/lib/src/custom_gesture_detector.dart b/lib/src/custom_gesture_detector.dart new file mode 100644 index 0000000..1128fb0 --- /dev/null +++ b/lib/src/custom_gesture_detector.dart @@ -0,0 +1,72 @@ +part of flutter_multi_slider; + +class _CustomGestureDetector extends StatelessWidget { + final ValueChanged? onPanStart; + final ValueChanged? onPanUpdate; + final ValueChanged? onPanEnd; + final Widget child; + + const _CustomGestureDetector({ + required this.onPanStart, + required this.onPanUpdate, + required this.onPanEnd, + required this.child, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return RawGestureDetector( + gestures: { + _CustomPanGestureRecognizer: + GestureRecognizerFactoryWithHandlers<_CustomPanGestureRecognizer>( + () => _CustomPanGestureRecognizer( + onPanStart: onPanStart, + onPanUpdate: onPanUpdate, + onPanEnd: onPanEnd, + ), + (_CustomPanGestureRecognizer instance) {}, + ), + }, + child: child, + ); + } +} + +class _CustomPanGestureRecognizer extends OneSequenceGestureRecognizer { + final ValueChanged? onPanStart; + final ValueChanged? onPanUpdate; + final ValueChanged? onPanEnd; + + _CustomPanGestureRecognizer({ + required this.onPanStart, + required this.onPanUpdate, + required this.onPanEnd, + }); + + @override + void addPointer(PointerEvent event) { + startTrackingPointer(event.pointer); + resolve(GestureDisposition.accepted); + } + + @override + void handleEvent(PointerEvent event) { + if (event is PointerDownEvent) { + onPanStart?.call(event.position); + } + if (event is PointerMoveEvent) { + onPanUpdate?.call(event.position); + } + if (event is PointerUpEvent) { + onPanEnd?.call(event.position); + stopTrackingPointer(event.pointer); + } + } + + @override + void didStopTrackingLastPointer(int pointer) {} + + @override + String get debugDescription => 'CustomPanGestureRecognizer'; +} diff --git a/lib/src/multi_slider.dart b/lib/src/multi_slider.dart index 5c65e69..c80f783 100644 --- a/lib/src/multi_slider.dart +++ b/lib/src/multi_slider.dart @@ -1,5 +1,4 @@ -import 'package:flutter/material.dart'; -import 'dart:math' as math; +part of flutter_multi_slider; /// Used in [ValueRangePainterCallback] as parameter. /// Every range between the edges of [MultiSlider] generate an [ValueRange]. @@ -111,7 +110,7 @@ class _MultiSliderState extends State { return LayoutBuilder( builder: (context, BoxConstraints constraints) { _maxWidth = constraints.maxWidth; - return GestureDetector( + return _CustomGestureDetector( child: Container( constraints: constraints, width: double.infinity, @@ -149,25 +148,25 @@ class _MultiSliderState extends State { ); } - void _handleOnChangeStart(DragStartDetails details) { + void _handleOnChangeStart(Offset details) { double valuePosition = _convertPixelPositionToValue( - details.localPosition.dx, + details.dx, ); int index = _findNearestValueIndex(valuePosition); setState(() => _selectedInputIndex = index); - final updatedValues = updateInternalValues(details.localPosition.dx); + final updatedValues = updateInternalValues(details.dx); widget.onChanged!(updatedValues); if (widget.onChangeStart != null) widget.onChangeStart!(updatedValues); } - void _handleOnChanged(DragUpdateDetails details) { - widget.onChanged!(updateInternalValues(details.localPosition.dx)); + void _handleOnChanged(Offset details) { + widget.onChanged!(updateInternalValues(details.dx)); } - void _handleOnChangeEnd(DragEndDetails details) { + void _handleOnChangeEnd(Offset details) { setState(() => _selectedInputIndex = null); if (widget.onChangeEnd != null) widget.onChangeEnd!(widget.values); From fcc487b4f59597257286a3b1e34ba352539ac277 Mon Sep 17 00:00:00 2001 From: sthefano Date: Thu, 28 Jul 2022 00:00:31 -0300 Subject: [PATCH 2/2] fix(_CustomGestureDetector): convert global offset to local offset when handling with gestures --- lib/src/custom_gesture_detector.dart | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/src/custom_gesture_detector.dart b/lib/src/custom_gesture_detector.dart index 1128fb0..cb49955 100644 --- a/lib/src/custom_gesture_detector.dart +++ b/lib/src/custom_gesture_detector.dart @@ -16,14 +16,20 @@ class _CustomGestureDetector extends StatelessWidget { @override Widget build(BuildContext context) { + ValueChanged globalToLocalWrapper(ValueChanged? callback) => + (Offset globalOffset) { + final renderBox = context.findRenderObject()! as RenderBox; + callback?.call(renderBox.globalToLocal(globalOffset)); + }; + return RawGestureDetector( gestures: { _CustomPanGestureRecognizer: GestureRecognizerFactoryWithHandlers<_CustomPanGestureRecognizer>( () => _CustomPanGestureRecognizer( - onPanStart: onPanStart, - onPanUpdate: onPanUpdate, - onPanEnd: onPanEnd, + onPanStart: globalToLocalWrapper(onPanStart), + onPanUpdate: globalToLocalWrapper(onPanUpdate), + onPanEnd: globalToLocalWrapper(onPanEnd), ), (_CustomPanGestureRecognizer instance) {}, ), @@ -34,9 +40,9 @@ class _CustomGestureDetector extends StatelessWidget { } class _CustomPanGestureRecognizer extends OneSequenceGestureRecognizer { - final ValueChanged? onPanStart; - final ValueChanged? onPanUpdate; - final ValueChanged? onPanEnd; + final ValueChanged onPanStart; + final ValueChanged onPanUpdate; + final ValueChanged onPanEnd; _CustomPanGestureRecognizer({ required this.onPanStart, @@ -53,13 +59,13 @@ class _CustomPanGestureRecognizer extends OneSequenceGestureRecognizer { @override void handleEvent(PointerEvent event) { if (event is PointerDownEvent) { - onPanStart?.call(event.position); + onPanStart.call(event.position); } if (event is PointerMoveEvent) { - onPanUpdate?.call(event.position); + onPanUpdate.call(event.position); } if (event is PointerUpEvent) { - onPanEnd?.call(event.position); + onPanEnd.call(event.position); stopTrackingPointer(event.pointer); } }