Late Initialization Error in Flutter because of _startDate - flutter

As you can see in the included screenshot, I am getting a LateInitializationError upon running my app. The cause is in the code below, but I can't figure out how to fix it. It certainly has to do with the "late DateTime _startDate;" that I am using, but unsure what the right approach is. Do you have any idea? Thanks in advance for looking into it!
class AddEventPage extends StatefulWidget {
final DateTime? selectedDate;
final AppEvent? event;
const AddEventPage({Key? key, this.selectedDate, this.event})
: super(key: key);
#override
_AddEventPageState createState() => _AddEventPageState();
}
late DateTime _startDate;
late TimeOfDay _startTime;
late DateTime _endDate;
late TimeOfDay _endTime;
class _AddEventPageState extends State<AddEventPage> {
final _formKey = GlobalKey<FormBuilderState>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
leading: IconButton(
icon: Icon(
Icons.clear,
color: AppColors.primaryColor,
),
onPressed: () {
Navigator.pop(context);
},
),
actions: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () async {
//save
_formKey.currentState!.save();
final data =
Map<String, dynamic>.from(_formKey.currentState!.value);
data["Time Start"] =
(data["Time Start"] as DateTime).millisecondsSinceEpoch;
if (widget.event != null) {
//update
await eventDBS.updateData(widget.event!.id!, data);
} else {
//create
await eventDBS.create({
...data,
"user_id": context.read(userRepoProvider).user!.id,
});
}
Navigator.pop(context);
},
child: Text("Save"),
),
)
],
),
body: ListView(
padding: const EdgeInsets.all(16.0),
children: <Widget>[
//add event form
FormBuilder(
key: _formKey,
child: Column(
children: [
FormBuilderTextField(
name: "title",
initialValue: widget.event?.title,
decoration: InputDecoration(
hintText: "Add Title",
border: InputBorder.none,
contentPadding: const EdgeInsets.only(left: 48.0)),
),
Divider(),
FormBuilderTextField(
name: "description",
initialValue: widget.event?.description,
minLines: 1,
maxLines: 5,
decoration: InputDecoration(
hintText: "Add Details",
border: InputBorder.none,
prefixIcon: Icon(Icons.short_text)),
),
Divider(),
FormBuilderSwitch(
name: "public",
initialValue: widget.event?.public ?? false,
title: Text("Public"),
controlAffinity: ListTileControlAffinity.leading,
decoration: InputDecoration(
border: InputBorder.none,
),
),
Divider(),
Neumorphic(
style: NeumorphicStyle(color: Colors.white),
child: Column(
children: [
GestureDetector(
child: Text(
DateFormat('EEE, MMM dd, yyyy')
.format(_startDate),
textAlign: TextAlign.left),
onTap: () async {
final DateTime? date = await showDatePicker(
context: context,
initialDate: _startDate,
firstDate: DateTime(2000),
lastDate: DateTime(2100),
);
if (date != null && date != _startDate) {
setState(() {
final Duration difference =
_endDate.difference(_startDate);
_startDate = DateTime(
date.year,
date.month,
date.day,
_startTime.hour,
_startTime.minute,
0);
_endDate = _startDate.add(difference);
_endTime = TimeOfDay(
hour: _endDate.hour,
minute: _endDate.minute);
});
}
}),
Container(
child: FormBuilderDateTimePicker(
name: "Time End",
initialValue: widget.selectedDate ??
widget.event?.date ??
DateTime.now(),
initialDate: DateTime.now(),
fieldHintText: "Add Date",
initialDatePickerMode: DatePickerMode.day,
inputType: InputType.both,
format: DateFormat('EEE, dd MMM, yyyy HH:mm'),
decoration: InputDecoration(
border: InputBorder.none,
prefix: Text(' '),
),
),
),
],
),
),
],
),
),
],
),
);
}
}

late to the keyword means that your property will be initialized when you use it for the first time.
You like to initialize like this:
late DateTime _startDate = DateTime.now();
And as well as change the others value respectively

In GestureDetector you are using a Text widget and passing the _startDate as value but you have not assigned any value to it beforehand, this causes this error, try giving it an initial value before using it.

You can use the following code as well :
DateTime? _startDate;

I have exactly problem.
Some objects should not be initialized directly, hence the creation of late.
For example I don't want to initialize a File object at creation, but afterwards I use late but flutter returns an error.strong text.
So run: flutter run --release

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

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

StateProvider not updating with correct values

I've been trying to use riverpod for state management in a POC app, I have a screen with a text editing controller, I'm trying to check whether the text is empty for a textfield and enable/disable a button based on that logic. There seems to be an issue with my code since the button seems to always display the opposite status of what I'm trying to do. How can I fix this issue ?
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:to_do_list/models/task.dart';
import 'package:to_do_list/reusable_widgets/app_bar.dart';
class TaskScreen extends ConsumerStatefulWidget {
const TaskScreen({Key? key}) : super(key: key);
#override
TaskScreenState createState() => TaskScreenState();
}
class TaskScreenState extends ConsumerState<TaskScreen> {
DateTime? _selectedDate;
static final TextEditingController _titleController = TextEditingController();
final TextEditingController _descriptionController = TextEditingController();
var textProvider = StateProvider((_) => _titleController.text.isNotEmpty);
#override
Widget build(BuildContext context) {
bool value = ref.watch(textProvider);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: const AppBarWrapper(
title: "To-Do List",
),
body: Stack(children: [
Padding(
padding: const EdgeInsets.all(14.0),
child: Column(
children: [
TextField(
controller: _titleController,
onChanged: (val) {
ref.read(textProvider.state).state = val.isEmpty;
},
autofocus: true,
decoration: const InputDecoration(hintText: "Title"),
),
const SizedBox(
height: 14,
),
TextField(
controller: _descriptionController,
decoration: const InputDecoration(hintText: "Description"),
minLines: 3,
maxLines: 5,
),
SizedBox(
width: 150,
child: ListTile(
leading: const Icon(Icons.calendar_today),
title: Text(
_selectedDate?.toString() ?? 'No Date',
),
onTap: () async {
_selectedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime.now(),
lastDate: DateTime.now().add(const Duration(days: 365)),
);
},
),
),
],
),
),
Positioned(
bottom: MediaQuery.of(context).viewInsets.bottom,
left: 0,
right: 0,
child: ElevatedButton(
onPressed: value
? () {
Navigator.of(context).pop(Task(
title: _titleController.text,
description: _descriptionController.text.isNotEmpty
? _descriptionController.text
: null,
date: _selectedDate));
}
: null,
child: const Icon(
Icons.done,
),
),
),
]),
);
}
}
Try to change ref.read(textProvider.state).state = val.isEmpty; to ref.read(textProvider.notifier).state = val.isEmpty;. It should help you

Non-nullable instance field '_selectedDate' must be initialized

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,
),
],
),
),
);
}
}

getting error " getter 'year' was called on null " when clearing textfield in flutter

here when I click on the clear button it will not clear the all the textbox only clearing the expiry_date field only and also showing me the exception "getter year was called on null".
in this code I have a clear button when I click on that button it will not clearing the button and showing year called on null. I think this problem happens because I've added onChanged in ExpiryDate widget. but I need an onChnaged event. so I need to clear everything. Hope you understand the question. Your help can make my day.
Here is the code :
class _BspLicensedSignupPageState extends State<BspLicensedSignupPage>
with AfterLayoutMixin<BspLicensedSignupPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _bspBusinessLicenseNumber =
TextEditingController();
final TextEditingController _bspLicenseAuthorityName =
TextEditingController();
final TextEditingController _bspLicenseExpiryDate = TextEditingController();
final format = new DateFormat.yMMMEd('en-US');
String _isoDate;
List<BusinessProfilePicture> _bspLicenseImages =
new List<BusinessProfilePicture>();
List<Object> images = List<Object>();
List<dynamic> _bspLicenseAuthorityTypes = <dynamic>[];
Map<String, dynamic> _bspLicenseAuthorityType;
bool _isvisibleissuingauthority = false;
bool businesslicensecheck = false;
int radioValue = -1;
Future<File> licenceimage;
Future<File> profilepicture;
DateTime date;
TimeOfDay time;
String _countryId;
BSPSignupRepository _bspSignupRepository = new BSPSignupRepository();
bool autovalidate = false;
bool _isEditMode = false;
int selected = 0;
List<String> _imageFilesList = [];
var isUploadingPost = false;
var isEditInitialised = true;
List<File> _licenseImages = [];
#override
void initState() {
super.initState();
_scrollController = ScrollController();
}
Widget _buildbusinesslicenseno() {
return new TudoTextWidget(
prefixIcon: Icon(FontAwesomeIcons.idCard),
controller: _bspBusinessLicenseNumber,
labelText: AppConstantsValue.appConst['licensedsignup']
['businesslicenseno']['translation'],
validator: Validators().validateLicenseno,
);
}
Widget _buildexpirydate() {
return DateTimeField(
format: format,
autocorrect: true,
autovalidate: false,
controller: _bspLicenseExpiryDate,
readOnly: true,
validator: (date) => (date == null || _bspLicenseExpiryDate.text == '')
? 'Please enter valid date'
: null,
onChanged: (date) {
_isoDate = DateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(date);
},
decoration: InputDecoration(
labelText: "Expiry Date",
hintText: "Expiry Date",
prefixIcon: Icon(
FontAwesomeIcons.calendar,
size: 24,
)),
onShowPicker: (context, currentValue) {
return showDatePicker(
context: context,
firstDate: DateTime.now(),
initialDate: currentValue ?? DateTime.now(),
lastDate: DateTime(2100),
);
},
);
}
Widget _buildlicenseauthority() {
return new TudoTextWidget(
validator: (val) => Validators.validateName(val, "Issuing Authority"),
controller: _bspLicenseAuthorityName,
prefixIcon: Icon(Icons.account_circle),
labelText: AppConstantsValue.appConst['licensedsignup']
['licenseissuingauthority']['translation'],
hintText: AppConstantsValue.appConst['licensedsignup']
['licenseissuingauthority']['translation'],
);
}
Widget _buildlabeluploadlicensepicture() {
return Text(AppConstantsValue.appConst['licensedsignup']
['labeluploadlicenpicture']['translation']);
}
Widget _buildlegalbusinesscheck() {
return TudoConditionWidget(
text: AppConstantsValue.appConst['licensedsignup']['legalbusinesscheck']
['translation'],
);
}
Widget content(
BuildContext context, BspLicensedSignupViewModel bspLicensedSignupVm) {
final appBar = AppBar(
centerTitle: true,
title: Text("BSP Licensed Details"),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
NavigationHelper.navigatetoBack(context);
},
),
);
final bottomNavigationBar = Container(
height: 56,
//margin: EdgeInsets.symmetric(vertical: 24, horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new FlatButton.icon(
icon: Icon(Icons.close),
label: Text('Clear'),
color: Colors.redAccent,
textColor: Colors.black,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
// onpress
onPressed: () {
_formKey.currentState.reset();
_bspBusinessLicenseNumber.clear();
_bspLicenseAuthorityName.clear();
_bspBusinessLicenseNumber.text = '';
_bspLicenseAuthorityName.text = '';
setState(() {
_licenseImages.clear();
_imageFilesList.clear();
});
},
),
new FlatButton.icon(
icon: Icon(FontAwesomeIcons.arrowCircleRight),
label: Text('Next'),
color: colorStyles["primary"],
textColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () async {
},
),
],
),
);
return new Scaffold(
appBar: appBar,
bottomNavigationBar: bottomNavigationBar,
body: Container(
height: double.infinity,
width: double.infinity,
child: Stack(
children: <Widget>[
// Background(),
SingleChildScrollView(
controller: _scrollController,
child: SafeArea(
top: false,
bottom: false,
child: Form(
autovalidate: autovalidate,
key: _formKey,
child: Scrollbar(
child: SingleChildScrollView(
dragStartBehavior: DragStartBehavior.down,
// padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: new Container(
margin: EdgeInsets.fromLTRB(30, 30, 30, 0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildbusinesslicenseno(),
_buildexpirydate(),
_buildlegalbusinesscheck()
],
),
),
),
),
),
),
),
],
),
),
);
}
#override
Widget build(BuildContext context) {
return new StoreConnector<AppState, BspLicensedSignupViewModel>(
converter: (Store<AppState> store) =>
BspLicensedSignupViewModel.fromStore(store),
onInit: (Store<AppState> store) {
_countryId = store.state.auth.loginUser.user.country.id.toString();
print('_countryId');
print(_countryId);
},
builder: (BuildContext context,
BspLicensedSignupViewModel bspLicensedSignupVm) =>
content(context, bspLicensedSignupVm),
);
}
}
}
The formatter in the onChanged can't format a null date.
So you can add a null check in your DateTimeField's onChanged method.
Like this:
onChanged: (date) {
if (date != null){
_isoDate = DateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(date);
}
}