I've created a form using Flutter which has date picker.
User is supposed to pick his/her date of birth using it to make sure if the user is 16 and above. How do I validate date of birth to age 16?
Here are the parts of the code:
class _WelcomeScreenState extends State<WelcomeScreen> {
TextEditingController dateinput = TextEditingController();
final formKey = GlobalKey<FormState>();
String name = "";
#override
void initState() {
dateinput.text = ""; //set the initial value of text field
super.initState();
}
--
GestureDetector(
child: TextField(
style: TextStyle(color: Colors.white),
controller:
dateinput, //editing controller of this TextField
decoration: InputDecoration(
labelStyle: TextStyle(color: Colors.white),
icon: Icon(Icons.calendar_today),
iconColor: Colors.white, //icon of text field
labelText: "Enter Date Of Birth" //label text of field
),
readOnly:
true, //set it true, so that user will not able to edit text
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(
1900), //DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2040));
if (pickedDate != null) {
print(
pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000
String formattedDate =
DateFormat('dd-MM-yyyy').format(pickedDate);
print(
formattedDate); //formatted date output using intl package => 2021-03-16
//you can implement different kind of Date Format here according to your requirement
setState(() {
dateinput.text =
formattedDate; //set output date to TextField value.
});
} else {
print("Date is not selected");
}
},
),
),
A naive approach would be to just construct a DateTime object from the selected birth date and to then compute DateTime.now().difference(birthDate).inDays / 365. That doesn't account for leap days, and maybe it's close enough, but it's not how a human would compute age.
When attempting to solve a programming problem, one of the first things you usually should ask yourself is: How would you solve this without a computer?
To determine if someone is at least 16 years old, you would take the current date, subtract 16 from the year, use the same month and day1, and see if their birthday is on or before that date, ignoring the time. So just do that:
extension IsAtLeastYearsOld on DateTime {
bool isAtLeastYearsOld(int years) {
var now = DateTime.now();
var boundaryDate = DateTime(now.year - years, now.month, now.day);
// Discard the time from [this].
var thisDate = DateTime(year, month, day);
// Did [thisDate] occur on or before [boundaryDate]?
return thisDate.compareTo(boundaryDate) <= 0;
}
}
void main() {
// The results below were obtained with 2022-06-11 as the current date.
print(DateTime(2006, 6, 10).isAtLeastYearsOld(16)); // Prints: true
print(DateTime(2006, 6, 11).isAtLeastYearsOld(16)); // Prints: true
print(DateTime(2006, 6, 12).isAtLeastYearsOld(16)); // Prints: false
}
1 This should be fine even if the current date is a leap day since DateTime will convert February 29 into March 1 for non-leap years.
With a function for calculate :
class _WelcomeScreenState extends State<WelcomeScreen> {
TextEditingController dateinput = TextEditingController();
final formKey = GlobalKey<FormState>();
String name = "";
#override
void initState() {
dateinput.text = ""; //set the initial value of text field
super.initState();
}
int calculateAge(DateTime birthDate) {
DateTime currentDate = DateTime.now();
int age = currentDate.year - birthDate.year;
if (birthDate.month > currentDate.month) {
age--;
} else if (currentDate.month == birthDate.month) {
if (birthDate.day > currentDate.day) {
age--;
}
}
return age;
}
}
And in your TextField :
GestureDetector(
child: TextFormField(
style: TextStyle(color: Colors.white),
controller: dateinput, //editing controller of this TextField
decoration: InputDecoration(
labelStyle: TextStyle(color: Colors.white),
icon: Icon(Icons.calendar_today),
iconColor: Colors.white, //icon of text field
labelText: "Enter Date Of Birth" //label text of field
),
readOnly: true, //set it true, so that user will not able to edit text
validator: (value) {
if (calculateAge(DateTime.parse(value)) < 16 || value.isEmpty) {
return 'Please enter date.';
}
return null;
},
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(1900), //DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2040));
if (pickedDate != null) {
print(pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000
String formattedDate = DateFormat('dd-MM-yyyy').format(pickedDate);
print(formattedDate); //formatted date output using intl package => 2021-03-16
//you can implement different kind of Date Format here according to your requirement
setState(() => dateinput.text = formattedDate);
} else {
print("Date is not selected");
}
},
),
),
Related
In my class I have a date range picker and I have other fields I have used Form validation is working for other fields but I don't know how to declare the validation when the user submits the form if the dates are not chosen it should display the error like choose date.
Also, I wanted to check: let's say I have two class in main class I have declared the form and in sub class also I have declared but in second class I don't have button but in main class I have button how to refer?
Display the date choosen or else it will display the default text:
String _displayText(String begin, DateTime? date) {
if (date != null) {
return '$begin Date: ${date.toString().split(' ')[0]}';
} else {
return 'Press the button to show the picker';
}
}
Text will display:
Text(
_displayText('Start', startdate),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
color: Colors.black),
),
ShowDateRangepicker
void _show() async {
final DateTimeRange? result = await showDateRangePicker(
context: context,
firstDate: DateTime(DateTime.now().year - 10),
lastDate: DateTime(DateTime.now().year + 10),
currentDate: DateTime.now(),
saveText: 'Done.',
);
if (result != null) {
setState(() {
print(result.start);
startdate = result.start;
enddate = result.end;
_selectedDateRange = result;
});
}
}
I want to change the way DatePicker shows the Date, so that it shows
day/month/year. As of right now it shows in the format of year/month/day and I'm not sure on how to change this. I've been searching a lot but couldn't find the right way to do this.
I will show my code below, hopefully someone can help me :D
Thank you in Advance guys.
class WFDatePickerField extends DateTimeField {
final bool? hasError;
final bool hasErrorDecoration;
final double? errorHeight;
final dynamic error;
final DateTimeValue value;
final TextStyle? errorStyle;
WFDatePickerField(
{Key? key,
required this.value,
required String labelText,
String hintText = '',
TextEditingController? controller,
FormFieldValidator<DateTime>? validator,
FloatingLabelBehavior? floatingLabelBehavior =
FloatingLabelBehavior.never,
String format = 'dd.MM.yyyy',
final InputDecoration? decoration,
this.errorStyle,
this.hasError,
this.hasErrorDecoration=false,
this.error,
this.errorHeight})
: super(
key: key,
decoration: ErrorDecorationSelector(hasError, hasErrorDecoration, errorHeight, error, value, errorStyle).getDecoration(),
readOnly: true,
style: AppTheme()
.textStyles
.bodyText1!
.copyWith(color: Colors.white.withOpacity(0.5)),
initialValue: value.value,
format: DateFormat('dd/MM/yyyy'),
controller: controller,
validator: validator,
onShowPicker: (context, currentValue) async {
DateTime? newDate;
String? deviceLocale = await (Devicelocale.currentLocale);
LocaleType locale =
deviceLocale != null && deviceLocale.contains('de')
? LocaleType.de
: LocaleType.en;
await DatePicker.showDatePicker(context,
minTime: DateTime(1900, 1, 1),
maxTime: DateTime(DateTime.now().year - 18,
DateTime.now().month, DateTime.now().day),
locale: locale,
onConfirm: (date) => newDate = date);
return newDate;
},
onChanged: (newValue) {
value.value = newValue;
},
);
}
if I'M not mistaken your trying to format the date if that is what you're asking
[enter link description here][1]
// Use this link it is flutter intl dateFormater it has different style
[1]: https://pub.dev/packages/intl
I am working on a project which have a complete button:
Expanded(child: ElevatedButton(
onPressed: () {
completeTrip(
list[index]['id']);
},
child: Text("Complete"),
style: ElevatedButton
.styleFrom(
primary: Colors.green,),
),
and i have a date and time in my database:
{
'from_date':'16-01-2022'
'time' :'1:15 PM'
}
what i want is to show that button only when the given is passed, before that this button must not be shown?
is there anything or any way to do it?
Thanks in advance <3.
You can use Stream.periodic
DateTime current = DateTime.now();
Stream timer = Stream.periodic( Duration(seconds: 1), (i){
current = current.add(Duration(seconds: 1));
return current;
});
timer.listen((data){
//if it reached the given time do something on your button
});
and at the end call timer.cancel;
Couldn't figure out if you wanted help in parsing your date and time or using some sort of timer for displaying the button.So I modified #Bunny1376 's answer for adding some things:
Use a boolean to check whether to show button or not:
bool showBtn = false;
In your initState or some other place, parse the date and time you received as a json as :
Map<String,String> _dateTimeJson = {
'from_date':'16-01-2022',
'time': '1:15 PM'
};
String _dateTimeString = _dateTimeJson['from_date'] + ' ' +_dateTimeJson['time'];
DateFormat _format = DateFormat('dd-mm-yyyy HH:mm a');
DateTime _dateTime = _format.parse(_dateTimeString);
Here, we have appended 'time' field with 'from_date' to form a singe dateTime String which later on is parsed as DateTime.I have used intl package for this. For more details: https://pub.dev/packages/intl
Now, add a timer that executes every second to check if current date time is more than your dateTime as:
DateTime current = DateTime.now();
Stream timer = Stream.periodic( Duration(seconds: 1), (i){
current = current.add(Duration(seconds: 1));
return current;
});
timer.listen((data){
if(current.isAfter(_dateTime)){
// show button
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
setState(() {
showBtn = true;
});
});
}
});
The logic of the button would be:
if(showBtn)...[
Expanded(child: ElevatedButton(
onPressed: () {
completeTrip(
list[index]['id']);
},
child: Text("Complete"),
style: ElevatedButton
.styleFrom(
primary: Colors.green,),
),
]
I am building a calendar through syncfusion calendar on a flutter app and I have been getting an error that tells me "Field '' has not been initialized". I know that I need to initialize _startDate and _endDate but I am not sure what value it should be given.
Code :
class EventCalendar extends StatefulWidget {
const EventCalendar({Key? key}) : super(key: key);
#override
EventCalendarState createState() => EventCalendarState();
}
List<Color> _colorCollection = <Color>[];
List<String> _colorNames = <String>[];
int _selectedColorIndex = 0;
late DataSource _events;
Meeting? _selectedAppointment;
late DateTime _startDate;
late TimeOfDay _startTime;
late DateTime _endDate;
late TimeOfDay _endTime;
bool _isAllDay = false;
String _subject = '';
String _notes = '';
class EventCalendarState extends State<EventCalendar> {
EventCalendarState();
CalendarView _calendarView = CalendarView.month;
late List<String> eventNameCollection;
late List<Meeting> appointments;
#override
void initState() {
_calendarView = CalendarView.month;
appointments = getMeetingDetails();
_events = DataSource(appointments);
// initialize _startDate and _endDate here?
_selectedAppointment = null;
_selectedColorIndex = 0;
_subject = '';
_notes = '';
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: UserDrawer(),
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.black),
backgroundColor: Colors.transparent,
elevation: 0,
centerTitle: true,
title: const Text('Itinerary',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black)),
),
resizeToAvoidBottomInset: false,
body: Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 5, 5),
child: getEventCalendar(_calendarView, _events, onCalendarTapped)),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add, color: Colors.white),
backgroundColor: Color(0xFF003893),
onPressed: () => Navigator.push<Widget>(
context,
MaterialPageRoute(
builder: (BuildContext context) => EventEditor()),
)));
}
SfCalendar getEventCalendar(
CalendarView _calendarView,
CalendarDataSource _calendarDataSource,
CalendarTapCallback calendarTapCallback) {
return SfCalendar(
view: _calendarView,
backgroundColor: Colors.transparent,
initialSelectedDate: DateTime.now(),
todayHighlightColor: Color(0xFF003893),
selectionDecoration: BoxDecoration(color: Colors.white60),
showNavigationArrow: true,
cellBorderColor: Colors.transparent,
firstDayOfWeek: 1,
onTap: calendarTapCallback,
allowedViews: [
CalendarView.day,
CalendarView.week,
CalendarView.month,
CalendarView.timelineWeek
],
monthViewSettings: MonthViewSettings(
showAgenda: true,
agendaViewHeight: 250,
appointmentDisplayMode: MonthAppointmentDisplayMode.appointment),
dataSource: _calendarDataSource,
initialDisplayDate: DateTime(DateTime.now().year, DateTime.now().month,
DateTime.now().day, 0, 0, 0),
timeSlotViewSettings: TimeSlotViewSettings(
minimumAppointmentDuration: const Duration(minutes: 60)),
);
}
void onCalendarViewChange(String value) {
if (value == 'Day') {
_calendarView = CalendarView.day;
} else if (value == 'Week') {
_calendarView = CalendarView.week;
} else if (value == 'Month') {
_calendarView = CalendarView.month;
} else if (value == 'Timeline week') {
_calendarView = CalendarView.timelineWeek;
}
setState(() {});
}
void onCalendarTapped(CalendarTapDetails calendarTapDetails) {
if (calendarTapDetails.targetElement != CalendarElement.appointment) {
return;
}
setState(() {
_selectedAppointment = null;
_isAllDay = false;
_selectedColorIndex = 0;
_subject = '';
_notes = '';
if (_calendarView == CalendarView.month) {
_calendarView = CalendarView.day;
} else {
if (calendarTapDetails.appointments != null &&
calendarTapDetails.appointments!.length == 1) {
final Meeting meetingDetails = calendarTapDetails.appointments![0];
_startDate = meetingDetails.from;
_endDate = meetingDetails.to;
_isAllDay = meetingDetails.isAllDay;
_selectedColorIndex =
_colorCollection.indexOf(meetingDetails.background);
_subject = meetingDetails.eventName == '(No title)'
? ''
: meetingDetails.eventName;
_notes = meetingDetails.description;
_selectedAppointment = meetingDetails;
} else {
final DateTime date = calendarTapDetails.date!;
_startDate = date;
_endDate = date.add(const Duration(hours: 1));
}
_startTime =
TimeOfDay(hour: _startDate.hour, minute: _startDate.minute);
_endTime = TimeOfDay(hour: _endDate.hour, minute: _endDate.minute);
Navigator.push<Widget>(
context,
MaterialPageRoute(builder: (BuildContext context) => EventEditor()),
);
}
});
}
List<Meeting> getMeetingDetails() {
final List<Meeting> meetingCollection = <Meeting>[];
eventNameCollection = <String>[];
eventNameCollection.add('');
_colorCollection = <Color>[];
_colorCollection.add(const Color(0xFF3D4FB5));
_colorCollection.add(const Color(0xFF0F8644));
_colorCollection.add(const Color(0xFF8B1FA9));
_colorCollection.add(const Color(0xFFD20100));
_colorCollection.add(const Color(0xFFFC571D));
_colorCollection.add(const Color(0xFF85461E));
_colorCollection.add(const Color(0xFFFF00FF));
_colorCollection.add(const Color(0xFFE47C73));
_colorCollection.add(const Color(0xFF636363));
_colorNames = <String>[];
_colorNames.add('Blue');
_colorNames.add('Green');
_colorNames.add('Purple');
_colorNames.add('Red');
_colorNames.add('Orange');
_colorNames.add('Caramel');
_colorNames.add('Magenta');
_colorNames.add('Peach');
_colorNames.add('Gray');
return meetingCollection;
}
}
class DataSource extends CalendarDataSource {
DataSource(List<Meeting> source) {
appointments = source;
}
#override
bool isAllDay(int index) => appointments![index].isAllDay;
#override
String getSubject(int index) => appointments![index].eventName;
#override
String getNotes(int index) => appointments![index].description;
#override
Color getColor(int index) => appointments![index].background;
#override
DateTime getStartTime(int index) => appointments![index].from;
#override
DateTime getEndTime(int index) => appointments![index].to;
}
class Meeting {
Meeting(
{required this.from,
required this.to,
this.background = Colors.green,
this.isAllDay = false,
this.eventName = '',
this.description = ''});
final String eventName;
final DateTime from;
final DateTime to;
final Color background;
final bool isAllDay;
final String description;
}
On top of the error, when I go to another page and return back to this calendar page, whatever events that was saved on it earlier on disappears already.
What value should I be giving to initialize _startDate and _endDate and how do I save the state of the page?
All state properties must be declared inside State class. late means that such variable will be initialized later (for example in initState method). If initialization time is not known make variable nullable, I.e. use question mark for type (e.g. DateTime?.
You don't initialize start and end dates, to be able to accept null use ?
like that
DateTime? _startDate;
DateTime? _endDate;
You can make them nullable, with DateTime? and before using them, where you got an error like when you want to access the hour property from them, you can check them if they are null, like this:
if([_startTime, _endTime].contains(null)) return;
Also, since you don't use the data outside of the handler and it is used after you change the value, you can initialize them with DateTime.now(), like this:
DateTime _startDate = DateTime.now();
And you can do this:
if (_calendarView == CalendarView.month) {
_calendarView = CalendarView.day;
return;
}
To remove the else that's coming after.
While specifying the variable as nullable we need to set the values before use them, in this case the AppointmentEditor class used the _startDate and _endDate values before initializing them, hence to use the values we must ensure that the values were set with values, hence before navigate to the editor page we must set values for the startDate and endDate variable with the required values, in this case you can use the selected date value from CalendarController or current date time value, or the date time value which you want to add the event in calendar.
In this shared code snippet, we have used current time value for the both variables as a default value, kindly set the _startDate and _endDate in the onPressed callback of the FloatingActionButton. Please find the attached code snippet for the same.
Code snippet:
onPressed: () {
_startDate??=DateTime.now();
_endDate??=DateTime.now();
Navigator.push<Widget>(
context,
MaterialPageRoute(
builder: (BuildContext context) => AppointmentEditor()),
)
}
I'm trying to print all selected dates in date range picker, is there anyway to do it?
here is my code
import 'package:flutter/material.dart';
import 'package:date_range_picker/date_range_picker.dart' as DateRagePicker;
class TryCalendar extends StatefulWidget {
#override
_TryCalendarState createState() => _TryCalendarState();
}
class _TryCalendarState extends State<TryCalendar> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
),
body: new MaterialButton(
color: Color(0xFFED7D31),
onPressed: () async {
final List<DateTime> picked = await DateRagePicker.showDatePicker(
context: context,
initialFirstDate: new DateTime.now(),
initialLastDate: (new DateTime.now()).add(new Duration(days: 7)),
firstDate: new DateTime(2015),
lastDate: new DateTime(2020)
);
if (picked != null && picked.length == 2) {
print(picked);
}
},
child: new Text("Pick date range")
)
);
}
}
I need to print all selected dates instead of first and last selected dates. Thank you!
List<DateTime> getDaysInBeteween(DateTime startDate, DateTime endDate) {
List<DateTime> days = [];
for (int i = 0; i <= endDate.difference(startDate).inDays; i++) {
days.add(startDate.add(Duration(days: i)));
}
return days;
}
picked is list of DateTime , so you should iterate every date in list then print
if (picked != null && picked.length >= 2) {
picked.forEach((date) {
print(date.toString());
});
}