Skip to content

Instantly share code, notes, and snippets.

@roipeker
Created September 6, 2021 17:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roipeker/e58dac5ab90e7d0ec13f92d2f6f0e6b7 to your computer and use it in GitHub Desktop.
Save roipeker/e58dac5ab90e7d0ec13f92d2f6f0e6b7 to your computer and use it in GitHub Desktop.
radio slider widget sample.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: Center(
child: SizedBox(
height: 130,
width: 300,
child: RadioSlider(
innerGaps: 5,
itemW: 12,
valueCount: 20,
value: 1.4,
onChange: (v) {
print(v);
},
),
),
),
),
);
}
}
/// --- radio slider.
/// rustic implementation.
class RadioSlider extends StatefulWidget {
final double value;
final int innerGaps;
final int valueCount;
final double itemW;
final ScrollController? controller;
final ValueChanged<double>? onChange;
final Color valueLineColor;
final Color gapLineColor;
final Color backgroundLineColor;
final Color centerLineColor;
final TextStyle? textStyle;
const RadioSlider({
Key? key,
this.textStyle,
this.valueLineColor = Colors.deepPurple,
this.gapLineColor = Colors.deepPurple,
this.backgroundLineColor = Colors.deepPurple,
this.centerLineColor = Colors.black,
this.controller,
this.onChange,
this.value = 0,
this.innerGaps = 10,
this.valueCount = 10,
this.itemW = 12,
}) : super(key: key);
@override
_RadioSliderState createState() => _RadioSliderState();
}
class _RadioSliderState extends State<RadioSlider> {
int get innerGaps => widget.innerGaps;
int get valueCount => widget.valueCount;
int get totalItems => innerGaps * valueCount + 1;
double get itemW => widget.itemW;
late ScrollController _scrollController;
@override
void initState() {
_scrollController = widget.controller ?? ScrollController();
_scrollController.addListener(_onScrollChanged);
super.initState();
}
void _updateScrollValue() {
if (!_scrollController.hasClients) {
WidgetsBinding.instance!
.addPostFrameCallback((timeStamp) => _updateScrollValue());
}
var targetValue = widget.value;
var pValue = targetValue / valueCount;
var pixelValue = _scrollController.position.maxScrollExtent * pValue;
_scrollController.jumpTo(pixelValue);
}
void _onScrollChanged() {
if (widget.onChange != null) {
var px = _scrollController.position.pixels;
var gapValue = (px ~/ widget.itemW) / innerGaps;
// var percentValue = gapValue / valueCount;
widget.onChange!.call(gapValue);
}
}
void _updateController() {
_scrollController.dispose();
_scrollController = widget.controller ?? ScrollController();
_scrollController.addListener(_onScrollChanged);
}
@override
void dispose() {
_scrollController.removeListener(_onScrollChanged);
_scrollController.dispose();
super.dispose();
}
@override
void didUpdateWidget(covariant RadioSlider oldWidget) {
bool changed = false;
if (oldWidget.valueCount != widget.valueCount) {
changed = true;
}
if (oldWidget.value != widget.value) {
_updateScrollValue();
changed = true;
}
if (oldWidget.controller != widget.controller) {
_updateController();
changed = true;
}
if (changed) {
setState(() {});
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (_, constrains) {
final centerX = constrains.maxWidth / 2 - 5;
return SizedBox(
height: 100,
child: Stack(
children: [
Center(
child: DecoratedBox(
decoration: BoxDecoration(color: widget.backgroundLineColor),
child: SizedBox(width: double.infinity, height: 2),
),
),
ListView.builder(
controller: _scrollController,
padding: EdgeInsets.symmetric(horizontal: centerX),
itemBuilder: (_, idx) {
final isGap = idx % innerGaps == 0;
final gapNum = idx ~/ innerGaps;
late Widget child;
if (!isGap) {
child = Container(
width: 3,
height: 12,
decoration: BoxDecoration(
color: widget.gapLineColor,
borderRadius: BorderRadius.circular(4),
),
);
} else {
child = Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 32),
Container(
width: 4,
height: 40,
decoration: BoxDecoration(
color: widget.valueLineColor,
borderRadius: BorderRadius.circular(4)),
),
SizedBox(
height: 32,
child: OverflowBox(
child: Text('${gapNum * 1}', style: widget.textStyle),
maxWidth: 80,
alignment: Alignment.bottomCenter,
),
),
],
);
}
return SizedBox(
width: itemW,
child: Center(
child: child,
),
);
},
itemCount: totalItems,
scrollDirection: Axis.horizontal,
),
Center(
child: Container(
width: 6,
height: 48,
decoration: BoxDecoration(
color: widget.centerLineColor,
borderRadius: BorderRadius.circular(4),
),
),
),
],
),
);
},
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment