I have two separate buttons, one to select a date and one to select a time. How am I able to select both the date and then time by only having a single button?
For example, one the click of a "Schedule" button, a Datepicker will popup. Once the user selects a date and clicks "OK" in the Datepicker, the Timepicker will be called or popup.
This is the code for my time and date button widgets:
DateTime _date = new DateTime.now();
TimeOfDay _time = new TimeOfDay.now();
Future<Null> _selectDate(BuildContext context) async {
final DateTime picked = await showDatePicker(
context: context,
initialDate: _date,
firstDate: new DateTime(2019),
lastDate: new DateTime(2021),
);
if(picked != null && picked != _date) {
print('Date selected: ${_date.toString()}');
setState((){
_date = picked;
});
}
}
Future<Null> _selectTime(BuildContext context) async {
final TimeOfDay picked = await showTimePicker(
context: context,
initialTime: _time,
);
if(picked != null && picked != _time) {
print('Time selected: ${_time.toString()}');
setState((){
_time = picked;
});
}
}
Widgets:
final buttonRow = new Wrap(children: <Widget>[
new RaisedButton(
child: new Text('Select Date'),
onPressed: (){_selectDate(context);}
),
new RaisedButton(
child: new Text('Select Time'),
onPressed: (){_selectTime(context);}
)
]);
RaisedButton(
child: new Text('Select Date and Time'),
onPressed: (){ _selectDateAndTime(context); }
)
/* ... */
Future<Null> _selectDateAndTime(BuildContext context) async {
await _selectDate(context);
await _selectTime(context);
}
Related
The code below contains the EditableDateTime widget unit test as well as the EditableDateTime stateless widget class definition. The EditableDateTime widget uses the Flutter Date and Time Picker.
Using the EditableDateTime widget on a virtual Android smartphone is shown here:
My question is how can I test selecting both a date and a time value ? Currently, the testing code tests selecting a day only as well as selecting a month and a day. But trying to test selecting a time as well like shown on the picture was not possible for me, reason why I ask the question.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:intl/intl.dart';
/// EditableDateTime widget test code
Future<void> main() async {
final Finder previousMonthIcon = find.byWidgetPredicate((Widget w) =>
w is IconButton && (w.tooltip?.startsWith('Previous month') ?? false));
TextEditingController dateTimePickerController = TextEditingController();
group(
'EditableDateTime widget testing',
() {
testWidgets(
'Setting date day only',
(tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: EditableDateTime(
dateTimeTitle: 'Date time',
dateTimePickerController: dateTimePickerController,
),
),
),
);
dateTimePickerController.text = '2022-09-20 12:45';
TextField textField =
tester.widget(find.byKey(const Key('editableDateTimeTextField')));
expect(textField.controller!.text, '2022-09-20 12:45');
await tester.tap(find.byKey(const Key('editableDateTimeTextField')));
await tester.pumpAndSettle();
await tester.tap(find.text('14')); // set day
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
await tester.tap(find.text('OK'));
expect(textField.controller!.text, '2022-09-14 12:45');
},
);
testWidgets(
'Selecting previous date month and setting day only',
(tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: EditableDateTime(
dateTimeTitle: 'Date time',
dateTimePickerController: dateTimePickerController,
),
),
),
);
dateTimePickerController.text = '2022-09-20 12:45';
TextField textField =
tester.widget(find.byKey(const Key('editableDateTimeTextField')));
expect(textField.controller!.text, '2022-09-20 12:45');
await tester.tap(find.byKey(const Key('editableDateTimeTextField')));
await tester.pumpAndSettle();
await tester.tap(previousMonthIcon);
await tester.pumpAndSettle(const Duration(seconds: 1));
await tester.tap(find.text('6')); // set day
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
await tester.tap(find.text('OK'));
expect(textField.controller!.text, '2022-08-06 12:45');
},
);
},
);
}
/// EditableDateTime stateless widget definition
class EditableDateTime extends StatelessWidget {
EditableDateTime({
Key? key,
required this.dateTimeTitle,
required this.dateTimePickerController,
}) : super(key: key);
static final DateFormat englishDateTimeFormat =
DateFormat("yyyy-MM-dd HH:mm");
DateTime _selectedDate = DateTime.now();
TimeOfDay _selectedTime = TimeOfDay.now();
DateTime _dateTime = DateTime.now();
final String dateTimeTitle;
final TextEditingController dateTimePickerController;
void _updateDateTimePickerValues() {
_selectedDate = _dateTime;
_selectedTime = TimeOfDay(hour: _dateTime.hour, minute: _dateTime.minute);
dateTimePickerController.text = englishDateTimeFormat.format(_dateTime);
}
// Select for Date
Future<DateTime?> _selectDatePickerDate(BuildContext context) async {
final DateTime? selectedDate = await showDatePicker(
context: context,
initialDate: _selectedDate,
firstDate: DateTime(2020),
lastDate: DateTime(2100),
);
if (selectedDate == null) {
// User clicked on Cancel button
return null;
} else {
if (selectedDate != _selectedDate) {
_selectedDate = selectedDate;
}
}
return _selectedDate;
}
Future<TimeOfDay?> _selectDatePickerTime(BuildContext context) async {
final TimeOfDay? selectedTime = await showTimePicker(
context: context,
initialTime: _selectedTime,
builder: (
BuildContext context,
Widget? childWidget,
) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
alwaysUse24HourFormat: true,
),
child: childWidget!,
);
},
);
if (selectedTime == null) {
// User clicked on Cancel button
return null;
} else {
if (selectedTime != _selectedTime) {
_selectedTime = selectedTime;
}
}
return _selectedTime;
}
Future _selectDatePickerDateTime(BuildContext context) async {
final DateTime? date = await _selectDatePickerDate(context);
if (date == null) {
// User clicked on date picker dialog Cancel button. In
// this case, the time picker dialog is not displayed and
// the _dateTime value is not modified.
return;
}
final TimeOfDay? time = await _selectDatePickerTime(context);
if (time == null) {
// User clicked on time picker dialog Cancel button. In
// this case, the _dateTime value is not modified.
return;
}
// setState(() {
_dateTime = DateTime(
date.year,
date.month,
date.day,
time.hour,
time.minute,
);
// });
dateTimePickerController.text = englishDateTimeFormat.format(_dateTime);
}
#override
Widget build(BuildContext context) {
// print('_EditableDateTimeState.build()');
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
dateTimeTitle,
style: TextStyle(
color: Colors.yellow.shade300,
fontSize: 18.0,
),
),
const SizedBox(
height: 5.0,
),
SizedBox(
// Required to fix Row exception
// layoutConstraints.maxWidth < double.infinity.
width: 170,
child: Theme(
data: Theme.of(context).copyWith(
textSelectionTheme: TextSelectionThemeData(
selectionColor: Colors.blue.shade900,
),
),
child: TextField(
key: const Key('editableDateTimeTextField'),
decoration: const InputDecoration.collapsed(hintText: ''),
style: const TextStyle(
color: Colors.white,
fontSize: 18.0,
fontWeight: FontWeight.normal),
controller: dateTimePickerController,
readOnly: true,
onTap: () {
// initializing the date and time dialogs with the
// currently displayed date time value ...
DateTime currentDateTime = englishDateTimeFormat
.parse(dateTimePickerController.text);
_selectedTime =
TimeOfDay(hour: currentDateTime.hour, minute: currentDateTime.minute);
_selectedDate = currentDateTime;
_selectDatePickerDateTime(context);
},
),
),
),
],
),
],
);
}
}
i'va made a TimePicker that will pop up when user taps on textFormField it's working fine but the only problem is that i want to update textFormField value by the one selected from timePicker
I've declared a controller and initilize a selectedTimer
TimeOfDay selectedTime = TimeOfDay(hour: 0, minute: 0);
TextEditingController _timeController = TextEditingController();
And this is the code of TextFormField
TextFormField(
controller: _timeController,
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
_selectTime(context);
},
),
And this is the code for the method _selectTime
_selectTime(BuildContext context) async {
final TimeOfDay? timeOfDay = await showTimePicker(
context: context,
initialTime: selectedTime,
initialEntryMode: TimePickerEntryMode.dial,
);
if (timeOfDay != null && timeOfDay != selectedTime) {
setState(() {
selectedTime = timeOfDay;
TextEditingValue(text: timeOfDay.toString());
});
}
}
Do as follows:
_timeController.text=timeOfDay.toString();
selectTime(BuildContext context) async {
final TimeOfDay? timeOfDay = await showTimePicker(
context: context,
initialTime: selectedTime,
initialEntryMode: TimePickerEntryMode.dial,
);
if (timeOfDay != null && timeOfDay != selectedTime) {
setState(() {
selectedTime = timeOfDay;
_timeController.text=timeOfDay.toString();
TextEditingValue(text: timeOfDay.toString());
});
}
}
remove this line TextEditingValue(text: timeOfDay.toString());
and replace by this _timeController.text = selectedTime.toString();
This is the code i use to show time picker
var selectedTime;
void initState() {
super.initState();
selectedDate = DateTime.now();
selectedTime = TimeOfDay(hour: 23, minute: 23);
}
_selectTime(BuildContext context) async {
final TimeOfDay? picked = await showTimePicker(
context: context,
initialTime: selectedTime,
);
if(picked != null && picked != selectedTime)
setState(() {
selectedTime = picked;
});
}
This is the code i used to send data to another screen
SizedBox(width: 120,
child: ElevatedButton(
child: Text('Continue'),
onPressed: (){
widget.book.selectedDate = selectedDate;
widget.book.selectedTime= selectedTime;
Navigator.push(context, MaterialPageRoute(builder: (context)=> NewBookFinishView(book: widget.book)),
);
},
),
)
but when i press continue to send the value of date and time i get an error
TimeOfDay' is not a subtype of type 'DateTime? please help.
You try to assign twice your widget.book.selectedDate inside your onPressed (you might have used the wrong variable):
widget.book.selectedDate = selectedDate; // 1st assign with a DateTime
widget.book.selectedDate = selectedTime; // 2nd assign with a TimeOfDay
And even if your selectedDate is a dynamic with the 1st assignation being a DateTime you won't be able to assign the value of selectedTime as it is of type TimeOfDay.
In this below code to display a date picker in Flutter as I press a button but nothing happens. I tried looking into the docs found noting there are no errors on how I can debug this or get to work.
import "package:flutter/material.dart" ;
import "dart:async";
void main() {
runApp(new MaterialApp(
home: new Baseapp(),
));
}
class Baseapp extends StatefulWidget{
#override
_State createState() => new _State();
}
class _State extends State<Baseapp> {
String _value = '';
Future _selectDate() async {
DateTime? picked = await showDatePicker(
context: context,
initialDate: new DateTime.now(),
firstDate: new DateTime(2016),
lastDate: new DateTime(2019)
);
if(picked != null) setState(() => _value = picked.toString());
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title : new Text("NAME HERE", style :TextStyle(color:Colors.black)),
backgroundColor: Colors.yellow ,
),
body: new Container(
padding : new EdgeInsets.all(20),
child: new Center(
child: new Column(
children:<Widget>[
new Text(_value),
new ElevatedButton(onPressed:_selectDate, child: new Text("Date")),
],
),
),
),
);
}
}
In your showDatePicker function, try implementing a builder, like so:
final DateTime picked = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(1900),
lastDate: lastDate,
//Implement builder here
builder: (context, child) => child,
That should solve the issue.
Future _selectDate() async {
DateTime? picked = await showDatePicker(
context: context,
initialDate: new DateTime.now(),
firstDate: new DateTime(2016),
lastDate: new DateTime(2019) //<= you used here past year but you set initial date is today so use here future year such as "lastDate: new DateTime(2019)" other wise intial date set to less than last year
lastDate: new DateTime(2024)
);
you used here last date in past year but you set initial date is today so use here future year such as "lastDate: new DateTime(2019)" other wise intial date set to less than last year
if(picked != null){
setState(() {
int year = picked.year;
int mth = picked.month;
int day = picked.day;
_value = '${day} / ${mth} / ${year}';
});
hey I figured it out XD
this particular code was missing.
thanks
I am basically trying to create a signup screen for my app. I have already included all of the other TextFormField for the other data. However, I am now stuck on the date input section, I managed to create a TextFormField wrapped in a GestureDetector where it will open a date picker to select a date once the TextFormField is tapped. My problem is I want to set the value of the the date TextFormField with the date selected in the date picker, I don't know why with the code that I have it is very inconsistent, at times it does display the date and other times it doesn't update and keeps showing the hintText, and also since there is a date picker popping up when the field is tapped, how could I disable the keyboard from showing when focusing on the date field.
class DateState extends StatefulWidget {
const DateState({
Key key,
}) : super(key: key);
#override
_DateState createState() => _DateState();
}
class _DateState extends State<DateState> {
DateTime selectedDate = DateTime.now();
Future<Null> _selectDate(BuildContext context) async {
final DateTime picked = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(1950),
lastDate: DateTime.now());
if (picked != null && picked != selectedDate)
setState(() {
selectedDate = picked;
});
}
#override
Widget build(BuildContext context) {
var dateValue = TextEditingController();
return DateFieldContainer(
child: GestureDetector(
child: TextFormField(
controller: dateValue,
onTap: () {
_selectDate(context);
dateValue.text = "${selectedDate.toLocal()}".split(' ')[0];
},
decoration: InputDecoration(
hintText: "Select Date",
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: PrimaryColor),
),
),
),
),
);
}
}
Just replace your _selectDate() with below code.
void _selectDate(){
showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(1950),
lastDate: DateTime.now(),
).then((pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
selectedDate = pickedDate;
dateValue.text = "${selectedDate.toLocal()}".split(' ')[0];
});
});
}
Replace your onTap() implementation with below code
onTap: () {
_selectDate();
}
Keep var dateValue = TextEditingController(); outside build() method.