How can I apply the onSaved method on my dropdownlist - dart

I'm new on flutter, and would like to know how can I apply the onSaved method on my drpdownList.
Please Help!
Row(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width*.3,
child: Text('Départ:', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
),
Container(
width: MediaQuery.of(context).size.width*.5,
child: _fieldDropDown(_citiesdept, 0, 'depart'),
// onSaved: (value) => _citiesdept = value,
)
],
),

Will this solve your question?
if you want a callback to your widget you need to:
change
_fieldDropDown(List theList, int resultPosition, var dbField) {
// to
_fieldDropDown(List theList, int resultPosition, var dbField, void Function(dynamic newValue) callback) {
make the call when required in the dropDown...
setState(() {
this.resultsList[resultPosition] = newValue;
// HERE is the callback:
callback(newValue);
state.didChange(newValue);
print(
'The List result = ' + this.resultsList[resultPosition]);
//write newValue to a database field, which can be used in the override init to set the field originally
});
and use it as you wish, for example ...
Container(
width: MediaQuery.of(context).size.width*.5,
child: _fieldDropDown(_citiesdept, 0, 'depart'),
callback: (value) => print(value),

Related

How to have four of the same custom widget have text change individually?

I am looking to create a grid with 4 custom widgets that can either add or subtract from a given starting number. See image for reference.
For example, if you press player one, the number would increase or decrease to 100 or 99. But the other 3 players would remain the same.
I had originally used one stateful widget with a separate function for each player, but I am sure there's a way to do it in a more modular way.
class CommanderDamage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return CommanderDamageState();
}
}
class CommanderDamageState extends State<CommanderDamage> {
int damage = 0;
void update() {
setState(() {
damage++;
});
}
#override
Widget build(context) {
return MaterialApp(
home: Scaffold(
body: GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemCount: 4,
itemBuilder: (BuildContext context, index) {
return Container(
child: Column(
children: <Widget>[
Text("Player " + index.toString()),
InkWell(
onTap: update,
child: Container(
width: 100.0,
height: 100.0,
child: Text(damage),
)
],
),
);
},
),
),
);
}
}
EDIT: I have edited my code to reflect my current. Currently, when the damage area is pressed, the damage increases for all 4 players instead of the one I am pressing.
Wrap your text widget inside InkWell(). Basically what InkWell does is creates a rectangular touch responsive area.
InkWell(
child: Text(
'Player One',
style: TextStyle(
fontSize: 20, color: Colors.white),
onTap: () {
// Your function
}
)
But this make the interactive tap area according to size of the text which is very small, so it's better to wrap it inside a container and provide height-width or some space with padding
InkWell(
child: Container(
width: 100,
height: 100,
child: Text(
'Player One',
style: TextStyle(
fontSize: 20, color: Colors.white), ),
onTap: () {
// Your function
}
)
An inside onTap you can your function and perform changes.
Read more about InkWell:
https://docs.flutter.io/flutter/material/InkWell-class.html
After lots of trial and error I managed to find an answer.
I had to set the state within the onTap instead of making a separate function and calling it in the onTap.
class CommanderDamage extends StatefulWidget {
int damage = 0;
CommanderDamage({this.damage, Key key});
#override
State<StatefulWidget> createState() {
return CommanderDamageState();
}
}
class CommanderDamageState extends State<CommanderDamage> {
var damage = [0, 0, 0, 0, 0, 0];
#override
Widget build(context) {
return MaterialApp(
home: Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft, end: Alignment.bottomRight,
colors: [Color(0xfff6921e), Color(0xffee4036)],
),
),
child: GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemCount: damage.length,
itemBuilder: (BuildContext context, index) {
return Container(
child: Column(
children: <Widget>[
InkWell(
onTap: () {
setState(() {
damage[index]++;
});
},
onLongPress: () {
setState(() {
damage[index] = 0;
});
},
child: Container(
width: 100.0,
height: 100.0,
child: Text(damage[index].toString()),
),
),
],
),
);
},
),
),
],
),
),
);
}
}

flutter to enable and disable various Check_List_Tile using one boolean variable

I am new to flutter. in my project, there is a various check_list_tile depending upon the length of the List (attendance list). And I have used one Boolean variable. Now when I press on one checkbox it automatically checks all other checkboxes. Please help me in this (on tap one checkbox should not change the state of all other checkboxes except clicked). I have copied all code please check check_box_list field.
import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
import 'dart:async';
Map map_student_data;
Iterable iter_student_data,iter_student_key;
List list_student_data,list_student_key;
bool t=true,checkbox=false;
List list;
String validation="yes";
int i;
int year;
final FirebaseDatabase database = FirebaseDatabase.instance;
class IImca_attendence extends StatefulWidget {
#override
_IImca_attendenceState createState() => _IImca_attendenceState();
}
class _IImca_attendenceState extends State<IImca_attendence> {
#override
void initState(){
this.check_year();
super.initState();
}
DateTime date = DateTime.now();
Future check_year()async{
var k= await database.reference().child("NITTE/CLASS/MCA").once().then((DataSnapshot snapshot){
Map sea= snapshot.value;
Iterable iter=sea.keys;
list=iter.toList();
list.sublist(list.length-1);
list.sort();
setState(() {
year=list.length-2;
});
check();
});
}
Future check()async{
var m=await database.reference().child("NITTE/CLASS/MCA/${list[year].toString().toUpperCase()}/STUDENT").once().then((DataSnapshot currentyear){
map_student_data=currentyear.value;
iter_student_data=map_student_data.values;
iter_student_key=map_student_data.keys;
list_student_data=iter_student_data.toList();
list_student_key=iter_student_key.toList();
for(i=0;i<=list_student_data.length;i++){
bool ss=true;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("II-MCA"),
centerTitle: true,
actions: <Widget>[
IconButton(icon: Icon(Icons.refresh),onPressed: (){
setState(() {
check_year();
});
})
],
),
body:
validation=="1"?
new Center(
child: Text("STUDENT DOSE NOT EXIST IN $year",style: TextStyle(color: Colors.grey,fontWeight: FontWeight.bold,fontSize: 20),),
):
new ListView.builder(
itemCount: list_student_data==null?0
:list_student_data.length,
itemBuilder: (BuildContext context,int index){
var student_detail= ['NAME : ${list_student_data[index]['NAME']}','GENDER : ${list_student_data[index]['CURRENT CLASS']}','PHOTO : ${list_student_data[index]['PHOTO']}'];
return new Container(
child: new Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Card(
child: new Container(
child: CheckboxListTile(
title: Text("${list_student_key[index]}",style:TextStyle(fontWeight: FontWeight.bold,),),
subtitle: Text("NAME : ${list_student_data[index]['NAME']}"),
value: checkbox,
onChanged: (val){
setState(() {
checkbox=val;
if(checkbox==true){
print("${list_student_data[index]['NAME']}: i am absent");
}if(checkbox==false){
print("${list_student_data[index]['NAME']}: i am present");
}
});
},
),
padding: EdgeInsets.all(5),
),
)
],
),
),
);
}
)
);
}
}
Well... you are using a global checkbox variable, so It's quite normal that if you change it, all widget depending on its state will change accordingly.
What I suggest you to do is to add the selected state inside your model class. Just as an example, assumed you have this Student class (I know you are using firebase, but for sake of time I don't)
class Student {
var name = 'foo';
var year = '2018';
var selected = false;
Student(this.name);
}
This class has is selected state inside of it.
Now assume that your snapshot give you 3 students. Always for sake of time I've embedded a local array:
class _IImca_attendenceState extends State<IImca_attendence> {
var _students = [Student('foo'), Student('pub'), Student('beer')];
...
(Ellipses are not part of code... ;-])
I suggest you to put your state variables inside the Stateful Widget scope and not onto the Global Scope.
That said you could have:
ListView.builder(
itemCount: _students.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
child: new Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Card(
child: new Container(
child: CheckboxListTile(
title: Text(
_students[index].name,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
subtitle:
Text("NAME : ${_students[index].name}"),
value: _students[index].selected,
onChanged: (val) {
setState(() {
_students[index].selected = val;
if (!_students[index].selected) {
print(
'${_students[index].name}: i am absent');
}
if (_students[index].selected) {
print(
'${_students[index].name}: i am present');
}
});
},
),
padding: EdgeInsets.all(5),
),
)
],
),
),
);
}
)
You should also use an array of bool(s) of the same length of your snapshot data students array... but I'd like to suggest to track this information directly on your Student model.
UPDATE
As you are more comfortable using array I've change my code using a complementary array of bool of the same size of your student list.
All you have to do is an array (not a single value) of boolean values, the same size of your student array, lets call this list_student_present (I instead use list_student_present2)
At the beginning you initialize this in your check function a way like that:
list_student_data = iter_student_data.toList();
// This is the array you wanna use (first all false)
list_student_present = iter_student_data.map((_) => false).toList();
And then you will use this array of bool to check the state of your checkboxes:
itemBuilder: (BuildContext context, int index) {
return new Container(
child: new Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Card(
child: new Container(
child: CheckboxListTile(
title: Text(
_students[index].name,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
subtitle:
Text("NAME : ${_students[index].name}"),
value: list_student_present[index],
onChanged: (val) {
setState(() {
list_student_present[index] = val;
if (!list_student_present[index]) {
print(
'${_students[index].name}: i am absent');
}
if (list_student_present[index]) {
print(
'${_students[index].name}: i am present');
}
});
},
),
padding: EdgeInsets.all(5),
),
)
],
),
),
);
}
Do not use my Student class (I continue using it so that I'm able to show you data without firebase), continue using your students array list from your firebase snapshot.
Full code:
import 'package:flutter/material.dart';
import 'dart:async';
Map map_student_data;
Iterable iter_student_data, iter_student_key;
List list_student_data, list_student_key, list_student_present, list_student_present2;
bool t = true;
List list;
String validation = "yes";
int i;
int year;
class Student {
var name = 'foo';
var year = '2018';
var selected = false;
Student(this.name);
}
class IImca_attendence extends StatefulWidget {
#override
_IImca_attendenceState createState() => _IImca_attendenceState();
}
class _IImca_attendenceState extends State<IImca_attendence> {
var _students = [Student('foo'), Student('pub'), Student('beer')];
#override
void initState() {
this.check_year();
super.initState();
}
DateTime date = DateTime.now();
Future check_year() async {
Map sea = {1: 'atlantic', 2: 'pacific'};
Iterable iter = sea.keys;
list = iter.toList();
list.sublist(list.length - 1);
list.sort();
setState(() {
year = list.length - 2;
});
check();
}
Future check() async {
map_student_data = {
0: {'NAME': 'foo', 'CURRENT CLASS': 'pub', 'PHOTO': ''}
};
iter_student_data = map_student_data.values;
iter_student_key = map_student_data.keys;
list_student_data = iter_student_data.toList();
// This is the array you wanna use
list_student_present = iter_student_data.map((_) => false).toList();
// This is the array for my example
list_student_present2 = _students.map((_) => false).toList();
list_student_key = iter_student_key.toList();
for (i = 0; i <= list_student_data.length; i++) {
bool ss = true;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("II-MCA"),
centerTitle: true,
actions: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
setState(() {
check_year();
});
})
],
),
body: validation == "1"
? new Center(
child: Text(
"STUDENT DOSE NOT EXIST IN $year",
style: TextStyle(
color: Colors.grey,
fontWeight: FontWeight.bold,
fontSize: 20),
),
)
: ListView.builder(
itemCount: _students.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
child: new Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Card(
child: new Container(
child: CheckboxListTile(
title: Text(
_students[index].name,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
subtitle:
Text("NAME : ${_students[index].name}"),
value: list_student_present2[index],
onChanged: (val) {
setState(() {
list_student_present2[index] = val;
if (!list_student_present2[index]) {
print(
'${_students[index].name}: i am absent');
}
if (list_student_present2[index]) {
print(
'${_students[index].name}: i am present');
}
});
},
),
padding: EdgeInsets.all(5),
),
)
],
),
),
);
}));
}
}
I really don't like this solution. What I suggest you is to create your PODOs (Plain Old Dart Object) representing your firebase models and deserialise them from your firebase snapshots.

onTap doesn't work on ListWheelScrollView children items - Flutter

I'm trying to make a list of items using ListWheelScrollView and I want to have the ability of tapping on items but it seems onTap doesn't work.
Here is a simple code
List<int> numbers = [
1,
2,
3,
4,
5
];
...
Container(
height: 200,
child: ListWheelScrollView(
controller: fixedExtentScrollController,
physics: FixedExtentScrollPhysics(),
children: numbers.map((month) {
return Card(
child: GestureDetector(
onTap: () {
print(123);
},
child: Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
month.toString(),
style: TextStyle(fontSize: 18.0),
),
)),
],
),
));
}).toList(),
itemExtent: 60.0,
),
)
Is there something wrong with this code ? I'm pretty sure something like this will work on a ListView or other scrolling widgets.
I solved the problem. I hope is helpful.
create a int variable in State class.
class _MenuWheelState extends State {
int _vIndiceWheel;
in the Function onSelectedItemChanged of the ListWheelScrollView set the variable:
onSelectedItemChanged: (ValueChanged) {
setState(() {
_vIndiceWheel = ValueChanged; });
},
create a GestureDetecture and put the ListWheelScrollView inside:
GestureDetector(
child: ListWheelScrollView(...
create onTap function at the GestureDetecture like this code:
// this is necessary
if (_vIndiceWheel == null) {
_vIndiceWheel = 0;
}
switch (_vIndiceWheel) {
case 0:
{
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return YourSecondScreen();
},
...

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...)
)

Detect Keyboard event in Flutter

How to detect keyboard events on TextFormField in Flutter? If the user deletes the last letter and presses the delete button again, the cursor should jump back to the previous TextFormField.
I would like to implement a pin-enter screen, something like that one.
https://mobile-solutions.ingenico.com/wp-content/uploads/IMG_022.png
Yes you can do that using onChange And FocusScope for set focus
Here when you delete last letter of second FormField focus set on first FormField
var firstField = FocusNode();
var secondField = FocusNode();
#override
Widget build(BuildContext context) {
return Column(children: <Widget>[
TextFormField(
focusNode: firstField,
onChanged: (text) {
print("First field: $text");
},
),
TextFormField(
focusNode: secondField,
onChanged: (text) {
if (text.length <= 0) {
FocusScope.of(context).requestFocus(firstField);
}
print("Second field: $text");
},
)
]);
}
To implement pin-enter screen, You don't have to manage the Keyboard events. As that screen has different button and each button does the entry as its text.
So logically you just have to add the value of the button to the list and remove the value from the list when delete key is pressed.
You can fill the TextField with the length and data of the list.
I hope this answer helps you. I faced a similar situation and this is how I did it!
class _OtpEntryFormState extends StatelessWidget {
final _formKey = GlobalKey<FormState>();
//Declare your focusNodes
FocusNode node1 = FocusNode();
FocusNode node2 = FocusNode();
FocusNode node3 = FocusNode();
FocusNode node4 = FocusNode();
String pin1;
String pin2;
String pin3;
String pin4;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
//Now create a row widget, where we'll be adding our textboxes
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//TextBox1
SizedBox(
width: 50,
child: TextFormField(
inputFormatters: [
LengthLimitingTextInputFormatter(1),
],
focusNode: node1,
onChanged: (text) {
if (text.length == 1) {
FocusScope.of(context).requestFocus(node2);
pin1 = text;
}
},
keyboardType: TextInputType.number,
)
),
//Spacing in-between textbox
SizedBox(
width: 20
),
//TextBox2
SizedBox(
width: 50,
child: TextFormField(
inputFormatters: [
LengthLimitingTextInputFormatter(1),
],
focusNode: node2,
onChanged: (text) {
if (text.length == 1) {
FocusScope.of(context).requestFocus(node3);
pin2 = text;
}
else if (text.isEmpty) {
FocusScope.of(context).requestFocus(node1);
}
},
keyboardType: TextInputType.number,
)
),
//Spacing in-between textbox
SizedBox(
width: 20
),
//TextBox3
SizedBox(
width: 50,
child: TextFormField(
inputFormatters: [
LengthLimitingTextInputFormatter(1),
],
focusNode: node3,
onChanged: (text) {
if (text.length == 1) {
FocusScope.of(context).requestFocus(node4);
pin3 = text;
}
else if (text.isEmpty) {
FocusScope.of(context).requestFocus(node2);
}
},
)
),
//Spacing in-between textbox
SizedBox(
width: 20
),
//TextBox4
SizedBox(
width: 50,
child: TextFormField(
inputFormatters: [
LengthLimitingTextInputFormatter(1),
],
focusNode: node4,
onChanged: (text) {
if (text.length == 1) {
FocusScope.of(context).requestFocus(node4);
pin4 = text;
}
else if (text.isEmpty) {
FocusScope.of(context).requestFocus(node3);
}
},
)
),
],
),
//Spacing Between the textbox row and the submit button
SizedBox(
height: 10,
),
//Submit Button
SizedBox(
width: 30,
child: RaisedButton(
color: Colors.blueAccent,
textColor: Colors.white,
onPressed: () {
if (_formKey.currentState.validate()) {
String fullPin = pin1+pin2+pin3+pin4;
String combinedPin = fullPin;
int intPin = int.parse(combinedPin);
print("OPT Pin: " + intPin.toString());
}
},
child:
Text(
'SUBMIT',
)
),
),
],
);
}
}

Resources