Skip to content

Instantly share code, notes, and snippets.

@Davenchy
Created November 22, 2020 21:30
Show Gist options
  • Save Davenchy/ef33a90013b9f6f16930ab86d24ed75b to your computer and use it in GitHub Desktop.
Save Davenchy/ef33a90013b9f6f16930ab86d24ed75b to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart';
class CustomRangeSlider extends StatefulWidget {
final double width;
final double height;
final double markerWidth;
final double markerHeight;
final double sliderWidth;
final int divisions;
final double start;
final double end;
final Color leftOutRangeBackground;
final Color rightOutRangeBackground;
final Color background;
final Color inRangeBackground;
final Color outRangeMarkers;
final Color inRangeMarkers;
final Color leftSliderColor;
final Color rightSliderColor;
final ValueChanged<double> onStartChange;
final ValueChanged<double> onEndChange;
CustomRangeSlider({
@required this.width,
@required this.height,
this.divisions = 10,
this.sliderWidth = 5,
this.markerWidth = 2,
this.markerHeight = 10,
this.leftOutRangeBackground: Colors.transparent,
this.rightOutRangeBackground: Colors.transparent,
this.background = Colors.white,
Color inRangeBackground,
this.outRangeMarkers = Colors.black,
this.inRangeMarkers = Colors.black,
this.leftSliderColor = Colors.red,
this.rightSliderColor = Colors.red,
@required this.start,
@required this.end,
@required this.onStartChange,
@required this.onEndChange,
}) : this.inRangeBackground = inRangeBackground ?? Colors.red.withAlpha(80);
@override
_CustomRangeSliderState createState() => _CustomRangeSliderState();
}
class _CustomRangeSliderState extends State<CustomRangeSlider> {
double start = 0, end = 0;
@override
initState() {
super.initState();
start = widget.start;
end = widget.end;
}
@override
void didUpdateWidget(covariant CustomRangeSlider oldWidget) {
super.didUpdateWidget(oldWidget);
start = widget.start;
end = widget.end;
}
@override
Widget build(BuildContext context) {
return Container(
width: widget.width,
height: widget.height,
child: Stack(
children: [
Container(
height: widget.height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: widget.background,
),
child: Stack(
children: [
..._drawMarkers(isTop: true),
..._drawMarkers(isTop: false),
Positioned(
left: start * widget.width,
child: GestureDetector(
onPanUpdate: (details) {
final d = end - start;
double np = (start + details.delta.dx / widget.width)
.clamp(0.0, 1.0);
bool c = np + d <= 1;
start = c ? np : 1 - d;
end = c ? np + d : 1;
setState(() {
widget.onStartChange(
double.parse(
start.toStringAsFixed(2),
),
);
widget.onEndChange(
double.parse(
end.toStringAsFixed(2),
),
);
});
},
child: Container(
width: widget.width * (end - start),
height: widget.height,
color: widget.inRangeBackground,
),
),
),
Positioned(
left: (start * widget.width)
.clamp(0, widget.width - widget.sliderWidth),
bottom: 0,
child: _drawSlider(widget.leftSliderColor),
),
Positioned(
left: (end * widget.width)
.clamp(0, widget.width - widget.sliderWidth),
bottom: 0,
child: _drawSlider(widget.rightSliderColor),
),
Positioned(
left: 0,
bottom: 0,
child: GestureDetector(
onPanUpdate: (details) {
double np = (start + details.delta.dx / widget.width)
.clamp(0.0, 1.0);
start = end >= np ? np : end;
setState(
() => widget.onStartChange(
double.parse(
start.toStringAsFixed(2),
),
),
);
},
child: Container(
width: start * widget.width + widget.sliderWidth,
height: widget.height,
color: widget.leftOutRangeBackground,
),
),
),
Positioned(
left: (end * widget.width)
.clamp(0, widget.width - widget.sliderWidth),
bottom: 0,
child: GestureDetector(
onPanUpdate: (details) {
double np = (end + details.delta.dx / widget.width)
.clamp(0.0, 1.0);
end = start <= np ? np : start;
setState(
() => widget.onEndChange(
double.parse(
end.toStringAsFixed(2),
),
),
);
},
child: Container(
width: ((1 - end) * widget.width)
.clamp(widget.sliderWidth, widget.width),
height: widget.height,
color: widget.rightOutRangeBackground,
),
),
),
],
),
),
],
),
);
}
double get _divisionWidth => widget.width / (widget.divisions);
List<Widget> _drawMarkers({@required bool isTop}) => List<Widget>.generate(
widget.divisions - 1,
(index) {
final pos = (index + 1) * _divisionWidth;
return Positioned(
top: isTop ? 0 : null,
bottom: isTop ? null : 0,
left: pos,
child: _drawMarker(
start <= pos && pos <= end
? widget.inRangeMarkers
: widget.outRangeMarkers,
),
);
},
);
Widget _drawMarker(Color color) => Container(
color: color,
width: widget.markerWidth,
height: widget.markerHeight,
);
Widget _drawSlider(Color color) => Container(
color: color,
width: widget.sliderWidth,
height: widget.height,
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment