Non-nullable instance field '_selectedDate' must be initialized - flutter

I am developing an Expenses Management App in flutter, Here I am trying to create a DateTimePicker using showDatePicker(). therefore Inside my presentDateTimePicker() function i am calling showDatePicker() method.
and I am creating a variable for selectedDate where I am not initializing it to a current value since it will change based on the user input.
Inside my setState() method I use _selectedDate = pickedDate.
Still, it shows an error saying "non-nullable instance field"
My widget
//flutter imports
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:intl/intl.dart';
class NewTransaction extends StatefulWidget {
final Function addTx;
NewTransaction(this.addTx);
#override
_NewTransactionState createState() => _NewTransactionState();
}
class _NewTransactionState extends State<NewTransaction> {
final _titleController = TextEditingController();
final _amountController = TextEditingController();
final _brandNameControlller = TextEditingController();
DateTime _selectedDate;
void _submitData() {
final enteredTitle = _titleController.text;
final enteredAmount = double.parse(_amountController.text);
final enteredBrandName = _brandNameControlller.text;
if (enteredTitle.isEmpty || enteredAmount <= 0) {
return;
}
widget.addTx(
enteredTitle,
enteredAmount,
enteredBrandName,
);
Navigator.of(context).pop();
}
void _presentDateTimePicker() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2019),
lastDate: DateTime.now(),
).then((pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
_selectedDate = pickedDate;
});
});
}
#override
Widget build(BuildContext context) {
return Card(
elevation: 5,
child: Container(
padding: EdgeInsets.all(10),
child: Column(
children: <Widget>[
TextField(
autocorrect: true,
decoration: InputDecoration(labelText: 'Title'),
controller: _titleController,
onSubmitted: (_) => _submitData,
//onChanged: (val) {
//titleInput = val;
//},
),
TextField(
autocorrect: true,
decoration: InputDecoration(labelText: 'Amount'),
controller: _amountController,
keyboardType: TextInputType.number,
onSubmitted: (_) => _submitData,
//onChanged: (val) => amountInput = val,
),
TextField(
autocorrect: true,
decoration: InputDecoration(labelText: 'Brand Name'),
controller: _brandNameControlller,
onSubmitted: (_) => _submitData,
//onChanged: (val) => brandInput = val,
),
Container(
height: 70,
child: Row(
children: <Widget>[
Text(_selectedDate == null
? 'No Date Chosen'
: 'Picked Date: ${DateFormat.yMd().format(_selectedDate)}'),
FlatButton(
onPressed: _presentDateTimePicker,
child: Text(
'Chose Date',
style: TextStyle(fontWeight: FontWeight.bold),
),
textColor: Theme.of(context).primaryColor,
),
],
),
),
RaisedButton(
onPressed: _submitData,
child: Text('Add Transaction'),
color: Theme.of(context).primaryColor,
textColor: Theme.of(context).textTheme.button!.color,
),
],
),
),
);
}
}

If the value is nullable just mark the variable as such DateTime? _selectedDate;
And whenever you would use it just check for null first:
Text(_selectedDate == null
? 'No Date Chosen'
: 'Picked Date: ${DateFormat.yMd().format(_selectedDate!)}'),
// Here you do know that the value can't be null so you assert to the compiler that
Check out the official docs on null safety they are extremely high quality and instructive.
There you can read about when to use nullable ? non nullable or late variables.

There is a work around in this situation
initialize the date with
DateTime _selectedDate = DateTime.parse('0000-00-00');
and later on in text widget
instead of
Text(_selectedDate == null
? 'No Date Chosen'
: 'Picked Date: ${DateFormat.yMd().format(_selectedDate!)}'),
use
Text(_selectedDate == DateTime.parse('0000-00-00')
? 'No Date Chosen'
: 'Picked Date: ${DateFormat.yMd().format(_selectedDate!)}'),

If you will change the value in the setState method, you can initialize the variable with
DateTime _selectedDate = DateTime.now();
and format it to the format you show the user, this would get rid of the error and if you are going to change it each time the user selects a date, seeing the current date as a starting value gives the user a starting point as a reference.

step1 : Put a question mark '?' after DateTime data type such as DateTime? _selectedDate;
Adding a question mark indicates to Dart that the variable can be null. I expect that initially the _selectedDate is not set, so Dart is giving an error warning due to null-safety. By putting a question mark in the type, you are saying you expect that the variable can be null (which is why it fixes the error).
step2 : whenever you would use it just check for null first by adding a null check (!)
After fixing errors your code is now :
//flutter imports
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:intl/intl.dart';
class NewTransaction extends StatefulWidget {
final Function addTx;
NewTransaction(this.addTx);
#override
_NewTransactionState createState() => _NewTransactionState();
}
class _NewTransactionState extends State<NewTransaction> {
final _titleController = TextEditingController();
final _amountController = TextEditingController();
final _brandNameControlller = TextEditingController();
DateTime? _selectedDate;
void _submitData() {
final enteredTitle = _titleController.text;
final enteredAmount = double.parse(_amountController.text);
final enteredBrandName = _brandNameControlller.text;
if (enteredTitle.isEmpty || enteredAmount <= 0) {
return;
}
widget.addTx(
enteredTitle,
enteredAmount,
enteredBrandName,
);
Navigator.of(context).pop();
}
void _presentDateTimePicker() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2019),
lastDate: DateTime.now(),
).then((pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
_selectedDate = pickedDate;
});
});
}
#override
Widget build(BuildContext context) {
return Card(
elevation: 5,
child: Container(
padding: EdgeInsets.all(10),
child: Column(
children: <Widget>[
TextField(
autocorrect: true,
decoration: InputDecoration(labelText: 'Title'),
controller: _titleController,
onSubmitted: (_) => _submitData,
//onChanged: (val) {
//titleInput = val;
//},
),
TextField(
autocorrect: true,
decoration: InputDecoration(labelText: 'Amount'),
controller: _amountController,
keyboardType: TextInputType.number,
onSubmitted: (_) => _submitData,
//onChanged: (val) => amountInput = val,
),
TextField(
autocorrect: true,
decoration: InputDecoration(labelText: 'Brand Name'),
controller: _brandNameControlller,
onSubmitted: (_) => _submitData,
//onChanged: (val) => brandInput = val,
),
Container(
height: 70,
child: Row(
children: <Widget>[
Text(_selectedDate == null
? 'No Date Chosen'
: 'Picked Date: ${DateFormat.yMd().format(_selectedDate!)}'),
FlatButton(
onPressed: _presentDateTimePicker,
child: Text(
'Chose Date',
style: TextStyle(fontWeight: FontWeight.bold),
),
textColor: Theme.of(context).primaryColor,
),
],
),
),
RaisedButton(
onPressed: _submitData,
child: Text('Add Transaction'),
color: Theme.of(context).primaryColor,
textColor: Theme.of(context).textTheme.button!.color,
),
],
),
),
);
}
}

Related

Closure call with mismatched arguments using DateTime

I'm a designer dipping my toes on programming by following a tutorial. It's supposed to be an app that saves and shows transactions made within the last week. But I'm getting this error message whenever I try to add a transaction on the app. It throws me this error:
════════ Exception caught by gesture ═══════════════════════════════════════════
Closure call with mismatched arguments: function '_MyHomePageState._addNewTransaction'
Receiver: Closure: (String, double) => void from Function '_addNewTransaction#17043806':.
Tried calling: _MyHomePageState._addNewTransaction("Wednesday", 555.0, Instance of 'DateTime')
Found: _MyHomePageState._addNewTransaction(String, double) => void
════════════════════════════════════════════════════════════════════════════════
In simple terms, how could I fix this?
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class NewTransaction extends StatefulWidget {
final Function addTx;
NewTransaction(this.addTx);
#override
State<NewTransaction> createState() => _NewTransactionState();
}
class _NewTransactionState extends State<NewTransaction> {
final _titleController = TextEditingController();
final _amountController = TextEditingController();
DateTime _selectedDate;
void _submitData() {
if (_amountController.text.isEmpty) {
return;
}
final enteredTitle = _titleController.text;
final enteredAmount = double.parse(_amountController.text);
if (enteredTitle.isEmpty || enteredAmount <= 0 || _selectedDate == null) {
return;
}
widget.addTx(
enteredTitle,
enteredAmount,
_selectedDate,
);
Navigator.of(context).pop();
}
void _presentDatePicker() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2023),
lastDate: DateTime.now(),
).then((pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
_selectedDate = pickedDate;
});
});
}
#override
Widget build(BuildContext context) {
return Card(
child: Container(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextField(
decoration: InputDecoration(labelText: 'Title'),
controller: _titleController,
onSubmitted: (_) => _submitData(),
),
TextField(
decoration: InputDecoration(labelText: 'Amount'),
controller: _amountController,
keyboardType: TextInputType.number,
onSubmitted: (_) => _submitData(),
),
Row(
children: [
Flexible(
fit: FlexFit.tight,
child: Text(
_selectedDate == null
? 'No Date Chosen'
: 'Picked Date: ${DateFormat.MEd().format(_selectedDate)}',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
TextButton(
child: Text(
'Choose Date',
style: TextStyle(fontWeight: FontWeight.bold),
),
onPressed: _presentDatePicker)
],
),
SizedBox(
height: 60,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor:
Theme.of(context).textTheme.labelLarge.color,
textStyle: TextStyle(fontWeight: FontWeight.w600)),
onPressed: _submitData,
child: Text('Add transaction'),
),
)
],
),
),
);
}
}
What I've tried and sort of "worked"
It seems to run when I delete _selectedDate from the widget.addTx but without it, it won't save the transaction with the correct date.
What is supposed to happen
The problem occurs when I tap the Add Transaction button, it's supposed to save the transaction and show it on a list. But I can't seem to actually add a transaction since I've implemented the selectedDate parameter.
The error is due to a mismatch between how the addTx function is called and how the function passed in is actually defined. The number and/or types of arguments do not match.
Since you have not provided an example of how NewTransaction() is used, the best way to get to the bottom is to declare addTx more precisely.
Instead of
final Function addTx;
Declare it as
final void Function(String, double, DateTime) addTx;
Then you should see a static type-checking error at any location that passes in the incorrect function type.
See also PREFER signatures in function type annotations.

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();
}

How to disable Flutter DatePicker?

I have an issue with DatePicker in my application. Here's a simple TextFormField that I've created in my app which will open up DatePicker whenever the user taps on it.
This widget is a part of a form where I also have specified the GlobalKey and TextController for it. The rightmost calendar icon uses the suffixIcon property of InputDecoration and it changes to a clear icon whenever the user selects a date.
Here's the code for the above widget.
TextFormField(
onTap: () => _selectStartDate(context),
controller: _startDateTextController,
keyboardType: TextInputType.datetime,
readOnly: true,
decoration: InputDecoration(
suffixIcon: showClear ? IconButton(
icon: Icon(Icons.clear),
onPressed: _clearStartDate,
) : Icon(Icons.date_range),
labelText: 'Start Date',
labelStyle: TextStyle(
fontSize: AppDimensions.font26,
color: AppColors.paraColor
),
),
validator: (value){
if(value!.isEmpty) {
return 'Please enter a date';
}
},
),
My goal is to let the user pick on a date, and clear it should they choose to do so.
Here's the code for the _selectStartDate and _clearStartDate functions as well as the necessary controller and key. I'm using the intl package to format the date.
final _formKey = GlobalKey<FormState>();
TextEditingController _startDateTextController = TextEditingController();
DateTime selectedStartDate = DateTime.now();
bool showClear = false;
_selectStartDate(BuildContext context) async {
final DateTime? newStartDate = await showDatePicker(
context: context,
initialDate: selectedStartDate,
firstDate: DateTime(1900),
lastDate: DateTime(2100),
helpText: 'STARTING DATE'
);
if(newStartDate != null && newStartDate != selectedStartDate) {
setState(() {
selectedStartDate = newStartDate;
_startDateTextController.text = DateFormat.yMMMd().format(selectedStartDate);
showClear = true;
});
}
}
_clearStartDate() {
_startDateTextController.clear();
setState(() {
showClear = !showClear;
});
}
When i run the app, the DatePicker pops up and I'm able to select a date. The date is then shown on the TextFormField like the image below.
As you can see the clear icon is displayed. However, when i clicked on it, the DatePicker still popped up. And when i clicked on cancel on the DatePicker window, the TextFormField is cleared as expected.
Here's the complete code.
class BookingForm extends StatefulWidget {
const BookingForm({Key? key}) : super(key: key);
#override
_BookingFormState createState() => _BookingFormState();
}
class _BookingFormState extends State<BookingForm> {
final _formKey = GlobalKey<FormState>();
TextEditingController _startDateTextController = TextEditingController();
DateTime selectedStartDate = DateTime.now();
bool showClear = false;
_selectStartDate(BuildContext context) async {
final DateTime? newStartDate = await showDatePicker(
context: context,
initialDate: selectedStartDate,
firstDate: DateTime(1900),
lastDate: DateTime(2100),
helpText: 'STARTING DATE'
);
if(newStartDate != null && newStartDate != selectedStartDate) {
setState(() {
selectedStartDate = newStartDate;
_startDateTextController.text = DateFormat.yMMMd().format(selectedStartDate);
showClear = true;
});
}
}
_clearStartDate() {
_startDateTextController.clear();
setState(() {
showClear = !showClear;
});
}
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Container(
margin: EdgeInsets.only(left: AppDimensions.width20, right: AppDimensions.width20),
padding: EdgeInsets.all(AppDimensions.height20),
decoration: BoxDecoration(
color: Colors.white70,
borderRadius: BorderRadius.circular(AppDimensions.radius20),
boxShadow: [
BoxShadow(
color: Color(0xFFe8e8e8),
blurRadius: 5.0,
spreadRadius: 1.0,
offset: Offset(2,2)
),
]
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
TextFormField(
onTap: () => _selectStartDate(context),
controller: _startDateTextController,
keyboardType: TextInputType.datetime,
readOnly: true,
decoration: InputDecoration(
suffixIcon: showClear ? IconButton(
icon: Icon(Icons.clear),
onPressed: _clearStartDate,
) : Icon(Icons.date_range),
labelText: 'Start Date',
labelStyle: TextStyle(
fontSize: AppDimensions.font26,
color: AppColors.paraColor
),
),
validator: (value){
if(value!.isEmpty) {
return 'Please enter a date';
}
},
),
ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.only(
top: AppDimensions.height10,
bottom: AppDimensions.height10,
left: AppDimensions.width45,
right: AppDimensions.width45
),
primary: AppColors.mainColor2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppDimensions.radius20)
)
),
child: SmallText(
text: 'Search',
size: AppDimensions.font26,
color: Colors.white,
),
onPressed: (){
if(_formKey.currentState!.validate()) {
_formKey.currentState!.save();
}
}
),
],
),
),
);
}
}
I'm not sure why this happens. Any help is greatly appreciated. Thank you

how to make a button disable in flutter

I'am trying to create a profile form . I want to disable or enable the button based on the validation. I tried to check the currentstate of form validated or not. it is working fine but when i type on one of the textFormField , I am getting error message on all of the textFormField .
Here is the code i'am working.
import 'dart:html';
import 'package:flutter/material.dart';
class ProfileForm extends StatefulWidget {
const ProfileForm({Key? key}) : super(key: key);
#override
State<ProfileForm> createState() => _ProfileFormState();
}
class _ProfileFormState extends State<ProfileForm> {
final _formKey = GlobalKey<FormState>();
DateTime selectedDate = DateTime.now();
final _nameController = TextEditingController();
final _emailController = TextEditingController();
final _dobController = TextEditingController();
final _currentLocationController = TextEditingController();
final _locationIndiaController = TextEditingController();
bool btndisable = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
RegExp emailValidator = RegExp(
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,}))$');
return Scaffold(
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
onChanged: () {
notdisbled();
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Let’s create a profile for you",
style: AppTextStyleSecondary.headline2sec,
),
const SizedBox(
height: 12,
),
Padding(
padding: const EdgeInsets.only(right: 100.0),
child: Text(
"Please enter your personal details",
),
),
const SizedBox(
height: 16,
),
Text("Your name"),
TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
onChanged: (val) {
},
validator: (val) {
if (val.toString().length < 2) {
return "Please enter your name";
}
return null;
},
controller: _nameController,
decoration: const InputDecoration(
isDense: true,
contentPadding:
EdgeInsets.symmetric(horizontal: 10, vertical: 12)),
),
Text(labelText: "Date of Birth"),
InkWell(
child: TextFormField(
onTap: () {
_selectDate(context);
},
validator: (val) {
if (val.toString().isEmpty) {
return "This field is required";
} else {
return null;
}
},
readOnly: true,
controller: _dobController,
),
),
Text("Email address"),
TextFormField(
controller: _emailController,
onChanged: (val) {},
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (val) {
String email = val!.trim();
if (emailValidator.hasMatch(email)) {
return null;
} else {
return "Enter valid email address";
}
},
decoration: const InputDecoration(
isDense: true,
contentPadding:
EdgeInsets.symmetric(horizontal: 10, vertical: 12)),
),
Text(labelText: "Your location where you currently live"),
TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (val) {
if (val.toString().isEmpty) {
return "This field is required";
} else {
return null;
}
},
onChanged: (val) {
},
controller: _currentLocationController,
),
Text("Your location in India"),
TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (val) {
if (val.toString().isEmpty) {
return "This field is required";
} else {
return null;
}
},
onChanged: (val) {},
controller: _locationIndiaController,
),
const SizedBox(
height: 25,
),
Container(
width: double.infinity,
height: 45,
decoration: BoxDecoration(
color: btndisable ? Colors.blue : Colors.red,
borderRadius: BorderRadius.circular(8),
),
child: TextButton(
onPressed: (){
if(_formKey.currentState!.validate()){
Navigator.of(context).pushNamedAndRemoveUntil(
'HomeScreen', (Route<dynamic> route) => false);
}
}
child: Text("Continue"),
),
),
const SizedBox(
height: 20,
),
],
),
),
)),
);
}
Future<void> _selectDate(BuildContext context) async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(1970),
lastDate: DateTime(2101));
if (picked != null && picked != selectedDate) {
setState(() {
selectedDate = picked;
_dobController.text =
"${selectedDate.day}/${selectedDate.month}/${selectedDate.year}";
});
}
}
void notdisbled() {
if (_formKey.currentState!.validate() == false) {
setState(() {
btndisable = false;
});
} else {
setState(() {
btndisable = true;
});
}
}
}
for disable the button change to this :
child: TextButton(
onPressed: _formKey.currentState != null && _formKey.currentState.validate() ? () {
Navigator.of(context).pushNamedAndRemoveUntil(
'HomeScreen', (Route<dynamic> route) => false);
}: null,
child: AppText(
text: "Continue",
fontSize: 16,
fontWeight: FontWeight.bold,
color: AppColor.white),
),
but when you use
formKey.currentState!.validate()
when you type any thing, flutter try call it every time so your form widget run all validator func in all its textfield. so it is natural for all textfield to get error message

TextField values are dissapeared?

I started learning flutter recently. In my app window when I push a button a bottom sheet modal will appear like in the below image. When I typing in the 'Amount' text field, the previously typed 'Title' text filed content is disappeared. Why is this happen and how to solve this?
This my code related to read text field inputs.
import 'dart:io';
import './adaptive_flat_button.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class NewTransaction extends StatelessWidget {
final Function newTransactionHandler;
final titleController = TextEditingController();
final amountController = TextEditingController();
DateTime selectedDate;
NewTransaction(this.newTransactionHandler);
void addTx() {
if (amountController.text.isEmpty) {
return;
}
String titileTxt = titleController.text;
double amount = double.parse(amountController.text);
if (titileTxt.isEmpty || amount <= 0 || selectedDate == null) {
return;
}
newTransactionHandler(titileTxt, amount, selectedDate);
Navigator.of(context).pop();
}
void presentDatePicker() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2019),
lastDate: DateTime.now())
.then((pickedDate) {
if (pickedDate == null) {
return;
}
selectedDate = pickedDate;
});
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Card(
elevation: 5,
child: Container(
padding: EdgeInsets.only(
top: 10,
left: 10,
right: 10,
bottom: MediaQuery.of(context).viewInsets.bottom + 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
TextField(
decoration: InputDecoration(labelText: 'Title'),
controller: titleController,
onSubmitted: (_) => addTx(),
// onChanged: (val) {
// titleInput = val;
// },
),
TextField(
decoration: InputDecoration(labelText: 'Amount'),
controller: amountController,
keyboardType: TextInputType.phone,
onSubmitted: (_) => addTx(),
// onChanged: (val) {
// amountInput = val;
// },
),
Container(
height: 70,
child: Row(
children: [
Text(selectedDate == null
? 'No date chosen!'
: 'PickedDate: ${DateFormat.yMd().format(selectedDate)}'),
AdaptiveFlatButton('Choose Date', presentDatePicker),
],
),
),
RaisedButton(
color: Theme.of(context).primaryColor,
textColor: Theme.of(context).textTheme.button.color,
child: Text(
'Add Transaction',
),
onPressed: addTx,
)
],
),
),
),
);
}
}
I solved this issue by just replacing StatelessWidget with StatefulWidget.
import 'dart:io';
import './adaptive_flat_button.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class NewTransaction extends StatefulWidget {
final Function newTransactionHandler;
NewTransaction(this.newTransactionHandler) {
print('Constructor NewTRansaction');
}
#override
_NewTransactionState createState() {
print('CreateState NewTransaction');
return _NewTransactionState();
}
}
class _NewTransactionState extends State<NewTransaction> {
final _titleController = TextEditingController();
final _amountController = TextEditingController();
DateTime selectedDate;
_NewTransactionState() {
print('Constructor NewTransaction State');
}
#override
void initState() {
// TODO: implement initState
print('initState()');
super.initState();
}
#override
void didUpdateWidget(covariant NewTransaction oldWidget) {
// TODO: implement didUpdateWidget
print('didUpdate()');
super.didUpdateWidget(oldWidget);
}
#override
void dispose() {
// TODO: implement dispose
print('dispose()');
super.dispose();
}
void _addTx() {
if (_amountController.text.isEmpty) {
return;
}
String titileTxt = _titleController.text;
double amount = double.parse(_amountController.text);
if (titileTxt.isEmpty || amount <= 0 || selectedDate == null) {
return;
}
widget.newTransactionHandler(titileTxt, amount, selectedDate);
Navigator.of(context).pop();
}
void _presentDatePicker() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2019),
lastDate: DateTime.now())
.then((pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
selectedDate = pickedDate;
});
});
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Card(
elevation: 5,
child: Container(
padding: EdgeInsets.only(
top: 10,
left: 10,
right: 10,
bottom: MediaQuery.of(context).viewInsets.bottom + 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
TextField(
decoration: InputDecoration(labelText: 'Title'),
controller: _titleController,
onSubmitted: (_) => _addTx(),
// onChanged: (val) {
// titleInput = val;
// },
),
TextField(
decoration: InputDecoration(labelText: 'Amount'),
controller: _amountController,
keyboardType: TextInputType.phone,
onSubmitted: (_) => _addTx(),
// onChanged: (val) {
// amountInput = val;
// },
),
Container(
height: 70,
child: Row(
children: [
Text(selectedDate == null
? 'No date chosen!'
: 'PickedDate: ${DateFormat.yMd().format(selectedDate)}'),
AdaptiveFlatButton('Choose Date', _presentDatePicker),
],
),
),
RaisedButton(
color: Theme.of(context).primaryColor,
textColor: Theme.of(context).textTheme.button.color,
child: Text(
'Add Transaction',
),
onPressed: _addTx,
)
],
),
),
),
);
}
}
You can't use setState in Stateless Widget
Just refractor with right click on StatelessWidget, select refractor then select Convert to StatefulWidget