Reputation: 131
I'm trying to implement a slider for temperature, which can go from negative to positive values.
I have found many examples that have sliders that go from left to right, but I have not found one which starts from middle, and goes left (negative) and right (positive).
The attached image shows what I am trying to achieve. Is there a widget or library (I am not sure if it is called Slider) that can achieve the desired widget?
Upvotes: 5
Views: 1608
Reputation: 7465
Maybe the Slider
Widget provided by Flutter could achieve that nice behaviour you are wishing for.
With this widget you can
I wrote a simple yet complete sample for you that might provide you with a good starting point:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _restartSlider() {
setState(() {
_sliderValue=0;
});
}
double _sliderValue = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text("Slider value is: $_sliderValue",),
Slider(
value: _sliderValue,
activeColor: Colors.red,
inactiveColor: Colors.green,
thumbColor: _sliderValue == 0 ? Colors.grey : _sliderValue > 0 ? Colors.green : Colors.red,
min: -50, max: 50,
divisions: 100,
label: _sliderValue.toString(),
onChanged: (double newValue) {
setState(() {
_sliderValue = newValue;
});
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _restartSlider,
tooltip: 'Restart',
child: const Icon(Icons.restart_alt),
),
);
}
}
That code will create a slider "which starts from middle, and goes left (negative) and right (positive)". The result is like this (click the floating button to return the slider thumb to 0):
Using Stack
widget you can draw a bar that looks exactly as what you showed in your pictures.
Basically you just need to set the Slider
's bar color to transparent and draw a Container below that have the colors you want.
A complete, working example would be as follows:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _restartSlider() {
setState(() {
_sliderValue=0;
});
}
double _sliderValue = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text("Temperature is: $_sliderValue °C",),
Stack(alignment: AlignmentDirectional.center,
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Expanded(
child: Container(
height: 7,
color: _sliderValue < 0 ? Colors.red : Colors.grey,
),
),
Expanded(
child: Container(
height: 7,
color: _sliderValue > 0 ? Colors.green : Colors.grey,
),
),
],
),
),
Slider(
value: _sliderValue,
activeColor: Colors.transparent,
inactiveColor: Colors.transparent,
thumbColor: _sliderValue == 0 ? Colors.grey : _sliderValue > 0 ? Colors.green : Colors.red,
min: -50, max: 50,
divisions: 100,
label: _sliderValue.toString(),
onChanged: (double newValue) {
setState(() {
_sliderValue = newValue;
});
},
),
],
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _restartSlider,
tooltip: 'Restart',
child: const Icon(Icons.restart_alt),
),
);
}
}
Which would produce a slider as in the following pictures:
If only that part of the slider until the thumb should be coloured you can use LinearProcessIndicator instead to dinamically only colour a part of the bar. This is done as in the code below:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _restartSlider() {
setState(() {
_sliderValue=0;
});
}
double _sliderValue = 0;
double _min = -60;
double _max = 80;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text("Temperature is: $_sliderValue °C",),
Stack(alignment: AlignmentDirectional.center,
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Expanded(
child: LinearProgressIndicator(
value: 1-_sliderValue/_min,
color: Colors.grey,
backgroundColor: Colors.red,
),
flex:_min.abs().round(),
),
Expanded(
child: LinearProgressIndicator(
value: _sliderValue/_max,
color: Colors.green,
backgroundColor: Colors.grey,
),
flex:_max.abs().round(),
),
],
),
),
Slider(
value: _sliderValue,
activeColor: Colors.transparent,
inactiveColor: Colors.transparent,
thumbColor: _sliderValue == 0 ? Colors.grey : _sliderValue > 0 ? Colors.green : Colors.red,
min: _min, max: _max,
divisions: (_min.abs() + _max.abs()).round(),
label: _sliderValue.toString(),
onChanged: (double newValue) {
setState(() {
_sliderValue = newValue;
});
},
),
],
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _restartSlider,
tooltip: 'Restart',
child: const Icon(Icons.restart_alt),
),
);
}
}
which gives the following result:
Upvotes: 8
Reputation: 1
https://i.sstatic.net/0DmhB.png
You can use this:
import 'package:flutter/material.dart';
import 'package:madeup_flutter/base_class/base_widget.dart';
import 'package:sizer/sizer.dart';
import '../../constants/colors.dart';
class CustomSlider extends StatefulWidget {
double value;
final Function? onChange;
double? maxValue;
CustomSlider({Key? key, required this.value, this.onChange,
this.maxValue})
: super(key: key);
@override
State<CustomSlider> createState() => _CustomSliderState();
}
class _CustomSliderState extends BaseFullState<CustomSlider> {
@override
Widget build(BuildContext context) {
return SliderTheme(
data: SliderTheme.of(context).copyWith(
trackShape: RectangularSliderTrackShape(),
trackHeight: 15.0,
valueIndicatorColor:
widget.value < 0 ? AppColors.red : AppColors.green200,
showValueIndicator: ShowValueIndicator.always,
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 10),
overlayShape: RoundSliderOverlayShape(overlayRadius: 28.0),
valueIndicatorTextStyle: TextStyle(
color: Colors.white, letterSpacing: 2.0, fontFamily: "Roboto")),
child: Stack(
children: [
Positioned.fill(
child: Container(
margin: EdgeInsets.symmetric(vertical: 3.h, horizontal: 3.w),
decoration: BoxDecoration(
color: AppColors.grey500,
borderRadius: BorderRadius.circular(3.w)),
child: Row(
children: List.generate(20, (index) {
var color = _getColor(index);
var isCircle =
(color == AppColors.red && index == (widget.value) + 10) ||
(color == AppColors.baseAppColor &&
index == (widget.value) + 9);
return Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.horizontal(
left: Radius.circular(isCircle
? (color == AppColors.red ? 5 : 0)
: (index == 10 && color == AppColors.baseAppColor
? 5
: 0)),
right: Radius.circular(isCircle
? (color == AppColors.baseAppColor ? 5 : 0)
: (index == 10 && color == AppColors.red ? 5 :
0))),
color: color,
)),
);
}),
),
)),
Slider(
value: widget.value,
max: widget.maxValue ?? 10,
min: -10,
divisions: 20,
activeColor: Colors.transparent,
inactiveColor: Colors.transparent,
thumbColor: AppColors.grey800,
label: widget.value.round().toString() ?? "",
onChangeEnd: (newValue) {
widget.onChange?.call(newValue);
},
onChanged: (newValue) {
widget.value = newValue;
setState(() {});
},
),
],
),
);
}
_getColor(int index) {
if (widget.value > 0) {
if (index < 10) {
return AppColors.grey500;
} else if (index < (widget.value + 10)) {
return AppColors.baseAppColor;
} else {
return AppColors.grey500;
}
} else {
if (index > 10) {
return AppColors.grey500;
} else if (index < (widget.value + 10)) {
return AppColors.grey500;
} else {
return AppColors.red;
}
}
}
}
Upvotes: 0
Reputation: 100
You can use the RangeSlider widget.
Below is the example code for the range slider. If suppose you need to make the center point constant, you can use the onChanged callback method to make the center value constant.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
RangeValues _currentRangeValues = const RangeValues(0, 0);
@override
Widget build(BuildContext context) {
return RangeSlider(
values: _currentRangeValues,
max: 100,
min: -100,
divisions: 150,
labels: RangeLabels(
_currentRangeValues.start.round().toString(),
_currentRangeValues.end.round().toString(),
),
onChanged: (RangeValues values) {
setState(() {
_currentRangeValues = values;
});
},
);
}
}
Upvotes: 1