I have a Future (async) Date&Time picker function which works fine from within the body of my stateful widget which contains the "Builder" and the function can be called via the onpressed by just this:
onPressed: () {SelectDayAndTimeL();},
Code:
Future _selectDayAndTimeL(BuildContext context) async {
DateTime _selectedDay = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2021),
lastDate: DateTime(2030),
builder: (BuildContext context, Widget child) => child);
TimeOfDay _selectedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
if (_selectedDay != null && _selectedTime != null) {
//a little check
}
setState(() {
selectedDateAndTime = DateTime(
_selectedDay.year,
_selectedDay.month,
_selectedDay.day,
_selectedTime.hour,
_selectedTime.minute,
);
// _selectedDate = _selectedDay;
});
// print('...');
}
Now I want to be able to call this function from different dart files/screens which means I have to keep this function on a different dart file which I have tried to do, but because of the setState in the function it needs to be inside a stateful widget. I have tried putting it inside a stateful widget but keeps getting errors.
class Picker extends StatefulWidget {
#override
_PickerState createState() => _PickerState();
}
class _PickerState extends State<Picker> {
#override
Future<Widget> build(BuildContext context) async { //the error in on the build on this line
DateTime _selectedDay = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2021),
lastDate: DateTime(2030),
builder: (BuildContext context, Widget child) => child);
TimeOfDay _selectedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
if (_selectedDay != null && _selectedTime != null) {
//a little check
}
setState(() {
selectedDateAndTime = DateTime(
_selectedDay.year,
_selectedDay.month,
_selectedDay.day,
_selectedTime.hour,
_selectedTime.minute,
);
// _selectedDate = _selectedDay;
});
}
}
How do I properly place the Future Function inside a stateful widget and how to call it on an onpressed?
I don't know if the title I gave this question is actually what it's supposed to be, but I don't know how else to put it.
The whole setState inside your method is the problem. Your method should do one thing: get a date and time from the user. And there it's responsibility ends.
Future<DateTime> SelectDayAndTimeL(BuildContext context) async {
DateTime _selectedDay = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2021),
lastDate: DateTime(2030),
builder: (BuildContext context, Widget child) => child);
TimeOfDay _selectedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
if (_selectedDay != null && _selectedTime != null) {
//a little check
}
return DateTime(
_selectedDay.year,
_selectedDay.month,
_selectedDay.day,
_selectedTime.hour,
_selectedTime.minute,
);
}
Now your onPressed becomes:
onPressed: () async {
final pickedDatetime = await SelectDayAndTimeL(context);
setState(() { selectedDateAndTime = pickedDatetime });
},
You have sucessfully divided your code into the function that picks a thing and your widget, which updates after the thing is picked.
The function that picks the date and time can now be reused in every other widget.
Create a function which will take datetime as a parameter and setstate of the stateful widget. Write this inside the stateful widget. Pass this function to the other class as an argument. Once the date is picked call this method by passing the datetime selected.
You should pass the setState function itself as a parameter to the method. This way, inside the method you will always be using the correct state setter function. That is especially necessary since you need to keep the variables _selectedDay etc inside the widget, not on the static method. Try this:
Future selectDayAndTimeL(BuildContext context, Function(DateTime) dateTimeSetter) async { //add a function to receive and use the
DateTime _selectedDay = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2021),
lastDate: DateTime(2030),
builder: (BuildContext context, Widget child) => child);
TimeOfDay _selectedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
if (_selectedDay != null && _selectedTime != null) {
//a little check
}
// Create the variable with the picked date
DateTime selectedDateAndTime = DateTime(
_selectedDay.year,
_selectedDay.month,
_selectedDay.day,
_selectedTime.hour,
_selectedTime.minute,
);
//call the function from the parameter, which will be executed on the calling widget
dateTimeSetter(selectedDateAndTime);
....
}
Then, call your function to show the datetime picker passing the context, and a function that receives a DateTime parameter, which will be the parameter picked by the user. When the user picks a date, this function body will be executed, calling setState and setting the pickedTime variable.
class Picker extends StatefulWidget {
DateTime pickedTime;
#override
_PickerState createState() => _PickerState();
}
class _PickerState extends State<Picker> {
#override
Future<Widget> build(BuildContext context) async {
return ...
_selectDayAndTimeL(context, (DateTime time){
setState((){
widget.pickedTime = time;
});
});
...
You can also extract the function from the parameter into a normal named function, and just use its name in the parameter, but I'll leave it as is for now to make it simpler.
Also, don't forget to make your method public and static if necessary for the scope of your code.
I am trying to use the DatePicker in flutter and I want to set the initialDate to DateTime.now() and the lastDate I want to set to DateTime.now() + 20yrs so it increments the year dynamically.
Current I have my lastDate set to the year (2100), but this would cause a problem when the year 2101 reaches. So how can I modify my function to increase the lastDate dynamically?
Here is my function:
DateTime _date = DateTime.now();
Future < Null > _checkInDate(BuildContext contex) async {
DateTime ? _datePicker = await showDatePicker(
context: context,
builder: (BuildContext context, Widget ? child) {
return Theme(
data: ThemeData(
primaryColor: Color(0xFFEF5350),
colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.red)
.copyWith(secondary: Color(0xFFFFF176)),
),
child: child ? ? Text(""),
);
},
initialDate: _date,
firstDate: DateTime.now(),
lastDate: DateTime(2100), //how can i increase the lastDate dynamically?
);
if (_datePicker != null && _datePicker != _date) {
_checkInController.text = DateFormat('dd-MM-yyyy').format(_datePicker);
}
}
Yes you can, use DateTime.now().year to get the current year and then add as many years as you want. Like this:
final date = DateTime(DateTime.now().year + 20);
I'm trying to let the user select only a certain time into business-hours (using 24H format), but I don't know how to achieve this. Is there a way to do it? I tried this plugin https://pub.dev/packages/time_picker_widget but it's not available in 24H format and sometimes it doesn't let you select a "correct" time
you need to use alwaysUse24HourFormat in MediaQuery like this
void inputTimeSelect() async {
final TimeOfDay picked = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
builder: (BuildContext context, Widget child) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true),
child: child,
);
},
);
}
I need to get a time from a timepicker and show it on a text widget.
Here you have the widget that should show the time:
pickedTime = TimeOfDay.now();
ListTile(
title: Text(
" ${pickedTime.hour}:${pickedTime.minute}"),
trailing: Icon(Icons.timer,size: 45,),
onTap: _pickTime,
),
And here you have the function _pickTime:
_pickTime() async{
TimeOfDay time = await showTimePicker(
context: context,
initialTime: pickedTime);
setState(() {
pickedTime = time;
});
}
I have detected an issue when the picked time hour or minute is smaller than 10, the output is as shown in the picture for 04:05:
I would like to show the picked time always in format HH:mm.
The following is an extension on the int class that you can use to enforce two characters for each part of the time:
extension TwoChar on int {
String toTwoChars() {
return this.toString().padLeft(2, '0');
}
}
Then modify your code to use the extension:
title: Text("${pickedTime.hour.toTwoChars()}:${pickedTime.minute.toTwoChars()}"),
I want the user to pick a date using datePicker widget, when the user finish picking a date, a timePicker widget should appear and do the same thing. After that an item will be added to a list which uses the picked date & time. But what really happens is that everything shows up at the same time.
onPressed: () {
var date = DateTime.now();
showMaterialDatePicker(
context: context,
selectedDate: date,
onChanged: (value) => setState(() => date = value),
);
//Should wait until DatePicker finish
var time = TimeOfDay.now();
showMaterialTimePicker(
context: context,
selectedTime: time,
onChanged: (value) => setState(() => time = value),
);
//Should wait until timePicker finish, to add the item to the list
this.widget.litems.add(Reminder(
time: time,
date: date,
));
setState((){});
}
I suggest using the showDatePicker & showTimePicker. As per the documentation showDatePicker and showTimePicker returns Future. You can simply await these for the desired outcome.
onPressed: () async {
var date = DateTime.now();
date = await showDatePicker(
context: context,
initialDate: date,
);
//Should wait until DatePicker finish
var time = TimeOfDay.now();
time = await showTimePicker(
context: context,
initialTime: time,
);
//Should wait until timePicker finish, to add the item to the list
this.widget.litems.add(Reminder(
time: time,
date: date,
));
setState((){});
}
If you don't want to use these, then you should add showMaterialDatePicker & showMaterialTimePicker to separate functions which returns a Future and handle the logic inside those functions.