I am trying to implement a time picker in my flutter which display time like this see picture below.
although I am able to format time like this but problem is it does not display the current time when the widget is initialized.
I tried using this. here see my code.
TimeOfDay _currentTime = new TimeOfDay.now();
String timeText = 'Set A Time';
Future<Null> selectTime(BuildContext context) async {
TimeOfDay selectedTime = await showTimePicker(
context: context,
initialTime: _currentTime,
);
MaterialLocalizations localizations = MaterialLocalizations.of(context);
String formattedTime = localizations.formatTimeOfDay(selectedTime,
alwaysUse24HourFormat: false);
if (formattedTime != null) {
setState(() {
timeText = formattedTime;
});
}
};
Widget Date picker
class DatePicker extends StatelessWidget {
DatePicker({
this.formatedDate,
this.selectedDate,
});
final String formatedDate;
final Function selectedDate;
#override
Widget build(BuildContext context) {
return FlatButton(
padding: EdgeInsets.only(top: 30.0, bottom: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Icon(
Icons.calendar_today,
color: Color(kBorderTileColor),
),
SizedBox(
width: 50.0,
),
Text(
'$formatedDate',
style: kInputTextStyle,
),
SizedBox(
width: 50.0,
),
Text(
'All Day',
style: TextStyle(color: Color(kBorderTileColor)),
),
],
),
onPressed: () {
selectedDate(context);
},
);
}
}
Try this source code.
TimeOfDay _currentTime = new TimeOfDay.now();
String timeText = 'Set A Time';
Future<Null> selectTime(BuildContext context) async {
TimeOfDay selectedTime = await showTimePicker(
context: context,
initialTime: _currentTime,
);
MaterialLocalizations localizations = MaterialLocalizations.of(context);
if (selectedTime != null) {
String formattedTime = localizations.formatTimeOfDay(selectedTime,
alwaysUse24HourFormat: false);
if (formattedTime != null) {
setState(() {
timeText = formattedTime;
});
}
}
}
void setCurrentTime(){
TimeOfDay selectedTime=new TimeOfDay.now();
MaterialLocalizations localizations = MaterialLocalizations.of(context);
String formattedTime = localizations.formatTimeOfDay(selectedTime,
alwaysUse24HourFormat: false);
if (formattedTime != null) {
setState(() {
timeText = formattedTime;
});
}
}
Call this function in build(BuildContext context)
Widget build(BuildContext context) {
setCurrentTime();
...
}
Related
I am trying to use riverpod inside my modalBottomsheet to update values, but when i try to update the state, it updates but Widget(FilterView) doesnot rebuild.
Since it is Inside of showModalBottomSheet, I have enclosed it inside
ProviderScope()
Below is my code to open ModalBottomSheet
_showFilter(BuildContext context) {
final container = ProviderScope.containerOf(context);
showModalBottomSheet(
context: context,
builder: (context) => ProviderScope(
parent: container,
child: FilterView(
key: Key("filterview"),
),
),
);
}
Below is FilterView Widget
class FilterView extends ConsumerWidget {
const FilterView({super.key});
#override
Widget build(BuildContext context, WidgetRef ref) {
return Consumer(builder: ((context, ref, _) {
var reference = ref.read(filterController.notifier);
var filter = ref.watch(filterController);
return Card(
child: Column(
children: [
const Text(
"FILTER",
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Checkbox(
value: filter.today,
onChanged: (bool? value) {
reference.today(value ?? false);
}),
const Text("Today only")
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text("From Date:"),
IconButton(
onPressed: filter.today
? null
: () async {
var date =
await _showDatePicker(context, filter.fromDate);
reference.newFromDate(date);
},
icon: const Icon(Icons.date_range),
),
if (filter.fromDate != null)
Text(filter.fromDate!.toHumanRedable()),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text("To Date"),
IconButton(
onPressed: filter.today
? null
: () async {
var date =
await _showDatePicker(context, filter.toDate);
reference.newToDate(date);
},
icon: const Icon(Icons.date_range),
),
if (filter.toDate != null)
Text(filter.toDate!.toHumanRedable()),
],
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text("Filter with Given Criteria"),
),
],
),
);
}));
}
Future<DateTime?> _showDatePicker(
BuildContext context, DateTime? initialDate) async {
var result = await showDatePicker(
context: context,
firstDate: DateTime.now().add(const Duration(days: -100)),
lastDate: DateTime.now().add(const Duration(days: 100)),
initialDate: initialDate ?? DateTime.now(),
);
return result;
}
}
State does update I have verified that. But FilterView Widget is not rebuilding.
Below is the provider
final filterController =
StateNotifierProvider<FilterNotifier, Filter>((ref) => FilterNotifier());
class FilterNotifier extends StateNotifier<Filter> {
FilterNotifier() : super(Filter.initialize());
void newFromDate(DateTime? fromDateTime) {
state = state.registerNewFromDate(fromDateTime);
}
void newToDate(DateTime? toDateTime) {
state = state.registerNewToDate(toDateTime);
}
void today(bool today) {
state = state.registerNewValues(today, state.fromDate, state.toDate);
}
}
Below is a filter class
class Filter {
bool today = true;
DateTime? fromDate;
DateTime? toDate;
Filter._(this.today, this.fromDate, this.toDate);
static Filter initialize() {
return Filter._(true, null, null);
}
Filter registerNewValues(bool today, DateTime? fromDate, DateTime? toDate) {
this.today = today;
this.fromDate = fromDate;
this.toDate = toDate;
return this;
}
Filter registerNewFromDate(DateTime? fromDate) {
this.fromDate = fromDate;
return this;
}
Filter registerNewToDate(DateTime? toDate) {
this.toDate = toDate;
return this;
}
}
The current model class Filter having same instance, therefore the UI isn't updating. To update the state, you need to pass new instance. I am creating copyWith method wich is handy,
class Filter { // I pefer creating final fileds with copyWith
bool today = true;
DateTime? fromDate;
DateTime? toDate;
Filter._(this.today, this.fromDate, this.toDate);
static Filter initialize() {
return Filter._(true, null, null);
}
Filter registerNewFromDate(DateTime? fromDate) {
this.fromDate = fromDate;
return this;
}
Filter registerNewToDate(DateTime? toDate) {
this.toDate = toDate;
return this;
}
Filter copyWith(
bool? today,
DateTime? fromDate,
DateTime? toDate,
) {
return Filter._(
today ?? this.today,
fromDate ?? this.fromDate,
toDate ?? this.toDate,
);
}
}
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 am working on a flutter app where I want to show date and time pickers. In the third example, I am trying to show how to pick a date and time together, which is working fine. But what I want is when the user cancels the date picker dialog then the time picker should automatically get cancelled. But right now we have to do it separately. For reference, you can see the problem in the image below.
I need some help with the app logic to cancel the date & time picked at once.
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
void main() {
runApp(const DateTimePickerApp());
}
class DateTimePickerApp extends StatelessWidget {
const DateTimePickerApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: TextScreen(),
);
}
}
class TextScreen extends StatefulWidget {
const TextScreen({
Key? key,
}) : super(key: key);
#override
State<TextScreen> createState() => _TextScreenState();
}
class _TextScreenState extends State<TextScreen> {
DateTime selectedDate = DateTime.now();
TimeOfDay selectedTime = TimeOfDay.now();
DateTime dateTime = DateTime.now();
bool showDate = true;
bool showTime = true;
bool showDateTime = true;
// Select for Date
Future<DateTime> _selectDate(BuildContext context) async {
final selected = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(2000),
lastDate: DateTime(2025),
);
if (selected != null && selected != selectedDate) {
setState(() {
selectedDate = selected;
});
}
return selectedDate;
}
// Select for Time
Future<TimeOfDay> _selectTime(BuildContext context) async {
final selected = await showTimePicker(
context: context,
initialTime: selectedTime,
);
if (selected != null && selected != selectedTime) {
setState(() {
selectedTime = selected;
});
}
return selectedTime;
}
// select date time picker
Future _selectDateTime(BuildContext context) async {
final date = await _selectDate(context);
final time = await _selectTime(context);
if (date == null) return;
if (time == null) return;
setState(() {
dateTime = DateTime(
date.year,
date.month,
date.day,
time.hour,
time.minute,
);
});
}
String getDate() {
// ignore: unnecessary_null_comparison
if (selectedDate == null) {
return 'select date';
} else {
return DateFormat('MMM d, yyyy').format(selectedDate);
}
}
String getDateTime() {
// ignore: unnecessary_null_comparison
if (dateTime == null) {
return 'select date timer';
} else {
return DateFormat('yyyy-MM-dd HH:mm a').format(dateTime);
}
}
String getTime(TimeOfDay tod) {
final now = DateTime.now();
final dt = DateTime(now.year, now.month, now.day, tod.hour, tod.minute);
final format = DateFormat.jm();
return format.format(dt);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('GeeksforGeeks'),
centerTitle: true,
backgroundColor: Colors.green,
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
showDate ? Center(child: Text(getDate())) : const SizedBox(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: ElevatedButton(
onPressed: () {
_selectDate(context);
showDate = true;
},
child: const Text('Date Picker'),
),
),
showTime
? Center(child: Text(getTime(selectedTime)))
: const SizedBox(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: ElevatedButton(
onPressed: () {
_selectTime(context);
showTime = true;
},
child: const Text('Timer Picker'),
),
),
showDateTime
? Center(child: Text(getDateTime()))
: const SizedBox(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: ElevatedButton(
onPressed: () {
_selectDateTime(context);
showDateTime = true;
},
child: const Text(' Date & Time '),
),
),
],
),
),
);
}
}
You need to use nullable selectedDate
class _TextScreenState extends State<TextScreen> {
DateTime? selectedDate ;
then make _selectDate nullable return
// Select for Date
Future<DateTime?> _selectDate(BuildContext context) async {
final selected = await showDatePicker(
context: context,
initialDate: selectedDate??DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2025),
);
if (selected != null && selected != selectedDate) {
setState(() {
selectedDate = selected;
});
}
return selectedDate;
}
And while showing _selectTime check selectedDate if it null or not. Also make it null again if you like to avoid cancel second timer loop
// select date time picker
Future<void> _selectDateTime(BuildContext context) async {
final date = await _selectDate(context);
if (date == null) return;
selectedDate = null; // if you want to avoid second loop time picker on cancel
final time = await _selectTime(context);
You also need to add bang on getDate while selectedDate is now nullable. Using bang! because we've check it's null
String getDate() {
// ignore: unnecessary_null_comparison
if (selectedDate == null) {
return 'select date';
} else {
return DateFormat('MMM d, yyyy').format(selectedDate);
}
}
I am trying to add TimePicker in my todo app. But whenever I navigate to the screen where I have added timepicker I get error saying this.
The getter 'hourOfPeriod' was called on null.
Receiver: null
Tried calling: hourOfPeriod
I have no idea how to fix this or where it is coming from.
These are my code
class _CreateTaskScreenState extends State<CreateTaskScreen> {
final TextEditingController _taskTitleController = TextEditingController();
String taskTitle = '';
bool _taskTitleValidate = false;
DateTime _currentDate = new DateTime.now();
TimeOfDay _currentTime = new TimeOfDay.now();
TimeOfDay selectedTime;
#override
Widget build(BuildContext context) {
/// Time Picker
MaterialLocalizations localizations = MaterialLocalizations.of(context);
String formattedTime = localizations.formatTimeOfDay(selectedTime,
alwaysUse24HourFormat: false);
String timeText = formattedTime;
return Scaffold(
backgroundColor: Color(kPrimaryColor),
appBar: AppBar(
elevation: 0.0,
title: Text('Create a Task'),
),
body: SafeArea(
child: Container(
child: Column(
children: <Widget>[
TimePicker(
icon: Icons.access_time,
selectedTime: '$timeText',
onPress: () async {
selectedTime = await showTimePicker(
context: context,
initialTime: _currentTime,
);
},
),
Anyone, please help me to solve this.
In the first build of the Screen your selectedTime Object is null.
Then you want to create a formatted String with it. Which wont work on a null object.
TimeOfDay selectedTime;
String formattedTime = localizations.formatTimeOfDay(selectedTime,
alwaysUse24HourFormat: false);
EDIT:
class _CreateTaskScreenState extends State<CreateTaskScreen> {
final TextEditingController _taskTitleController = TextEditingController();
MaterialLocalizations localizations;
String taskTitle = '';
bool _taskTitleValidate = false;
DateTime _currentDate = new DateTime.now();
TimeOfDay _currentTime = new TimeOfDay.now();
String timeText = 'initText'; //
#override
void didChangeDependencies() {
super.didChangeDependencies();
setState{(){
timeText = localizations.formatTimeOfDay(_currentTime,
alwaysUse24HourFormat: false);
});
}
#override
Widget build(BuildContext context) {
/// Time Picker
localizations = MaterialLocalizations.of(context);
return Scaffold(
backgroundColor: Colors.red,
appBar: AppBar(
elevation: 0.0,
title: Text('Create a Task'),
),
body: SafeArea(
child: Container(
child: Column(
children: <Widget>[
TimePicker(
icon: Icons.access_time,
selectedTime: '$timeText',
onPress: () {
showTimePicker(
context: context,
initialTime: _currentTime,
).then(
(TimeOfDay value) => setState(
() {
timeText = localizations.formatTimeOfDay(value,
alwaysUse24HourFormat: false);
},
),
);
},
)
],
),
),
),
);
}
}
As said above you're passing slectedTime to formatTimeOfDay function before initialising it first.
I'm having trouble setting the state to get the value from textfield with a date picker.
How do you return the value in the text field if the date picked is between start and end. I set pressed initially to false and when it's press it becomes true which will return the value from the text field.
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(
home: MyApp(),
));
class MyApp extends StatefulWidget {
#override
MyAppState createState() {
return new MyAppState();
}
}
class MyAppState extends State<MyApp> {
bool pressed = false;
final myController = TextEditingController();
DateTime selectedDate = DateTime.now();
Future<Null> _selectDate(BuildContext context) async {
final DateTime picked = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(2015, 8),
lastDate: DateTime(2101));
if (picked != null && picked != selectedDate)
setState(() {
selectedDate = picked;
});
}
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Route'),
),
body: Column(
children: <Widget>[
TextField(
controller: myController,
decoration: new InputDecoration(labelText: "Enter a number"),
keyboardType: TextInputType.number,
),
SizedBox(
height: 50.0,
),
RaisedButton(
onPressed: () => _selectDate(context),
child: Text('Select date'),
),
RaisedButton(
child: Text("show text"),
onPressed: () {
DateTime start = DateTime(2019, 01, 01);
final end = DateTime(2022, 12, 31);
if (selectedDate.isAfter(start) && selectedDate.isBefore(end)) {
return pressed = true;
} else {
return pressed = false;
}
},
),
pressed ? Text(myController.text) : Text('no'),
],
),
);
}
}
final myController = TextEditingController();
DateTime selectedDate = DateTime.now();
Future<Null> _selectDate(BuildContext context) async {
final DateTime picked = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(2015, 8),
lastDate: DateTime(2101));
if (picked != null && picked != selectedDate)
setState(() {
selectedDate = picked;
myController.text = selectedDate.toString();
});
}