Flutter Dropdownbutton state is not updated - Everything else works fine - flutter

I have created a stateful customer information form and the there are two widgets that use the state - two list tiles with radio buttons and a drop down button. The list tiles respond to the code and updates their states and saves the data to the variable as expected.
But the drop down button only saves the value in the assigned variable, but does not display the new value instead of the hint.
I have followed sample code but as I'm new to Flutter I can't find where the mishap might be. Thank you for any help!
The code is as follows; please note that some of the label texts are in my native language and it wouldn't affect code readability. The code in question is in bold (**).
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class CustomerForm extends StatefulWidget {
const CustomerForm({Key key}) : super(key: key);
#override
CustomerFormState createState() {
return CustomerFormState();
}}
class CustomerFormState extends State<CustomerForm> {
final formKey = GlobalKey<FormState>();
String _name = '';
String _age = '';
String _nic = '';
String _sex = '';
String _telephone = '';
String _address = '';
String _email = '';
String _inquiry = '';
**String _branch = '';**
#override
Widget build(BuildContext context) => Scaffold(
resizeToAvoidBottomInset : false,
appBar: AppBar(
title: Text('ඔබේ තොරතුරු පහත පුරවන්න',
style: TextStyle(
fontSize: (20.0),),),
centerTitle: true,
backgroundColor: Colors.cyan,
shadowColor: Colors.tealAccent[50] ),
body:SingleChildScrollView(
child: Form(
key: formKey,
child: Column(children:[
Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: TextFormField(
autofocus: true,
decoration: InputDecoration(labelText: 'නම'),
validator: (value) {
if (value.isEmpty) {
return 'මෙම තොරතුරු අවශ්‍යයි';}
else if(value.length < 4) {
return 'ඔබ යෙදූ නම කෙටි වැඩි ය.';}
return null;},
onSaved: (value) => _name = value),
),
Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: TextFormField(
decoration: InputDecoration(labelText: 'වයස (අවු. 18ට වැඩි විය යුතුයි)'),
validator: (value) {
if (value.isEmpty) {
return 'මෙම තොරතුරු අවශ්‍යයි';}
else if (int.parse(value)<18) {
return 'වයස අවුරුදු 18ට වැඩි විය යුතුයි';}
else if (int.parse(value) > 99) {
return 'සැබෑ වයසක් ඇතුළත් කරන්න';}
else {
return null;}},
onSaved: (value) => _age = value),
),
Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: TextFormField(
decoration: InputDecoration(
labelText: 'ජාතික හැඳුනුම්පත් අංකය'),
validator: (value) {
if (value.isEmpty) {
return 'මෙම තොරතුරු අවශ්‍යයි';}
//TODO finish regexp
String p = r'(^[0-9]v|V|x|X$)';
RegExp regExp = new RegExp(p);
if (regExp.hasMatch(p) && value.length == 10 ) {
return null;}
else
return 'ඇතුළත් කල ජාතික හැඳුනුම්පත් අංකය වලංගු නොවේ';},
onSaved: (value) => _nic = value),
),
Padding(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
child: Row(
children:[
SizedBox(
width: 100.0,
child: Text('ස්ත්‍රී / පුරුෂ භාවය :',
style: TextStyle(
color: Colors.black54,
fontSize: 16.0),),
),
Expanded(
child: ListTile(
leading:Radio<String>(
value:'male',
groupValue: _sex,
activeColor: Colors.teal,
onChanged: (value) {
setState(() {_sex = value;});},),
title: const Text('පුරුෂ'),),
),
Expanded(
child: ListTile(
leading:Radio<String>(
value:'female',
groupValue: _sex,
activeColor: Colors.teal,
onChanged: (value) {setState(() {_sex = value;});},),
title: const Text('ස්ත්‍රී'),),),
],
),
),
Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: TextFormField(
decoration: InputDecoration(
labelText: 'දුරකථන අංකය'),
validator: (value) {
String pattern = r'^[0-9]{10}$';
RegExp regExp = new RegExp(pattern);
if (value.length == 0) {
return 'මෙම තොරතුරු අවශ්‍යයි';}
else if (!regExp.hasMatch(value)) {
return 'ඉලක්කම් 10ක් සහිත 0න් ආරම්භ වන වලංගු දුරකථන අංකයක් \n ඇතුළත් කරන්න';}
else {return null;}},
onSaved: (value) => _telephone = value),
),
Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: TextFormField(
decoration: InputDecoration(
labelText: 'ලිපිනය'),
validator: (value) {
if (value.isEmpty) {
return 'මෙම තොරතුරු අවශ්‍යයි';}
else {return null;}},
onSaved: (value) => _address = value),
),
Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: TextFormField(
decoration: InputDecoration(
labelText: 'විද්‍යුත් ලිපිනය (තිබේනම්)'),
keyboardType: TextInputType.emailAddress,
validator: (value) {
String p = "[a-zA-Z0-9\+\.\_\%\-\+]{1,256}" +
"\\#" + "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" + "(" + "\\." +
"[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" + ")+";
RegExp regExp = new RegExp(p);
if (value.isNotEmpty && !regExp.hasMatch(value)) {
return 'ඇතුළත් කල විද්‍යුත් ලිපිනය වලංගු නොවේ';}
else
return null;},
onSaved: (value) => _email = value)
),
Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: TextFormField(
decoration: InputDecoration(
labelText: 'අමතර කරුණු / විමසීම්'),
onSaved: (value) => _inquiry = value),
),
Padding(padding: const EdgeInsets.fromLTRB(0, 0, 0, 25),),
**DropdownButton<String>(
hint:Text ('ඔබට ළඟම බැංකු ශාඛාව මෙතනින් තෝරන්න.'),
icon: Icon(Icons.arrow_downward),
iconSize: 24, elevation: 16, style: TextStyle(color: Colors.teal),
underline: Container(height: 2, color: Colors.teal,),
onChanged: (String value) {setState(() {_branch = value;});},
items: <String>['Matara', 'Colombo','Galle'].map<DropdownMenuItem<String>>((String value){
return DropdownMenuItem<String>(value:value,
child: Text(value, style: TextStyle(fontSize: 20.0), textAlign: TextAlign.center,));}).toList(),),**
Padding(padding: const EdgeInsets.fromLTRB(0, 0, 0, 45),),
ElevatedButton (
onPressed: () {
if (_sex.isEmpty){
final message = 'ස්ත්‍රි පුරුෂ බව ඇතුළත් කරන්න';
final snackBar = SnackBar(
content: Text(message),
backgroundColor:Colors.redAccent,
duration: Duration(milliseconds: 3000),);
ScaffoldMessenger.of(context).showSnackBar(snackBar);}
else
if (formKey.currentState.validate()){
formKey.currentState.save();
final message = '$_name, විමසීම සාර්ථකයි.';
final snackBar = SnackBar(
content: Text(message),
backgroundColor:Colors.blue,
duration: Duration(milliseconds: 3000),);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
return http.post(
Uri.parse('http://10.0.2.2:5000/api/userdata'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',},
body: jsonEncode(<String, String>{
'name': _name,
'age': _age,
'NIC': _nic,
'sex': _sex,
'tel': _telephone,
'addr': _address,
'email': _email,
'inquiry': _inquiry,
'branch': _branch
})
);
}},
child: Text('ඔබේ විමසීම මෙතැනින් අප වෙත යොමුකරන්න.'),
)
]
),
),
));}
I tried assigining _branch with 'value' of the button and reversing the names, it did not work. Then When I assigned _branch to the value attribute of teh drop down button it seemed to work but an error came on when the screen was reloaded

This might help you out:
DropdownButton(
hint: Text('ඔබට ළඟම බැංකු ශාඛාව මෙතනින් තෝරන්න.'),
icon: Icon(Icons.arrow_downward),
iconSize: 24,
value: _branch,
elevation: 16,
style: TextStyle(color: Colors.teal),
underline: Container(
height: 2,
color: Colors.teal,
),
onChanged: (value) async {
setState(() {
_branch = value;
debugPrint(value);
});
},
items: <String>['Matara', 'Colombo', 'Galle']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: TextStyle(fontSize: 20.0),
textAlign: TextAlign.center,
));
}).toList(),
),
You'vent declared the "value" inside the DropdownButton widget, if you want your text to appear soon as you choose an option from the Dropdown you must use it like this.
I guess if there's a problem after you change it maybe it's not related to dropdownbutton.

This could be very helpful
final List<String> _items = ['Matara', 'Colombo','Galle'];
late String _branch = _items[0]; //Default selected value
DropdownButton(
hint: Text('ඔබට ළඟම බැංකු ශාඛාව මෙතනින් තෝරන්න.'),
icon: Icon(Icons.arrow_downward),
iconSize: 24,
value: _branch,
elevation: 16,
style: TextStyle(color: Colors.teal),
underline: Container(
height: 2,
color: Colors.teal,
),
onChanged: (value) async {
setState(() {
_branch = value;
debugPrint(value);
});
},
items:_items.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: TextStyle(fontSize: 20.0),
textAlign: TextAlign.center,
)
);
}).toList(),
),

Related

Not able to remove focus from input field

I have four textfields, a title field, a details field, a date field, and a time field. Both the date and time fields are wrapped within a gesture detector, and onTap calls a pickDateAndTime method. The problem is that when I click on the date field and try to manually change the time through the input rather than the dial way, the focus goes to the title field and when I am still on the time picker and type something in the time picker, the title field gets changed with the new input. The weird part is that this error just appeared out of nowhere, and there are no errors reported in the console.
class TodoScreen extends StatefulWidget {
final int? todoIndex;
final int? arrayIndex;
const TodoScreen({Key? key, this.todoIndex, this.arrayIndex})
: super(key: key);
#override
State<TodoScreen> createState() => _TodoScreenState();
}
class _TodoScreenState extends State<TodoScreen> {
final ArrayController arrayController = Get.find();
final AuthController authController = Get.find();
final String uid = Get.find<AuthController>().user!.uid;
late TextEditingController _dateController;
late TextEditingController _timeController;
late TextEditingController titleEditingController;
late TextEditingController detailEditingController;
late String _setTime, _setDate;
late String _hour, _minute, _time;
late String dateTime;
late bool done;
#override
void initState() {
super.initState();
String title = '';
String detail = '';
String date = '';
String? time = '';
if (widget.todoIndex != null) {
title = arrayController
.arrays[widget.arrayIndex!].todos![widget.todoIndex!].title ??
'';
detail = arrayController
.arrays[widget.arrayIndex!].todos![widget.todoIndex!].details ??
'';
date = arrayController
.arrays[widget.arrayIndex!].todos![widget.todoIndex!].date!;
time = arrayController
.arrays[widget.arrayIndex!].todos![widget.todoIndex!].time;
}
_dateController = TextEditingController(text: date);
_timeController = TextEditingController(text: time);
titleEditingController = TextEditingController(text: title);
detailEditingController = TextEditingController(text: detail);
done = (widget.todoIndex == null)
? false
: arrayController
.arrays[widget.arrayIndex!].todos![widget.todoIndex!].done!;
}
DateTime selectedDate = DateTime.now();
TimeOfDay selectedTime = TimeOfDay(
hour: (TimeOfDay.now().minute > 55)
? TimeOfDay.now().hour + 1
: TimeOfDay.now().hour,
minute: (TimeOfDay.now().minute > 55) ? 0 : TimeOfDay.now().minute + 5);
Future<DateTime?> _selectDate() => showDatePicker(
builder: (context, child) {
return datePickerTheme(child);
},
initialEntryMode: DatePickerEntryMode.calendarOnly,
context: context,
initialDate: selectedDate,
initialDatePickerMode: DatePickerMode.day,
firstDate: DateTime.now(),
lastDate: DateTime(DateTime.now().year + 5));
Future<TimeOfDay?> _selectTime() => showTimePicker(
builder: (context, child) {
return timePickerTheme(child);
},
context: context,
initialTime: selectedTime,
initialEntryMode: TimePickerEntryMode.input);
Future _pickDateTime() async {
DateTime? date = await _selectDate();
if (date == null) return;
if (date != null) {
selectedDate = date;
_dateController.text = DateFormat("MM/dd/yyyy").format(selectedDate);
}
TimeOfDay? time = await _selectTime();
if (time == null) {
_timeController.text = formatDate(
DateTime(
DateTime.now().year,
DateTime.now().day,
DateTime.now().month,
DateTime.now().hour,
DateTime.now().minute + 5),
[hh, ':', nn, " ", am]).toString();
}
if (time != null) {
selectedTime = time;
_hour = selectedTime.hour.toString();
_minute = selectedTime.minute.toString();
_time = '$_hour : $_minute';
_timeController.text = _time;
_timeController.text = formatDate(
DateTime(2019, 08, 1, selectedTime.hour, selectedTime.minute),
[hh, ':', nn, " ", am]).toString();
}
}
#override
Widget build(BuildContext context) {
bool visible =
(_dateController.text.isEmpty && _timeController.text.isEmpty)
? false
: true;
final formKey = GlobalKey<FormState>();
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text((widget.todoIndex == null) ? 'New Task' : 'Edit Task',
style: menuTextStyle),
leadingWidth: (MediaQuery.of(context).size.width < 768) ? 90.0 : 100.0,
leading: Center(
child: Padding(
padding: (MediaQuery.of(context).size.width < 768)
? const EdgeInsets.only(left: 0)
: const EdgeInsets.only(left: 21.0),
child: TextButton(
style: const ButtonStyle(
splashFactory: NoSplash.splashFactory,
),
onPressed: () {
Get.back();
},
child: Text(
"Cancel",
style: paragraphPrimary,
),
),
),
),
centerTitle: true,
actions: [
Center(
child: Padding(
padding: (MediaQuery.of(context).size.width < 768)
? const EdgeInsets.only(left: 0)
: const EdgeInsets.only(right: 21.0),
child: TextButton(
style: const ButtonStyle(
splashFactory: NoSplash.splashFactory,
),
onPressed: () async {
},
child: Text((widget.todoIndex == null) ? 'Add' : 'Update',
style: paragraphPrimary),
),
),
)
],
),
body: SafeArea(
child: Container(
width: double.infinity,
padding: (MediaQuery.of(context).size.width < 768)
? const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0)
: const EdgeInsets.symmetric(horizontal: 35.0, vertical: 15.0),
child: Column(
children: [
Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
validator: Validator.titleValidator,
controller: titleEditingController,
autofocus: true, // problem here
autocorrect: false,
cursorColor: Colors.grey,
maxLines: 1,
maxLength: 25,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
counterStyle: counterTextStyle,
hintStyle: hintTextStyle,
hintText: "Title",
border: InputBorder.none),
style: todoScreenStyle),
primaryDivider,
TextField(
controller: detailEditingController,
maxLines: null,
autocorrect: false,
cursorColor: Colors.grey,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
counterStyle: counterTextStyle,
hintStyle: hintTextStyle,
hintText: "Notes",
border: InputBorder.none),
style: todoScreenDetailsStyle),
],
),
),
Visibility(
visible: (widget.todoIndex != null) ? true : false,
child: GestureDetector(
onTap: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Completed",
style: todoScreenStyle,
),
Transform.scale(
scale: 1.3,
child: Theme(
data: ThemeData(
unselectedWidgetColor: const Color.fromARGB(
255, 187, 187, 187)),
child: Checkbox(
shape: const CircleBorder(),
checkColor: Colors.white,
activeColor: primaryColor,
value: done,
side: Theme.of(context).checkboxTheme.side,
onChanged: (value) {
setState(() {
done = value!;
});
})),
)
],
),
),
),
GestureDetector(
onTap: () async {
await _pickDateTime();
setState(() {
visible = true;
});
},
child: Column(
children: [
Row(
children: [
Flexible(
child: TextField(
enabled: false,
controller: _dateController,
onChanged: (String val) {
_setDate = val;
},
decoration: InputDecoration(
hintText: "Date",
hintStyle: hintTextStyle,
border: InputBorder.none),
style: todoScreenStyle,
),
),
visible
? IconButton(
onPressed: () {
_dateController.clear();
_timeController.clear();
setState(() {});
},
icon: const Icon(
Icons.close,
color: Colors.white,
))
: Container()
],
),
primaryDivider,
TextField(
onChanged: (String val) {
_setTime = val;
},
enabled: false,
controller: _timeController,
decoration: InputDecoration(
hintText: "Time",
hintStyle: hintTextStyle,
border: InputBorder.none),
style: todoScreenStyle,
)
],
),
),
],
),
),
),
);
}
}
Should I open an issue on Github, as I had not made any changes to the code for it behave this way and also because there were no errors in the console
Here is the full code on Github
Update
Here is a reproducible example:
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(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const TodoScreen(),
);
}
}
class TodoScreen extends StatefulWidget {
const TodoScreen({Key? key}) : super(key: key);
#override
State<TodoScreen> createState() => _TodoScreenState();
}
class _TodoScreenState extends State<TodoScreen> {
late TextEditingController _dateController;
late TextEditingController _timeController;
late TextEditingController titleEditingController;
late TextEditingController detailEditingController;
late String _setTime, _setDate;
late String _hour, _minute, _time;
late String dateTime;
#override
void initState() {
super.initState();
String title = '';
String detail = '';
String date = '';
String? time = '';
_dateController = TextEditingController(text: date);
_timeController = TextEditingController(text: time);
titleEditingController = TextEditingController(text: title);
detailEditingController = TextEditingController(text: detail);
}
#override
void dispose() {
super.dispose();
titleEditingController.dispose();
detailEditingController.dispose();
_timeController.dispose();
_dateController.dispose();
}
Theme timePickerTheme(child) => Theme(
data: ThemeData.dark().copyWith(
timePickerTheme: TimePickerThemeData(
backgroundColor: const Color.fromARGB(255, 70, 70, 70),
dayPeriodTextColor: Colors.green,
hourMinuteTextColor: MaterialStateColor.resolveWith((states) =>
states.contains(MaterialState.selected)
? Colors.white
: Colors.white),
dialHandColor: Colors.green,
helpTextStyle: TextStyle(
fontSize: 12, fontWeight: FontWeight.bold, color: Colors.green),
dialTextColor: MaterialStateColor.resolveWith((states) =>
states.contains(MaterialState.selected)
? Colors.white
: Colors.white),
entryModeIconColor: Colors.green,
),
textButtonTheme: TextButtonThemeData(
style: ButtonStyle(
foregroundColor:
MaterialStateColor.resolveWith((states) => Colors.green)),
),
),
child: child!,
);
Theme datePickerTheme(child) => Theme(
data: ThemeData.dark().copyWith(
colorScheme: ColorScheme.dark(
surface: Colors.green,
secondary: Colors.green,
onPrimary: Colors.white,
onSurface: Colors.white,
primary: Colors.green,
)),
child: child!,
);
DateTime selectedDate = DateTime.now();
TimeOfDay selectedTime = TimeOfDay(
hour: (TimeOfDay.now().minute > 55)
? TimeOfDay.now().hour + 1
: TimeOfDay.now().hour,
minute: (TimeOfDay.now().minute > 55) ? 0 : TimeOfDay.now().minute + 5);
Future<DateTime?> _selectDate() => showDatePicker(
builder: (context, child) {
return datePickerTheme(child);
},
initialEntryMode: DatePickerEntryMode.calendarOnly,
context: context,
initialDate: selectedDate,
initialDatePickerMode: DatePickerMode.day,
firstDate: DateTime.now(),
lastDate: DateTime(DateTime.now().year + 5));
Future<TimeOfDay?> _selectTime() => showTimePicker(
builder: (context, child) {
return timePickerTheme(child);
},
context: context,
initialTime: selectedTime,
initialEntryMode: TimePickerEntryMode.input);
Future _pickDateTime() async {
DateTime? date = await _selectDate();
if (date == null) return;
if (date != null) {
selectedDate = date;
_dateController.text = selectedDate.toString();
}
TimeOfDay? time = await _selectTime();
if (time != null) {
selectedTime = time;
_hour = selectedTime.hour.toString();
_minute = selectedTime.minute.toString();
_time = '$_hour : $_minute';
_timeController.text = _time;
_timeController.text =
DateTime(2019, 08, 1, selectedTime.hour, selectedTime.minute)
.toString();
}
}
#override
Widget build(BuildContext context) {
bool visible =
(_dateController.text.isEmpty && _timeController.text.isEmpty)
? false
: true;
final formKey = GlobalKey<FormState>();
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
centerTitle: true,
),
body: SafeArea(
child: Container(
width: double.infinity,
padding: (MediaQuery.of(context).size.width < 768)
? const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0)
: const EdgeInsets.symmetric(horizontal: 35.0, vertical: 15.0),
child: Column(
children: [
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14.0)),
padding: const EdgeInsets.symmetric(
horizontal: 24.0, vertical: 15.0),
child: Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: titleEditingController,
autofocus: true,
autocorrect: false,
cursorColor: Colors.grey,
maxLines: 1,
maxLength: 25,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
hintText: "Title", border: InputBorder.none),
),
Divider(color: Colors.black),
TextField(
controller: detailEditingController,
maxLines: null,
autocorrect: false,
cursorColor: Colors.grey,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
hintText: "Notes", border: InputBorder.none),
),
],
),
)),
GestureDetector(
onTap: () async {
await _pickDateTime();
setState(() {
visible = true;
});
},
child: Container(
margin: const EdgeInsets.only(top: 20.0),
width: double.infinity,
padding: const EdgeInsets.symmetric(
horizontal: 24.0, vertical: 15.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14.0)),
child: Column(
children: [
Row(
children: [
Flexible(
child: TextField(
enabled: false,
controller: _dateController,
onChanged: (String val) {
_setDate = val;
},
decoration: InputDecoration(
hintText: "Date", border: InputBorder.none),
),
),
visible
? IconButton(
onPressed: () {
_dateController.clear();
_timeController.clear();
setState(() {});
},
icon: const Icon(
Icons.close,
color: Colors.white,
))
: Container()
],
),
Divider(
color: Colors.blue,
),
TextField(
onChanged: (String val) {
_setTime = val;
},
enabled: false,
controller: _timeController,
decoration: InputDecoration(
hintText: "Enter", border: InputBorder.none),
)
],
)),
),
],
),
),
),
);
}
}
In your main.dart file, you should return something like this:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
// This allows closing keyboard when tapping outside of a text field
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus &&
currentFocus.focusedChild != null) {
FocusManager.instance.primaryFocus!.unfocus();
}
},
child: // your app's entry point,
);
}
}
Add a focusNode in your textField:
FocusNode focusNode = FocusNode();
TextField(
focusNode: focusNode,
);
And then in the gesture detector, add that following code to unselect the textfield input:
FocusScope.of(context).requestFocus(FocusNode());
simply wrap your Scaffold widget GestureDetector and add FocusScope.of(context).requestFocus(FocusNode()); it will automatically unfocused text field when you click anywhere on your screen
GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Scaffold()
)
You can use below code to remove focus in gesture detector event
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
currentFocus.unfocus();
}

Flutter: Radio and Switch buttons isn't updating inside a listview.builder, but it works outside

I'm using a form to create multiples vehicle entries.
every time I click on the floating button it adds a vehicleform to the page one on top of the other. "A List type that receives widgets"
If I try to select the "leased radio option", the form doesn't update, and if hit the switch button right below, nothing happens again. BUT! Curiously if I hit any of the Dropdown State....it works for them (only the dropdown gets updated). and BUT! number 2: If I hit the floating button to add a new vehicle form, the changes made on the previous form gets carried to the new form. My theory is that the buttons are working under the hood, but the setStates are no running correctly
On the main_page.dart there is a stateful widget that calls vehicles_page() which holds all the scaffold and widgets for that form including a dropdown list which is called from a 3rd file(dropdown_forms.dart).
To guide towards the right direction, just lay your eyes at the end of the code on the build() function.
FYI - Flutter Doctor -v returned no errors
Yes -I'm using stateful widgets
Yes - I'm using setstates to update
No - I'm not a professional programmer, I'm a super beginner on flutter and this is my 3rd week playing with flutter
After running some tests.... if I remove them from the listview widget, it works fine.
main.dart
This is the main file
import 'package:flutter/material.dart';
import 'package:learningflutter/screens/parties_page.dart';
import 'package:learningflutter/screens/vehicles_page.dart';
import 'package:learningflutter/screens/incident_page.dart';
// import 'package:learningflutter/parties.dart';
enum VehicleType { pov, leased, pgti }
void main(List<String> args) {
runApp(const VortexEHS());
}
class VortexEHS extends StatelessWidget {
const VortexEHS({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'VortexEHS',
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: _MainPage(),
);
}
}
class _MainPage extends StatefulWidget {
_MainPage({Key? key}) : super(key: key);
#override
State<_MainPage> createState() => __MainPageState();
}
class __MainPageState extends State<_MainPage> {
// int currentPageIndex = 1;
final screens = [
PartiesPage(),
VehiclesPage(),
FormIncident(),
FormIncident()
];
#override
Widget build(BuildContext context) {
return Container(child: VehiclesPage());
}
}
vehicles_page.dart (Code Below)
// ignore_for_file: unused_element, unused_field
import 'package:flutter/material.dart';
import '../dropdown_forms.dart';
enum VehicleType { pov, leased, pgti }
class VehiclesPage extends StatefulWidget {
const VehiclesPage({Key? key}) : super(key: key);
#override
State<VehiclesPage> createState() => _VehiclesPageState();
}
class _VehiclesPageState extends State<VehiclesPage> {
//Variables
VehicleType vehicleType = VehicleType.pov;
bool isCommercial = false;
String? _make;
String? _model;
String? _year;
String? _color;
String? _vimNumber;
String? _plate;
String? _ownerName;
String? _ownerAddress;
String? _ownerCity;
String? _ownerState;
String? _ownerZip;
String? _ownerPhone;
String? _insuranceCoName;
String? _insuranceCoPhone;
String? _policyHolderName;
String? _policyNumber;
List<Widget> vehicles = [];
Widget buildTypeVeihicle() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: ListTile(
horizontalTitleGap: 0,
dense: true,
title: const Text('POV'),
leading: Radio(
value: VehicleType.pov,
groupValue: vehicleType,
onChanged: (VehicleType? value) {
setState(() {
vehicleType = value!;
});
}),
),
),
Expanded(
child: ListTile(
horizontalTitleGap: 0,
dense: true,
title: const Text('Leased'),
leading: Radio(
value: VehicleType.leased,
groupValue: vehicleType,
onChanged: (VehicleType? value) {
setState(() {
vehicleType = value!;
});
}),
),
),
Expanded(
child: ListTile(
horizontalTitleGap: 0,
dense: true,
title: const Text(
'PGTI',
softWrap: false,
),
leading: Radio(
value: VehicleType.pgti,
groupValue: vehicleType,
onChanged: (VehicleType? value) {
setState(() {
vehicleType = value!;
});
}),
),
),
],
);
}
Widget _buildIsCommercial() {
return SwitchListTile(
title: const Text('This is commercial vehicle?'),
value: isCommercial,
onChanged: (bool value) {
setState(() {
isCommercial = value;
});
},
// secondary: const Icon(Icons.car_repair),
);
}
Widget _buildVehicleMake() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Make'),
validator: (String? value) {
if (value == null) {
return 'Make is required';
}
return null;
},
onSaved: (String? value) {
_make = value;
},
);
}
Widget _buildVehicleModel() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Model'),
validator: (String? value) {
if (value == null) {
return 'Model is required';
}
return null;
},
onSaved: (String? value) {
_model = value;
},
);
}
Widget _buildVehicleYear() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Year'),
validator: (String? value) {
if (value == null) {
return 'Year is required';
}
return null;
},
onSaved: (String? value) {
_year = value;
},
);
}
Widget _buildVehicleColor() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Color'),
validator: (String? value) {
if (value == null) {
return 'Color is required';
}
return null;
},
onSaved: (String? value) {
_color = value;
},
);
}
Widget _buildVehicleVinNumber() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Vin Number'),
validator: (String? value) {
if (value == null) {
return 'Vin Number is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildVehiclePlate() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Plate Number'),
validator: (String? value) {
if (value == null) {
return 'Plate Number is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildOwnerName() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Owner Name'),
validator: (String? value) {
if (value == null) {
return 'Owner Name is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildOwnerAddress() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Owner Address'),
validator: (String? value) {
if (value == null) {
return 'Owner Address is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildOwnerCity() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Owner City'),
validator: (String? value) {
if (value == null) {
return 'Owner City is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildOwnerZip() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Owner Zip'),
validator: (String? value) {
if (value == null) {
return 'Owner Zip is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildOwnerPhone() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Owner Phone'),
validator: (String? value) {
if (value == null) {
return 'Owner Phone is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildInsuranceCo() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Owner Insurance Company'),
validator: (String? value) {
if (value == null) {
return 'Owner Insurance Company is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
// ignore: unused_element
Widget _buildInsurancePhone() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Insurance Phone'),
validator: (String? value) {
if (value == null) {
return 'Insurance Phone is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildPolicyHolderName() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Policy Holder'),
validator: (String? value) {
if (value == null) {
return 'Policy Holder is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildPolicyNumber() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Policy Number'),
validator: (String? value) {
if (value == null) {
return 'Policy Number is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildVehiclesCard() {
return Container(
margin: const EdgeInsets.all(8.0),
decoration: const BoxDecoration(
boxShadow: [
BoxShadow(color: Colors.blueGrey, blurRadius: 10, spreadRadius: -10)
],
),
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: Container(
margin: const EdgeInsets.fromLTRB(0, 0, 0, 10),
padding: const EdgeInsets.all(10),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Icon(Icons.directions_car, color: Colors.teal),
IconButton(
onPressed: () {
setState(() {
print('Trash presses');
});
},
icon: const Icon(Icons.delete),
color: Colors.red,
),
],
),
buildTypeVeihicle(),
_buildIsCommercial(),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Expanded(child: _buildVehicleMake()),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
child: _buildVehicleModel()))
]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Expanded(child: _buildVehicleYear()),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
child: _buildVehicleColor()))
]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Expanded(child: _buildVehicleVinNumber()),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8.0, 0, 0, 0),
child: _buildVehiclePlate(),
)),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 20, 0, 0),
child: DropdownStatesUs())),
]),
const SizedBox(
height: 8,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: const [
Icon(
Icons.person,
color: Colors.teal,
),
],
),
Row(
children: [
Expanded(child: _buildOwnerName()),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
child: _buildOwnerPhone(),
)),
],
),
_buildOwnerAddress(),
Row(
children: [
Expanded(child: _buildOwnerCity()),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 20, 0, 0),
child: DropdownStatesUs(),
)),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
child: _buildOwnerZip(),
)),
],
),
Row(
children: [
Expanded(child: _buildPolicyHolderName()),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
child: _buildPolicyNumber(),
)),
],
)
],
),
),
),
);
}
//
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.teal,
child: const Icon(Icons.add),
onPressed: () {
vehicles.add(_buildVehiclesCard());
// print(vehicles.length);
setState(() {});
},
),
body: Form(
child: ListView.builder(
primary: false,
itemCount: vehicles.length,
itemBuilder: (BuildContext context, int i) {
return vehicles[i];
},
),
),
);
}
}
Thanks for any help beforehand, any other advices or tips to make the code better, let me know
The current state changes behavior reflected on newly item because last item created before the radio-buttom or switch tile was changed. Once you switch the button and change the radioGroup value it will only get to the new created widget that will be trigger by floating action button.
Notice that List<Widget> vehicles = []; holds the created widgets. And while creating new widget you are using state variables like vehicleType and isCommercial. Once you click on 1st generated widget, these variables get new data based on your tap event. while the state is holding these variables, then again you click on fab to add item on vehicles to generate item with current state of vehicleType and isCommercial.
While every list-item will have its own state, it is better to create a new StatefulWidget for each item. Also, you can go for state-management like riverpod, bloc for future purpose.
A simplify version but not complete, you need to handle callback to get changes data or better start using riverpod or bloc for state management
enum VehicleType { pov, leased, pgti }
class VehiclesPage extends StatefulWidget {
const VehiclesPage({Key? key}) : super(key: key);
#override
State<VehiclesPage> createState() => _VehiclesPageState();
}
class _VehiclesPageState extends State<VehiclesPage> {
List<MyDataModel> vehicles = [];
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.teal,
child: const Icon(Icons.add),
onPressed: () {
vehicles.add(MyDataModel());
setState(() {});
},
),
body: Form(
child: ListView.builder(
primary: false,
itemCount: vehicles.length,
itemBuilder: (BuildContext context, int i) {
return ListItem(model: vehicles[i]);
},
),
),
);
}
}
class MyDataModel {
final VehicleType vehicleType;
final bool isCommercial;
MyDataModel({
this.vehicleType = VehicleType.pov,
this.isCommercial = false,
});
MyDataModel copyWith({
VehicleType? vehicleType,
bool? isCommercial,
}) {
return MyDataModel(
vehicleType: vehicleType ?? this.vehicleType,
isCommercial: isCommercial ?? this.isCommercial,
);
}
}
class ListItem extends StatefulWidget {
final MyDataModel model;
const ListItem({
Key? key,
required this.model,
}) : super(key: key);
#override
State<ListItem> createState() => _ListItemState();
}
class _ListItemState extends State<ListItem> {
late MyDataModel model = widget.model;
Widget buildTypeVeihicle() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: ListTile(
horizontalTitleGap: 0,
dense: true,
title: const Text('POV'),
leading: Radio(
value: VehicleType.pov,
groupValue: model.vehicleType,
onChanged: (VehicleType? value) {
setState(() {
model = model.copyWith(vehicleType: value);
});
}),
),
),
Expanded(
child: ListTile(
horizontalTitleGap: 0,
dense: true,
title: const Text('Leased'),
leading: Radio(
value: VehicleType.leased,
groupValue: model.vehicleType,
onChanged: (VehicleType? value) {
setState(() {
model = model.copyWith(vehicleType: value);
});
}),
),
),
Expanded(
child: ListTile(
horizontalTitleGap: 0,
dense: true,
title: const Text(
'PGTI',
softWrap: false,
),
leading: Radio(
value: VehicleType.pgti,
groupValue: model.vehicleType,
onChanged: (VehicleType? value) {
setState(() {
model = model.copyWith(vehicleType: value);
});
}),
),
),
],
);
}
Widget _buildIsCommercial() {
return SwitchListTile(
title: const Text('This is commercial vehicle?'),
value: model.isCommercial,
onChanged: (bool value) {
setState(() {
model = model.copyWith(isCommercial: value);
});
},
// secondary: const Icon(Icons.car_repair),
);
}
Widget _buildVehiclesCard() {
return Container(
margin: const EdgeInsets.all(8.0),
decoration: const BoxDecoration(
boxShadow: [
BoxShadow(color: Colors.blueGrey, blurRadius: 10, spreadRadius: -10)
],
),
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: Container(
margin: const EdgeInsets.fromLTRB(0, 0, 0, 10),
padding: const EdgeInsets.all(10),
child: Column(
children: [
buildTypeVeihicle(),
_buildIsCommercial(),
],
),
),
),
);
}
#override
Widget build(BuildContext context) {
return _buildVehiclesCard();
}
}

Flutter : Right overflowed by 70 pixels

i m new to flutter and am trying to make a Responsive app , i have a textfield and i wanted to add a dropdown list next to it it s working but it shows a error " right overflowed by 150 pixels" even tho i m using the Expanded widget in the child . the error is in both dropdownlists
thank you for ur help in advance
import '../Classes/demandes.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import '../data/menu_item.dart';
import '../Classes/menu.dart';
import '../data/logout.dart';
class Demandes extends StatefulWidget {
#override
_demande createState() => _demande();
}
class _demande extends State<Demandes> {
String dropdownValue = 'Assets';
String dropdownValue1 = 'Locations';
Future<Demandes?> submitData(String description, ASSETNUM, location,
DESCRIPTION_LONGDESCRIPTION) async {
var respone = await http.post(
Uri.parse(
'http://9080/maxrest/rest/mbo/sr/?_lid=azizl&_lpwd=max12345m&_format=json'),
body: {
"description": description,
"ASSETNUM": ASSETNUM,
"location": location,
"DESCRIPTION_LONGDESCRIPTION": DESCRIPTION_LONGDESCRIPTION,
});
var data = respone.body;
print(data);
if (respone.statusCode == 201) {
dynamic responseString = respone.body;
azizFromJson(responseString);
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("Demande de service Cree")));
} else
return null;
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("error")));
return null;
}
late Demandes? _demandes;
TextEditingController descriptionController = TextEditingController();
TextEditingController ASSETNUMController = TextEditingController();
TextEditingController locationController = TextEditingController();
TextEditingController DESCRIPTION_LONGDESCRIPTIONController =
TextEditingController();
Widget DescriptionTextField() {
return TextField(
decoration: InputDecoration(
// labelText: "Description",
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
),
controller: descriptionController,
);
}
Widget AssetTextField() {
return TextField(
decoration: InputDecoration(
// labelText: "Asset",
border: OutlineInputBorder(),
),
controller: ASSETNUMController,
);
}
Widget DeatialsTextField() {
return TextField(
decoration: InputDecoration(
// labelText: "Details",
border: OutlineInputBorder(),
),
maxLines: 10,
controller: DESCRIPTION_LONGDESCRIPTIONController,
);
}
Widget LocationTextField() {
return TextField(
decoration: InputDecoration(
// labelText: "Location",
border: OutlineInputBorder(),
),
controller: locationController,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Creation Demande de service'),
actions: [
PopupMenuButton<MenuItem>(
onSelected: (item) => onSelected(context, item),
itemBuilder: (context) =>
[...MenuItems.itemsFirst.map(buildItem).toList()],
)
],
),
body: ListView(
padding: EdgeInsets.all(8),
// ignore: prefer_const_literals_to_create_immutables
children: [
ListTile(
title: Text("Description"),
),
DescriptionTextField(),
ListTile(
title: Text("Details"),
),
DeatialsTextField(),
ListTile(title: Text("Asset")),
Row(
children: <Widget>[
Expanded(
child: AssetTextField(),
),
const SizedBox(
width: 20,
),
Expanded(
child: DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_drop_down),
elevation: 16,
style: const TextStyle(color: Colors.blue),
underline: Container(
height: 2,
color: Color.fromARGB(255, 0, 140, 255),
),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>[
'Assets',
'100014 moteur 3',
'100027 Système de freinage 1',
'12500 Overhead Crane #2',
'13110 Feeder System',
'13120 Bottom Sealing System',
'13130 Stripper System',
'13140 Conveyor System- Pkg. Dept.',
'13141 Elevator Rails And Drainpan Assembly',
'13142 Carton Escapement Assembly #2',
'13143 Chain Wash Assembly',
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)),
],
),
ListTile(
title: Text("Location"),
),
Row(
children: <Widget>[
Expanded(
child: LocationTextField(),
),
const SizedBox(
width: 20,
),
Expanded(
child: DropdownButton<String>(
value: dropdownValue1,
icon: const Icon(Icons.arrow_drop_down),
elevation: 16,
style: const TextStyle(color: Colors.blue),
underline: Container(
height: 2,
color: Color.fromARGB(255, 0, 140, 255),
),
onChanged: (String? newValue) {
setState(() {
dropdownValue1 = newValue!;
});
},
items: <String>[
'Locations',
'100014 moteur 3',
'10002 7 Système de freinage 1',
'12500 Overhead Crane #2',
'13110 Feeder System',
'13120 Bottom Sealing System',
'13130 Stripper System',
'13140 Conveyor System- Pkg. Dept.',
'13141 Elevator Rails And Drainpan Assembly',
'13142 Carton Escapement Assembly #2',
'13143 Chain Wash Assembly',
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)),
],
),
const SizedBox(
width: 20,
),
Padding(padding: EdgeInsets.all(10)),
ElevatedButton(
onPressed: (() async {
String description = descriptionController.text;
String ASSETNUM = ASSETNUMController.text;
String location = locationController.text;
String DESCRIPTION_LONGDESCRIPTION =
DESCRIPTION_LONGDESCRIPTIONController.text;
Demandes? data = await submitData(description, ASSETNUM,
location, DESCRIPTION_LONGDESCRIPTION);
setState(() {
_demandes = data;
});
}),
child: Text("submit "),
)
],
));
}
}
Ok the only thing that you have to write is this :
Expanded(
child: DropdownButton<String>(
value: dropdownValue,
isExpanded: true, // this
[...]
),
),
Like the documentation say :
Set the dropdown's inner contents to horizontally fill its parent.
By default this button's inner width is the minimum size of its
contents. If [isExpanded] is true, the inner width is expanded to fill
its surrounding container.

Flutter TextFormField Cursor Reset To First position of letter after onChanged

I need some help regarding flutter textformfield This is my code for the textfield controller. The problem is when I type new word,the cursor position is moved automatically from right to left (reset)(before first letter inside box). How I can make the cursor work as usual at the end of current text. I have read few solutions from stack overflow but it still not working. Please help me. Thanks.
class BillingWidget extends StatelessWidget {
final int pageIndex;
final Function validateController;
final formKey = new GlobalKey<FormState>();
BillingWidget(this.billingDetails,this.pageIndex,this.validateController);
final BillingDetails billingDetails;
#override
Widget build(BuildContext context) {
return Form(
key: formKey,
onChanged: () {
if (formKey.currentState.validate()) {
validateController(pageIndex,false);
formKey.currentState.save();
final val = TextSelection.collapsed(offset: _textTEC.text.length);
_textTEC.selection = val;
}
else {
//prevent procced to next page if validation is not successful
validateController(pageIndex,true);
}
},
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 20,bottom: 0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
"Maklumat Pembekal",
textAlign: TextAlign.left,
style: TextStyle(
decoration:TextDecoration.underline,
fontWeight: FontWeight.bold,
fontSize: 16,
color: Colors.grey.shade700,
),
),
),
),
TextFormField(
controller: billingDetails.companyNameTxtCtrl,
maxLength: 30,
decoration: InputDecoration(labelText: "Nama Syarikat"),
validator: (String value) {
return value.isEmpty ? 'Nama Syarikat Diperlukan' : null;
},
onSaved: (String value) {
billingDetails.companyName = value;
billingDetails.companyNameTxtCtrl.text = billingDetails.companyName;
},
),
TextFormField(
controller: billingDetails.addressLine1TxtCtrl,
maxLength: 30,
decoration: InputDecoration(labelText: "Alamat Baris 1"),
validator: (String value) {
return value.isEmpty ? 'Alamat Baris tidak boleh kosong.' : null;
},
onSaved: (String value) {
billingDetails.addressLine1 = value;
billingDetails.addressLine1TxtCtrl.text = billingDetails.addressLine1;
},
),
TextFormField(
controller: billingDetails.addressLine2TxtCtrl,
maxLength: 30,
decoration: InputDecoration(labelText: "Alamat Baris 2"),
onSaved: (String value) {
billingDetails.addressLine2 = value;
billingDetails.addressLine2TxtCtrl.text = billingDetails.addressLine2;
},
),
TextFormField(
controller: billingDetails.addressLine3TxtCtrl,
maxLength: 30,
decoration: InputDecoration(labelText: "Alamat Baris 3"),
onSaved: (String value) {
billingDetails.addressLine3 = value;
billingDetails.addressLine3TxtCtrl.text = billingDetails.addressLine3;
},
),
],
),
);
}
yourController.text = yourString;
yourController.selection = TextSelection.fromPosition(TextPosition(offset: yourController.text.length));

Flutter onChanged: not triggering method to read textfield content

I have a form where users capture information on multiple textfields. Within the Onchange:, I can see that there's activity every time the user types something on the textfield. However, when I call a method to read the textfield content, the method is not being fired. For example, I call the updateFirstName() method within the OnChange: within the nameController textfield. The method doesn't fire and the App fails when I press Save because the FirstName field is null. Any reason why the updateFirstName method on my code below is not being called? I'm new to Flutter so I might be missing something basic.
import 'dart:ffi';
import 'package:flutter/material.dart';
import '../widgets/main_drawer.dart';
import '../utils/database_helper.dart';
import '../models/customer.dart';
import 'package:intl/intl.dart';
class CustomerDetailsScreen extends StatefulWidget {
static const routeName = '/customer-details';
#override
_CustomerDetailsScreenState createState() => _CustomerDetailsScreenState();
}
class _CustomerDetailsScreenState extends State<CustomerDetailsScreen> {
//Define editing controllers for all the text fields
TextEditingController nameController = TextEditingController();
TextEditingController surnameController = TextEditingController();
TextEditingController cellphoneController = TextEditingController();
TextEditingController emailController = TextEditingController();
//Connecting to the database
DatabaseHelper helper = DatabaseHelper();
//Define some variables
String appBarTitle;
Customer customer; //This is the Customer Model
/*
String sFirstName;
String sSurname;
String sCellNumber;
String sEmailAddress;
String sCompanyName = '-';
*/
var _formKey = GlobalKey<FormState>();
//Method to validate e-mail address
bool validateEmail(String value) {
Pattern pattern =
r'^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regex = new RegExp(pattern);
return (!regex.hasMatch(value)) ? false : true;
}
#override
Widget build(BuildContext context) {
TextStyle textStyle = Theme.of(context).textTheme.title;
//Populate the text fields
//nameController.text = customer.sFirstName;
//surnameController.text = customer.sSurname;
//cellphoneController.text = customer.sCellNumber;
//emailController.text = customer.sEmailAddress;
return Scaffold(
appBar: AppBar(
title: Text('Edit Customer'),
),
body: GestureDetector(
//Gesture detector wrapped the entire body so we can hide keyboard \
// when user clicks anywhere on the screen
behavior: HitTestBehavior.opaque,
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: Form(
key: _formKey,
child: Padding(
padding: EdgeInsets.only(top: 15.0, left: 10.0, right: 10.0),
child: ListView(
children: <Widget>[
//First Element - Name
Padding(
padding: EdgeInsets.only(top: 15.0, bottom: 15.0),
child: TextFormField(
controller: nameController,
style: textStyle,
textCapitalization: TextCapitalization.words,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter your name';
}
return null;
},
onChanged: (value) {
debugPrint('Something changed on the Name Text Field');
updateFirstName();
},
decoration: InputDecoration(
labelText: 'Name',
labelStyle: textStyle,
errorStyle:
TextStyle(color: Colors.redAccent, fontSize: 15.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
),
),
//Second Element - Surname
Padding(
padding: EdgeInsets.only(top: 15.0, bottom: 15.0),
child: TextFormField(
controller: surnameController,
style: textStyle,
textCapitalization: TextCapitalization.words,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter your surname';
}
return null;
},
onChanged: (value) {
debugPrint('Something changed on the Surname Text Field');
updateSurname();
},
decoration: InputDecoration(
labelText: 'Surname',
labelStyle: textStyle,
errorStyle:
TextStyle(color: Colors.redAccent, fontSize: 15.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
),
),
//Third Element - Cellphone
Padding(
padding: EdgeInsets.only(top: 15.0, bottom: 15.0),
child: TextFormField(
controller: cellphoneController,
style: textStyle,
keyboardType: TextInputType.number,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter your cellphone number';
} else {
if (value.length < 10)
return 'Cell number must be at least 10 digits';
}
return null;
},
onChanged: (value) {
debugPrint(
'Something changed on the Cellphone Text Field');
updateCellNumber();
},
decoration: InputDecoration(
labelText: 'Cellphone',
labelStyle: textStyle,
errorStyle:
TextStyle(color: Colors.redAccent, fontSize: 15.0),
hintText: 'Enter Cell Number e.g. 0834567891',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
),
),
//Fourth Element - Email Address
Padding(
padding: EdgeInsets.only(top: 15.0, bottom: 15.0),
child: TextFormField(
controller: emailController,
style: textStyle,
keyboardType: TextInputType.emailAddress,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter your e-mail address';
} else {
//Check if email address is valid.
bool validmail = validateEmail(value);
if (!validmail) {
return 'Please enter a valid e-mail address';
}
}
return null;
},
onChanged: (value) {
debugPrint(
'Something changed on the Email Address Text Field');
updateEmailAddress();
},
decoration: InputDecoration(
labelText: 'E-mail',
labelStyle: textStyle,
errorStyle:
TextStyle(color: Colors.redAccent, fontSize: 15.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
),
),
//Fifth Element - Row for Save Button
Padding(
padding: EdgeInsets.only(top: 15.0, bottom: 15.0),
child: Row(
children: <Widget>[
Expanded(
child: RaisedButton(
color: Theme.of(context).primaryColorDark,
textColor: Theme.of(context).primaryColorLight,
child: Text(
'Save',
textScaleFactor: 1.5,
),
onPressed: () {
setState(() {
if (_formKey.currentState.validate()) {
debugPrint('Save button clicked');
//Call the Save method only if the validation is passed
_saveCustomerDetails();
}
});
}),
),
],
)),
],
),
),
),
),
);
}
//**********************Updating what is captured by the user on each text field******************/
//Update the sFirstName of the Customer model object
void updateFirstName() {
print('The updateFirstName was called');
customer.sFirstName = nameController.text;
}
//Update the sSurname of the Customer model object
void updateSurname() {
customer.sSurname = surnameController.text;
}
//Update the sCellNumber of the Customer model object
void updateCellNumber() {
customer.sCellNumber = cellphoneController.text;
}
//Update the sEmailAddress of the Customer model object
void updateEmailAddress() {
customer.sEmailAddress = emailController.text;
customer.sCompanyName = '-';
}
//**********************END - Updating what is captured by the user on each text field******************/
//**************************Saving to the Database*************************************/
void _saveCustomerDetails() async {
//moveToLastScreen();
//Update the dtUpdated of the Customer model with current time (Confirm that it is GMT)
print('Trying to save customer info was called');
customer.dtUpdated = DateFormat.yMMMd().format(DateTime.now());
print('Trying to save customer info was called - 2');
int result;
result = await helper.insertNewHumanCustomer(customer);
if (result != 0) {
//Saving was a Success
_showAlertDialog('Success', 'Customer details saved successfully');
print('The customer details were saved successfully');
} else {
//Saving was a Failure
print('FAILURE - The customer details failed to save');
_showAlertDialog('Failure', 'Oopsy.....something went wrong. Try again');
}
}
//*****Show Alert Popup message*****/
void _showAlertDialog(String title, String message) {
AlertDialog alertDialog = AlertDialog(
title: Text(title),
content: Text(message),
);
showDialog(context: context, builder: (_) => alertDialog);
}
//*****END - Show Alert Popup message*****/
//**************************Saving to the Database*************************************/
}