How to put numbers in top of Flutter Slider - dart

I need to make an slider, that slider works perfectly, it has its divisions, but the thing that I need to do is set numbers in each division, I mean, if the slider has 3 divisions, set numbers, 1, 2, 3, something kind of:
1 2 3
---|---|---|---
Is there a way to do that?
The code that I am using is this one:
Slider(
value: _place.value,
min: 0.0,
max: 10.0,
divisions: 10,
onChangeStart: (double value) {
print('Start value is ' + value.toString());
},
onChangeEnd: (double value) {
print('Finish value is ' + value.toString());
},
onChanged: (double value) {
if (vm.isRatingPlace) {
setState(() {
_place.value = value;
});
}
},
activeColor: HeatMapColors.getOnFireColor(_place.value),
inactiveColor: Colors.black45,
))

You could use a Column and draw the values at the top, check this example :
double value = 0.0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(10, (index) => Text("$index")),
),
),
Slider(
value: value,
min: 0.0,
max: 10.0,
divisions: 10,
onChangeStart: (double value) {
print('Start value is ' + value.toString());
},
onChangeEnd: (double value) {
print('Finish value is ' + value.toString());
},
onChanged: (double newValue) {
setState(() {
value = newValue;
});
},
activeColor: Colors.blue,
inactiveColor: Colors.black45,
),
],
));
}

Related

I want to highlight RED the timeslot that is already in the array clickedDateTime

When I click "SELECT ALL BUTTON", I want to highlight all currently shown buttons.
The desired timeslots are already in clickedDateTime
Console Log
but the buttons of timeslots are not changing color to red as I intended to
Problem:
expected output(I want it to be like this):
import 'package:flutter/material.dart';
import 'package:hoopers/widgets/textTheme.dart';
class RegistrationCalendarScheduling extends StatefulWidget {
List clickedDateTime;
int currentMonth = 0; // load current month
int currentDay = 0; // load current day
int currentAddYear = 0;
int currentAddMonth = 0;
int currentAddDay = 0;
RegistrationCalendarScheduling(this.clickedDateTime, {Key? key}) : super(key: key){
var now = DateTime.now();
currentMonth = now.month-1;
currentDay = now.day-1;
currentAddYear = now.year;
currentAddMonth = now.month;
currentAddDay = now.day;
}
List months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
List days = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31'];
//implement disabled property for buttons?
List time = [5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
//Dummy Data [API] : These are the schedules already taken.
//TO DO: Create a function that checks if the schedule is already taken
List currentClicked = [];
#override
_RegistrationCalendarSchedulingState createState() => _RegistrationCalendarSchedulingState();
}
class _RegistrationCalendarSchedulingState extends State<RegistrationCalendarScheduling> {
int dateChecker (int currentMonth) {
if (currentMonth == 1) {
return 28;
}
else if (currentMonth == 3 || currentMonth == 5 || currentMonth == 8 || currentMonth == 10 ) {
return 30;
}
else {
return 31;
}
}
String format (int item) {
if (item+1 < 10) {
return ("0"+(item+1).toString());
}
return((item+1).toString());
}
Future<void> _selectAllTime() async {
var year = widget.currentAddYear;
var month = format(widget.currentMonth);
var day = format(widget.currentDay);
for (int i = 0; i < widget.time.length; i++) {
String date = year.toString() +
"-" + month.toString() +
"-" + day.toString() +
"-" + widget.time[i].toString();
// log(date);
if (! widget.clickedDateTime.contains(date)) {
widget.clickedDateTime.add(date);
// String item;
// String clickedDateTime;
// List clickedDates;
calendarTimeButton(widget.time[i].toString(), date, widget.clickedDateTime).isClicked(widget.clickedDateTime, date);
// this.isClicked(clickedDates, date);
// RegistrationCalendarScheduling(const []).clickedDateTime!.add(date);
} else {
widget.clickedDateTime.remove(date);
}
}
// setState(() {
// calendarTimeButton(widget.time.toString(),
// '',
// widget.clickedDateTime!,
// widget.currentYear,
// widget.currentAddMonth,
// widget.currentAddDay,
// ).addAllTime(widget.clickedDateTime!);
// });
}
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.fromLTRB(10, 30, 10, 10),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: const Icon(Icons.arrow_back_ios_sharp, color: Colors.red),
onPressed:(){
widget.currentMonth == 0? widget.currentMonth = 0 : setState((){
widget.currentMonth -=1;
widget.currentDay = 0;
});
widget.currentAddMonth == 0? widget.currentAddMonth = 0 : setState((){
widget.currentAddMonth -=1;
widget.currentDay = 0;
});
},
),
textTheme(widget.months[widget.currentMonth], 25, Colors.red, 'ZingRust'),
IconButton(
icon: const Icon(Icons.arrow_forward_ios_sharp, color: Colors.red),
onPressed:(){
widget.currentMonth == 11? widget.currentMonth = 11 : setState((){
widget.currentMonth +=1;
widget.currentDay = 0;
});
widget.currentAddMonth == 11? widget.currentAddMonth = 11 : setState((){
widget.currentAddMonth +=1;
widget.currentDay = 0;
});
},
)
],
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: widget.days.sublist(0, dateChecker(widget.currentMonth)).map((item) =>
widget.currentDay == widget.days.indexOf(item) ? InkWell(
onTap: (){},
child: Container(
height: MediaQuery.of(context).size.height * 0.05,
padding: const EdgeInsets.fromLTRB(15, 0, 10, 10),
child: textTheme(item, 17, Colors.red, 'PoppinsRegular')
)
) : InkWell(
onTap: (){
setState(() {
widget.currentDay = widget.days.indexOf(item);
widget.currentAddDay = widget.days.indexOf(item);
});
},
child: Container(
height: MediaQuery.of(context).size.height * 0.05,
padding: const EdgeInsets.fromLTRB(15, 0, 10, 10),
child: textTheme(item, 17, Colors.black, 'PoppinsRegular')
)
)
).toList(),
),
),
Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(0, 5, 0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: widget.time.sublist(0,4).map((item) => calendarTimeButton(item.toString(), "2022-"+ format(widget.currentMonth) + "-"+format(widget.currentDay)+"-"+item.toString(), widget.clickedDateTime)).toList(),
),
),
Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: widget.time.sublist(4,8).map((item) => calendarTimeButton(item.toString(), "2022-"+format(widget.currentMonth)+ "-"+format(widget.currentDay)+"-"+item.toString(), widget.clickedDateTime)).toList(),
),
),
Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: widget.time.sublist(8,12).map((item) => calendarTimeButton(item.toString(), "2022-"+format(widget.currentMonth)+"-"+format(widget.currentDay)+"-"+ item.toString(), widget.clickedDateTime)).toList(),
),
),
Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: widget.time.sublist(12,16).map((item) => calendarTimeButton(item.toString(),"2022-"+format(widget.currentMonth)+"-"+format(widget.currentDay)+"-"+ item.toString(), widget.clickedDateTime)).toList(),
),
),
Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
child: MaterialButton(
height: MediaQuery.of(context).size.height * 0.03,
minWidth: double.infinity,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18)),
onPressed: _selectAllTime,
child: const Text(
"Select All",
style: TextStyle(
fontSize: 20,
fontFamily: 'ZingRust',
color: Colors.white,
),
),
color: Colors.red),
),
],
),
);
}
}
//pass a function that would get the current time, day, and month when button is clicked.
class calendarTimeButton extends StatefulWidget {
String item;
String clickedDateTime;
List clickedDates;
calendarTimeButton(this.item,this.clickedDateTime, this.clickedDates, {Key? key}) : super(key: key);
bool isAvailable(){
// for(int i=0; i<availableDates.length; i++){
// if(availableDates[i]['sched'] == clickedDateTime && availableDates[i]['availability'] == "-1"){
return true;
// }
// }
// return false;
}
bool isClicked (List clickedDates, String newClickedDate) {
return clickedDates.contains(newClickedDate);
}
bool check (List clickedDates, String newClickedDate) {
return clickedDates.contains(newClickedDate);
}
void deleteClickedDate(List clickedDates, String newClickedDate){
if(clickedDates.contains(newClickedDate)){
clickedDates.remove(newClickedDate);
}
}
void addClickedDate(List clickedDates, String newClickedDate){
clickedDates.add(newClickedDate);
}
#override
State<calendarTimeButton> createState() => _calendarTimeButtonState();
}
class _calendarTimeButtonState extends State<calendarTimeButton> {
#override
Widget build(BuildContext context) {
return SizedBox(
height: MediaQuery.of(context).size.height * 0.05,
width: MediaQuery.of(context).size.width * 0.19,
child: InkWell(
onTap: () {
setState(() {
if (widget.isAvailable()) {
widget.isClicked(widget.clickedDates, widget.clickedDateTime)
? widget.deleteClickedDate(
widget.clickedDates, widget.clickedDateTime)
: widget.addClickedDate(
widget.clickedDates, widget.clickedDateTime);
}
});
},
child: Card(
// widget.clickedDates, widget.clickedDateTime
color: widget.isAvailable()
? (widget.clickedDates.contains(widget.clickedDateTime)
? Colors.red
: Colors.white)
: Colors.grey[500],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
child: Center(
child: textTheme(
widget.item + ":00", 13, Colors.black, 'PoppinsRegular')),
elevation: 5,
),
),
);
}
}
I feel so embarassed now. I just change line 200 to
onPressed: () {
setState(() {
_selectAllTime();
});
},

Trouble with flutter radio button

I just want to have normal radio buttons where only one button in the group can be selected. According to tutorials online we need to set the groupvalue variable to the value in the setState method in onChanged. If i do this i can select all buttons and i dont want this to happen. I want only one button to be a selected at a time(from that group). if there is anything wrong with my code or any other way to do it let me know.
option is the string parameter for title optionvalue is the value for that option.
class Option extends StatefulWidget {
final String option;
final int optionvalue;
Option(this.option, this.optionvalue);
_OptionState createState() => _OptionState();
}
class _OptionState extends State<Option> {
int groupvalue;
#override
Widget build(BuildContext context) {
return Container(
child: Container(
child: RadioListTile(
title: Text(
widget.option,
style: TextStyle(fontSize: 20.0),
),
activeColor: Colors.black,
value: widget.optionvalue,
groupValue: groupvalue,
onChanged: (int a) {
print(a);
setState(() {
groupvalue = a;
});
},
),
),
);
}
}
Try this code
int _groupValue = -1;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
_myRadioButton(
title: "Checkbox 0",
value: 0,
onChanged: (newValue) => setState(() => _groupValue = newValue),
),
_myRadioButton(
title: "Checkbox 1",
value: 1,
onChanged: (newValue) => setState(() => _groupValue = newValue),
),
],
);
}
Widget _myRadioButton({String title, int value, Function onChanged}) {
return RadioListTile(
value: value,
groupValue: _groupValue,
onChanged: onChanged,
title: Text(title),
);
}
Output:
Try this for horizontal radio button list:
int _groupValue = -1;
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text('Gender', style: TextStyle(fontSize: 18)),
Container(
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
flex: 1,
child: RadioListTile(
value: 0,
groupValue: _groupValue,
title: Text("Male"),
onChanged: (newValue) =>
setState(() => _groupValue = newValue),
activeColor: Colors.red,
selected: false,
),
),
Expanded(
flex: 1,
child: RadioListTile(
value: 1,
groupValue: _groupValue,
title: Text("Female"),
onChanged: (newValue) =>
setState(() => _groupValue = newValue),
activeColor: Colors.red,
selected: false,
),
),
],
),
],
),
),
],
),
),

How to show a menu at press/finger/mouse/cursor position in flutter

I have this piece of code which I got from Style clipboard in flutter
showMenu(
context: context,
// TODO: Position dynamically based on cursor or textfield
position: RelativeRect.fromLTRB(0.0, 600.0, 300.0, 0.0),
items: [
PopupMenuItem(
child: Row(
children: <Widget>[
// TODO: Dynamic items / handle click
PopupMenuItem(
child: Text(
"Paste",
style: Theme.of(context)
.textTheme
.body2
.copyWith(color: Colors.red),
),
),
PopupMenuItem(
child: Text("Select All"),
),
],
),
),
],
);
This code works great, except that the popup that is created is at a fixed position, how would I make it so that it pops up at the mouse/press/finger/cursor position or somewhere near that, kind of like when you want to copy and paste on your phone. (This dialog popup will not be used for copy and pasting)
I was able to solve a similar issue by using this answer:
https://stackoverflow.com/a/54714628/559525
Basically, I added a GestureDetector() around each ListTile and then you use onTapDown to store your press location and onLongPress to call your showMenu function. Here are the critical functions I added:
_showPopupMenu() async {
final RenderBox overlay = Overlay.of(context).context.findRenderObject();
await showMenu(
context: context,
position: RelativeRect.fromRect(
_tapPosition & Size(40, 40), // smaller rect, the touch area
Offset.zero & overlay.size // Bigger rect, the entire screen
),
items: [
PopupMenuItem(
child: Text("Show Usage"),
),
PopupMenuItem(
child: Text("Delete"),
),
],
elevation: 8.0,
);
}
void _storePosition(TapDownDetails details) {
_tapPosition = details.globalPosition;
}
}
And then here is the full code (you'll have to tweak a few things like the image, and filling in the list of devices):
import 'package:flutter/material.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'dart:core';
class RecentsPage extends StatefulWidget {
RecentsPage({Key key, this.title}) : super(key: key);
final String title;
#override
_RecentsPageState createState() => _RecentsPageState();
}
class _RecentsPageState extends State<RecentsPage> {
List<String> _recents;
var _tapPosition;
#override
void initState() {
super.initState();
_tapPosition = Offset(0.0, 0.0);
getRecents().then((value) {
setState(() {
_recents = value;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFFFFFFF),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(height: 25),
Stack(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 40),
child: Center(
child: AutoSizeText(
"Recents",
maxLines: 1,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 32),
),
),
),
Container(
padding: EdgeInsets.only(left: 30, top: 0),
child: GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Transform.scale(
scale: 2.0,
child: Icon(
Icons.chevron_left,
),
)),
),
],
),
Container(
height: 15,
),
Container(
height: 2,
color: Colors.blue,
),
Container(
height: 10,
),
Flexible(
child: ListView(
padding: EdgeInsets.all(15.0),
children: ListTile.divideTiles(
context: context,
tiles: _getRecentTiles(),
).toList(),
),
),
Container(height: 15),
],
),
),
),
);
}
List<Widget> _getRecentTiles() {
List<Widget> devices = List<Widget>();
String _dev;
String _owner = "John Doe";
if (_recents != null) {
for (_dev in _recents.reversed) {
if (_dev != null) {
_dev = _dev.toUpperCase().trim();
String serial = "12341234";
devices.add(GestureDetector(
onTapDown: _storePosition,
onLongPress: () {
print("long press of $serial");
_showPopupMenu();
},
child: ListTile(
contentPadding: EdgeInsets.symmetric(vertical: 20),
leading: Transform.scale(
scale: 0.8,
child: Image(
image: _myImage,
)),
title: AutoSizeText(
"$_owner",
maxLines: 1,
style: TextStyle(fontSize: 22),
),
subtitle: Text("Serial #: $serial"),
trailing: Icon(Icons.keyboard_arrow_right),
)));
}
}
} else {
devices.add(ListTile(
contentPadding: EdgeInsets.symmetric(vertical: 20),
title: AutoSizeText(
"No Recent Devices",
maxLines: 1,
style: TextStyle(fontSize: 20),
),
subtitle:
Text("Click the button to add a device"),
onTap: () {
print('add device');
},
));
}
return devices;
}
_showPopupMenu() async {
final RenderBox overlay = Overlay.of(context).context.findRenderObject();
await showMenu(
context: context,
position: RelativeRect.fromRect(
_tapPosition & Size(40, 40), // smaller rect, the touch area
Offset.zero & overlay.size // Bigger rect, the entire screen
),
items: [
PopupMenuItem(
child: Text("Show Usage"),
),
PopupMenuItem(
child: Text("Delete"),
),
],
elevation: 8.0,
);
}
void _storePosition(TapDownDetails details) {
_tapPosition = details.globalPosition;
}
}
Use gesture detector's onTapDown like this
GestureDetector(
onTapDown: (TapDownDetails details) {
showPopUpMenu(details.globalPosition);
},
then in this method we use tap down details to find position
Future<void> showPopUpMenu(Offset globalPosition) async {
double left = globalPosition.dx;
double top = globalPosition.dy;
await showMenu(
color: Colors.white,
//add your color
context: context,
position: RelativeRect.fromLTRB(left, top, 0, 0),
items: [
PopupMenuItem(
value: 1,
child: Padding(
padding: const EdgeInsets.only(left: 0, right: 40),
child: Row(
children: [
Icon(Icons.mail_outline),
SizedBox(
width: 10,
),
Text(
"Menu 1",
style: TextStyle(color: Colors.black),
),
],
),
),
),
PopupMenuItem(
value: 2,
child: Padding(
padding: const EdgeInsets.only(left: 0, right: 40),
child: Row(
children: [
Icon(Icons.vpn_key),
SizedBox(
width: 10,
),
Text(
"Menu 2",
style: TextStyle(color: Colors.black),
),
],
),
),
),
PopupMenuItem(
value: 3,
child: Row(
children: [
Icon(Icons.power_settings_new_sharp),
SizedBox(
width: 10,
),
Text(
"Menu 3",
style: TextStyle(color: Colors.black),
),
],
),
),
],
elevation: 8.0,
).then((value) {
print(value);
if (value == 1) {
//do your task here for menu 1
}
if (value == 2) {
//do your task here for menu 2
}
if (value == 3) {
//do your task here for menu 3
}
});
hope it works
Here is a reusable widget which does what you need. Just wrap your Text or other Widget with this CopyableWidget and pass in the onGetCopyTextRequested. It will display the Copy menu when the widget is long pressed, copy the text contents returned to the clipboard, and display a Snackbar on completion.
/// The text to copy to the clipboard should be returned or null if nothing can be copied
typedef GetCopyTextCallback = String Function();
class CopyableWidget extends StatefulWidget {
final Widget child;
final GetCopyTextCallback onGetCopyTextRequested;
const CopyableWidget({
Key key,
#required this.child,
#required this.onGetCopyTextRequested,
}) : super(key: key);
#override
_CopyableWidgetState createState() => _CopyableWidgetState();
}
class _CopyableWidgetState extends State<CopyableWidget> {
Offset _longPressStartPos;
#override
Widget build(BuildContext context) {
return InkWell(
highlightColor: Colors.transparent,
onTapDown: _onTapDown,
onLongPress: () => _onLongPress(context),
child: widget.child
);
}
void _onTapDown(TapDownDetails details) {
setState(() {
_longPressStartPos = details?.globalPosition;
});
}
void _onLongPress(BuildContext context) async {
if (_longPressStartPos == null)
return;
var isCopyPressed = await showCopyMenu(
context: context,
pressedPosition: _longPressStartPos
);
if (isCopyPressed == true && widget.onGetCopyTextRequested != null) {
var copyText = widget.onGetCopyTextRequested();
if (copyText != null) {
await Clipboard.setData(ClipboardData(text: copyText));
_showSuccessSnackbar(
context: context,
text: "Copied to the clipboard"
);
}
}
}
void _showSuccessSnackbar({
#required BuildContext context,
#required String text
}) {
var scaffold = Scaffold.of(context, nullOk: true);
if (scaffold != null) {
scaffold.showSnackBar(
SnackBar(
content: Row(
children: <Widget>[
Icon(
Icons.check_circle_outline,
size: 24,
),
SizedBox(width: 8),
Expanded(
child: Text(text)
)
],
)
)
);
}
}
}
Future<bool> showCopyMenu({
BuildContext context,
Offset pressedPosition
}) {
var x = pressedPosition.dx;
var y = pressedPosition.dy;
return showMenu<bool>(
context: context,
position: RelativeRect.fromLTRB(x, y, x + 1, y + 1),
items: [
PopupMenuItem<bool>(value: true, child: Text("Copy")),
]
);
}

Flutter: AnimatedOpacity Checkbox why I can still clik it even it is not visible

I was wondering why I can still able to click or tap the checkbox even when it is not visible. It still gives print values in my console.
Container(
child: Row(
children: <Widget>[
AnimatedOpacity(
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Checkbox(
value: this.folder[index].checked,
onChanged: (bool value) {
print(value);
setState(() {
this.folder[index].checked = value;
});
},
),
)
],
),
)
Even if your Widget has opacity it has user interaction, so to solve your problem you have two options:
Add a condition inside onChanged method
Container(
child: Row(
children: <Widget>[
AnimatedOpacity(
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Checkbox(
value: this.folder[index].checked,
onChanged: (bool value) {
if (_visible) {
print(value);
setState(() {});
}
},
),
)
],
),
)
Or add a condition to display an empty Widget if the _visible variable is false
Container(
child: Row(
children: <Widget>[
AnimatedOpacity(
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: _visible
? Checkbox(
value: this.folder[index].checked,
onChanged: (bool value) {
if (_visible) {
print(value);
setState(() {});
}
},
)
: SizedBox(),
)
],
),
)

How can I remove internal padding on a RadioListTile so I can use 3 RadioListTiles in a row?

I am pretty new to Flutter and Dart and I can't seem to find any hints for this particular topic. I am trying to put 3 RadioListTiles in a Row like so:
Row(
children: [
Expanded(
child:RadioListTile<GoalSelection>(
title: Text(
'Net',
style: Theme.of(context).textTheme.body1,
),
value: GoalSelection.net,
groupValue: _goalSelection,
onChanged: (GoalSelection value) {
setState(() {
_goalSelection = value;
});
},
),
),
Expanded(
child: RadioListTile<GoalSelection>(
title: Text(
'Gross',
style: Theme.of(context).textTheme.body1,
),
value: GoalSelection.gross,
groupValue: _goalSelection,
onChanged: (GoalSelection value) {
setState(() {
_goalSelection = value;
});
},
),
),
Expanded(
child: RadioListTile<GoalSelection>(
title: Text(
'Salary',
style: Theme.of(context).textTheme.body1,
),
value: GoalSelection.salary,
groupValue: _goalSelection,
onChanged: (GoalSelection value) {
setState(() {
_goalSelection = value;
});
},
),
),
],
),
The buttons layout fine, but there seems to be a lot of wasted space for the label. I put a screenshot of what it currently looks like below. I have tried wrapping the Expanded, the RadioListTile, and the Text in Padding widgets (all one at a time) to manually set the padding to 0, but it didn't do anything. I have also tried to change Expanded to Flexible even though I didn't think that would change anything. I am at a loss now. Is there any way to get this layout to work? I am kind of assuming it is something really dumb that I am doing.
You can use Radio + text widget instead of RadioListTile. For removing internal padding in Radio widget set:
Radio(
visualDensity: const VisualDensity(
horizontal: VisualDensity.minimumDensity,
vertical: VisualDensity.minimumDensity),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
.....
),
You can use a Radio and Text widget in a row. But the Radio also has the same padding problem. To remove the padding you can put the Radio as a child of a SizedBox.
eg:- SizedBox(height: 20, width: 20, child: Radio(.......))
RadioListTile is used with the purpose of taking the full width in a vertical scroll list.
If you don't want this behavior, don't use it. Use Radio instead.
just set contentPadding: EdgeInsets.zero
RadioListTile(contentPadding: EdgeInsets.zero)
We can control the padding of the RadioListTile using Flexible widget. As you want to arrange 3 RadioListTiles inside a Row Widget. Please try with the below code, it will work.
Row(
children: <Widget>[
Flexible(
fit: FlexFit.loose,
child:
RadioListTile(
title: const Text('hello'),
onChanged: (value) {
setState(() {});
},
),
),
Flexible(
fit: FlexFit.loose,
child:
RadioListTile(
title: const Text('Lafayette'),
onChanged: (value) {
setState(() {});
},
),
)
],
),
Do, let me know. Once you tried with the above code. If it resolved you problem, please accept my answer as useful and provide your valuable comments.
I got the same problem. You could try to customize with Radio, Text, InkWell, Padding.
class LabeledRadio extends StatelessWidget {
const LabeledRadio({
this.label,
this.padding,
this.groupValue,
this.value,
this.onChanged,
});
final String label;
final EdgeInsets padding;
final bool groupValue;
final bool value;
final Function onChanged;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
if (value != groupValue)
onChanged(value);
},
child: Padding(
padding: padding,
child: Row(
children: <Widget>[
Radio<bool>(
groupValue: groupValue,
value: value,
onChanged: (bool newValue) {
onChanged(newValue);
},
),
Text(label),
],
),
),
);
}
}
// ...
bool _isRadioSelected = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <LabeledRadio>[
LabeledRadio(
label: 'This is the first label text',
padding: const EdgeInsets.symmetric(horizontal: 5.0),
value: true,
groupValue: _isRadioSelected,
onChanged: (bool newValue) {
setState(() {
_isRadioSelected = newValue;
});
},
),
LabeledRadio(
label: 'This is the second label text',
padding: const EdgeInsets.symmetric(horizontal: 5.0),
value: false,
groupValue: _isRadioSelected,
onChanged: (bool newValue) {
setState(() {
_isRadioSelected = newValue;
});
},
),
],
),
);
}
The documentation: https://api.flutter.dev/flutter/material/RadioListTile-class.html#material.RadioListTile.3
This is how I fix the padding:
enum ContactSex { nam, nu, khac }
class CreateContactScreen extends StatefulWidget {
static const routeName = './create_contact';
#override
_CreateContactScreenState createState() => _CreateContactScreenState();
}
class _CreateContactScreenState extends State<CreateContactScreen> {
ContactSex _contaxtSex = ContactSex.nu;
final _form = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'TẠO LIÊN HỆ',
style: kHeaderTextStyle,
),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('XONG', style: TextStyle(color: Colors.white),),
)
],
),
body: Container(
padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
child: Form(
key: _form,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(
labelText: 'Tên*',
),
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) {
// TODO: when submit this text field
},
validator: (value) {
if (value.isEmpty) {
return 'Hãy nhập tên cho liên hệ.';
}
return null;
},
onSaved: (value) {
// TODO : when save the whole form
},
),
TextFormField(
decoration: InputDecoration(
labelText: 'Họ',
),
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) {
// TODO: when submit this text field
},
// validator: (value) {
// if (value.isEmpty) {
// return null;
// }
// return null;
// },
onSaved: (value) {
// TODO : when save the whole form
},
),
TextFormField(
decoration: InputDecoration(
labelText: 'Số điện thoại*',
),
keyboardType: TextInputType.phone,
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) {
// TODO: when submit this text field
},
validator: (value) {
if (value.isEmpty) {
return 'Hãy nhập số điện thoại cho liên hệ.';
}
return null;
},
onSaved: (value) {
// TODO : when save the whole form
},
),
TextFormField(
decoration: InputDecoration(
labelText: 'Email',
),
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) {
// TODO: when submit this text field
},
// validator: (value) {
// if (value.isEmpty) {
// return null;
// }
// return null;
// },
onSaved: (value) {
// TODO : when save the whole form
},
),
Container(
padding: EdgeInsets.only(top: 15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Giới tính',
style: TextStyle(fontSize: 14.0),
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
LabeledRadio(
label: 'Nữ',
padding: EdgeInsets.all(0),
groupValue: _contaxtSex,
value: ContactSex.nu,
onChanged: (ContactSex newValue) {
setState(() {
_contaxtSex = newValue;
});
},
),LabeledRadio(
label: 'Nam',
padding: EdgeInsets.all(0),
groupValue: _contaxtSex,
value: ContactSex.nam,
onChanged: (ContactSex newValue) {
setState(() {
_contaxtSex = newValue;
});
},
),LabeledRadio(
label: 'Khác',
padding: EdgeInsets.all(0),
groupValue: _contaxtSex,
value: ContactSex.khac,
onChanged: (ContactSex newValue) {
setState(() {
_contaxtSex = newValue;
});
},
),
],
),
],
),
)
],
)),
),
),
);
}
}
class LabeledRadio extends StatelessWidget {
final String label;
final EdgeInsets padding;
final ContactSex groupValue;
final ContactSex value;
final Function onChanged;
const LabeledRadio(
{this.label, this.padding, this.groupValue, this.value, this.onChanged});
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
if (value != groupValue) {
onChanged(value);
}
},
child: Padding(
padding: padding,
child: Row(
children: <Widget>[
Radio<ContactSex>(
groupValue: groupValue,
value: value,
onChanged: (ContactSex newValue) {
onChanged(newValue);
},
),
Text(label),
],
),
),
);
}
}
You just need to set the "dense" property to true, example:
RadioListTile<String>(
title: "My radio",
dense: true, // <= here it is !
value: '1',
);
you should achieve this manually like
make a group of Radio() and Text() and wrap with InkWell() for state handling. now remove extra space of radio by materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, That's it. Get idea by sample code.
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
setState(() {
_radioVenue = 0;
});
},
child: Row(
children: [
Radio(
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
activeColor: primaryColor,
groupValue: _radioVenue,
onChanged: (value) {},
value: 0,
),
Text('From our list')
],
),
),
InkWell(
onTap: () {
setState(() {
_radioVenue = 1;
});
},
child: Row(
children: [
Radio(
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
activeColor: primaryColor,
groupValue: _radioVenue,
onChanged: (value) {},
value: 1,
),
Text('From our list')
],
),
),
],
),
We covered both both the issues in this sample.
Removed extra spaces.
whole group is selectable radio + text, Now it behaves like RadioListTile().
Simply use RadioListTile and remove extra padding, by default it's 18
RadioListTile(contentPadding: EdgeInsets.symmetric(horizontal: 0.0)),
OR
RadioListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
value: null,
groupValue: null,
onChanged: null,
),
glad to answer
I was looking for same question and ended up on Flutter Documentation
I was working on Column and RadioListTile and I faced same issue, there's a horizontal padding between content inside RadioListTile
So, here it's the answer
Looking for this documentation ! RadioListTile content padding
Just add contentPadding: EdgeInsets.symmetric(horizontal: 0) and here you go, there's no horizontal padding anymore
Just copy paste this code and enjoy
Container(
height:35,
child: Row(
children: [
Radio(
groupValue: data.selected,
value: e,
onChanged: (DataBindModel? value) {
listener.value = MultiChoiceData(selected: value, items: listener.value.items);
onChanged(value);
onSelected(value);
},
),
Text(
e.value,
style: body14,
)
],
),
)
Copy the RadioListTile code and create your on new new file and paste it in there.
Remove the imports causing errors:
import 'package:flutter/widgets.dart'; // leave it
import 'package:flutter/material.dart'; //add
import 'list_tile.dart'; //remove
import 'radio.dart'; //remove
import 'theme.dart'; //remove
import 'theme_data.dart'; //remove
Then add the following padding to it, like this:
//Inside the file locate this widget and Add the padding or remove it. I needed to remove it and add 5.
return MergeSemantics(
child: ListTileTheme.merge(
contentPadding: EdgeInsets.only( // Add this
left: 5,
right: 0,
bottom: 0,
top: 0
),
selectedColor: activeColor ?? Theme.of(context).accentColor,
child: ListTile(
leading: leading,
title: title,
subtitle: subtitle,
trailing: trailing,
isThreeLine: isThreeLine,
dense: dense,
enabled: onChanged != null,
onTap: onChanged != null && !checked ? () { onChanged(value); } : null,
selected: selected,
),
),
);
then Import the file into your project like this:
import 'package:Project_Name/common/customComponets/custom_radio_list_tile.dart' as CustomRadioListTile;
Then use it like this:
CustomRadioListTile.RadioListTile(); // and that's how I managed to do it. Thought I should share.
This is my way of reducing the space. I have three Radio in one row.
Row(
children: [
Expanded(
child: RadioListTile(
contentPadding: EdgeInsets.all(0.0),
value: DayoffType.Range,
groupValue: _dayoffType,
title: Transform.translate(offset: const Offset(-18, 0), child: Text('Range')),
onChanged: (DayoffType? val) {
setState(() {
_dayoffType = val!;
});
},
),
),
Expanded(...Radio2...),
Expanded(...Radio3...)
)

Resources