Flutter get all variable null using selector provider - flutter

I have this provider class:
import 'package:flutter/material.dart';
class DateProvider with ChangeNotifier {
DateTime? _date1 = null;
DateTime? _date2 = null;
DateTime? get date1 => _date1;
DateTime? get date2 => _date2;
void changeDate1(DateTime? date1) {
_date1 = date1;
notifyListeners();
}
void changeDate2(DateTime? date2) {
_date2 = date2;
notifyListeners();
}
}
I have two TextFormFields wrapped with Selector when I press on the first one to pick date
class _TestState extends State<Test> {
var _formKey = GlobalKey<FormState>();
var _date2Key = GlobalKey<FormState>();
TextEditingController _date1Controller = TextEditingController();
TextEditingController _date2Controller = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Form(
key: _formKey,
child: Column(
children: [
Selector<DateProvider, DateTime?>(
selector: (_, provider) => provider.date1,
builder: (context, date1, child) {
return TextFormField(
controller: _date1Controller,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
print("Validator $date1");
return;
},
readOnly: true,
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: date1 != null ? date1 : DateTime.now(),
firstDate: DateTime.now(),
lastDate: DateTime(2101),
);
if (pickedDate != null) {
Provider.of<DateProvider>(context, listen: false)
.changeDate1(pickedDate);
print("TextFormField1 date: $date1");
_date2Key.currentState!.validate();
_formKey.currentState!.validate();
}
},
);
}),
Selector<DateProvider, Tuple2<DateTime?, DateTime?>>(
selector: (_, provider) =>
Tuple2(provider.date1, provider.date2),
builder: (context, data, child) {
return Form(
key: _date2Key,
child: TextFormField(
controller: _date2Controller,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
print("Validator2 date1 :${data.item1}");
print("Validator2 date2 :${data.item2}");
return;
},
readOnly:
true, //set it true, so that user will not able to edit text
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate:
data.item2 != null ? data.item2! : DateTime.now(),
firstDate: DateTime.now(),
lastDate: DateTime(2101),
);
if (pickedDate != null) {
Provider.of<DateProvider>(context, listen: false)
.changeDate2(pickedDate);
}
},
),
);
}),
],
),
),
);
}
}
I want to change the date1 in provider class and validate that two textfromfield.
When I pickDate in the first one all the date printed is null knowing that I wrapped the two textformfield with selector
The console shows:
TextFormField1 date: null
Validator2 date1 :null
Validator2 date2 :null
Validator null
why are the values null

Related

Flutter app logic to cancell date and time picker dialog box at once

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

how to restrict choose date in datepicker flutter

I have two TextFormField startdate and enddate that takes date as a input from date picker. i want to restrict that user can not allow to select enddate before selected startdate.Be able to select from after selected startdate.how can i manage that.
Here is my cample code
TextFormField(
controller: startDate,
readOnly: true,
validator: (startDate){
if(startDate==null|| startDate.isEmpty){
return "Please Input Start Date";
}else return null;
},
onTap: () async{
DateTime? startPickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate:DateTime.now(),
lastDate: DateTime(2100)
);
if(startPickedDate!= null){
String formattedDate = DateFormat('dd-MM-yyyy').format(startPickedDate);
setState(() {
startDate.text = formattedDate; //set output date to TextField value.
});
}
},
)
TextFormField(
controller: endDate,
readOnly: true,
validator: (endDate){
if(endDate==null || endDate.isEmpty){
return "Please Input End Date";
}else return null;
},
onTap: () async{
DateTime? endPickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime.now(),
lastDate: DateTime(2100),
);
if(endPickedDate!= null){
String formattedDate = DateFormat('dd-MM-yyyy').format(endPickedDate);
setState(() {
endDate.text = formattedDate;
}
);
}
},
),
All you need is a check to see if startDate controller is empty or not. If it is not empty show date picker. Check the following code for implementation:
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
late TextEditingController startDate;
late TextEditingController endDate;
#override
void initState() {
startDate = TextEditingController();
endDate = TextEditingController();
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
TextFormField(
controller: startDate,
readOnly: true,
validator: (startDate){
if(startDate==null|| startDate.isEmpty){
return "Please Input Start Date";
}else return null;
},
onTap: () async{
DateTime? startPickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate:DateTime.now(),
lastDate: DateTime(2100)
);
if(startPickedDate!= null){
String formattedDate = DateFormat('dd-MM-yyyy').format(startPickedDate);
setState(() {
startDate.text = formattedDate; //set output date to TextField value.
});
}
},
),
TextFormField(
controller: endDate,
readOnly: true,
validator: (endDate){
if(endDate==null || endDate.isEmpty){
return "Please Input End Date";
}else {
return null;
}
},
onTap: () async{
if (startDate.text.isNotEmpty) {
String dateTime = startDate.text;
DateFormat inputFormat = DateFormat('dd-MM-yyyy');
DateTime input = inputFormat.parse(dateTime);
DateTime? endPickedDate = await showDatePicker(
context: context,
initialDate: input.add(const Duration(days: 1)),
firstDate: input.add(const Duration(days: 1)),
lastDate: DateTime(2100),
);
if(endPickedDate!= null){
String formattedDate = DateFormat('dd-MM-yyyy').format(endPickedDate);
setState(() {
endDate.text = formattedDate;
}
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('You need to select Start Date')));
}
},
),
],
);
}
}
You can check the text of startDate controller. If it is empty (i.e ''), the date picker will not be triggered.
controller: endDate,
readOnly: true,
validator: (endDate) {
if (endDate == null || endDate.isEmpty) {
return "Please Input End Date";
} else
return null;
},
onTap: () async {
if (startDate.text.isNotEmpty) {
DateTime? endPickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime.now(),
lastDate: DateTime(2100),
);
if (endPickedDate != null) {
String formattedDate = DateFormat('dd-MM-yyyy').format(endPickedDate);
setState(() {
endDate.text = formattedDate;
});
}
}
},
),
DateTime? startPickedDate
Define this variable outside the build method. When the start date is selected update this variable. Then in end date add this variable as the first date in date picker
Please update your Text form fields with the below code.
Added Text formfield and the start & end date. I have set 40 days range initially for start and end date. you can update this as per your need.
TextEditingController startDateController = TextEditingController();
TextEditingController endDateController = TextEditingController();
DateTime startDate = DateTime.now().subtract(Duration(days: 40));
DateTime endDate = DateTime.now().add(Duration(days: 40));
Build method column code
Column(children: [
TextFormField(
controller: startDateController,
readOnly: true,
validator: (startDate) {
if (startDate == null || startDate.isEmpty) {
return "Please Input Start Date";
} else
return null;
},
onTap: () async {
DateTime? startPickedDate = await showDatePicker(
context: context,
fieldLabelText: "Start Date",
initialDate: startDate,
firstDate: startDate,
lastDate: endDate);
if (startPickedDate != null) {
String formattedDate =
DateFormat('dd-MM-yyyy').format(startPickedDate);
setState(() {
startDate = startPickedDate;
startDateController.text =
formattedDate; //set output date to TextField value.
});
}
},
),
TextFormField(
controller: endDateController,
readOnly: true,
validator: (endDate) {
if (endDate == null || endDate.isEmpty) {
return "Please Input End Date";
} else
return null;
},
onTap: () async {
DateTime? endPickedDate = await showDatePicker(
context: context,
fieldLabelText: "End Date",
initialDate: startDate,
firstDate: startDate,
lastDate: endDate,
);
if (endPickedDate != null) {
String formattedDate =
DateFormat('dd-MM-yyyy').format(endPickedDate);
setState(() {
endDate = endPickedDate;
endDateController.text = formattedDate;
});
}
},
),
]

Flutter | How to validate time picker

Here I want to validate the time picker. when I am going to select the time from the time picker it must only allow me to select the current time or before the current time. I am not supposed to allow select future time.
Here is the time picker snippet I've tried.
DateTimeField(
format: format,
autocorrect: true,
autovalidate: false,
controller: _serviceDate,
readOnly: true,
validator: (date) => (date == null || _serviceDate.text == '')
? 'Please enter valid date'
: null,
onShowPicker: (context, currentValue) async {
final date = await showDatePicker(
context: context,
firstDate: DateTime.now(),
initialDate: currentValue ?? DateTime.now(),
lastDate: DateTime(2100));
if (date != null) {
final time = await showTimePicker(
context: context,
initialTime: TimeOfDay.fromDateTime(
currentValue ?? DateTime.now(),
),
);
return DateTimeField.combine(date, time);
} else {
return currentValue;
}
},
);
You can enable autovalidateMode on DateTimeField then check the DateTime difference of the selected DateTime with the current DateTime. If the time difference is negative, this means the selected DateTime is in the past.
DateTimeField(
format: format,
autocorrect: true,
readOnly: true,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (DateTime? selectedDateTime) {
if (selectedDateTime != null) {
// If the DateTime difference is negative,
// this indicates that the selected DateTime is in the past
if (selectedDateTime.difference(DateTime.now()).isNegative) {
debugPrint('Selected DateTime is in the past');
} else {
debugPrint('Selected DateTime is in the future');
}
}
},
onShowPicker: ...
),
Aside from that, you might also want to consider setting the lastDate on showDatePicker to DateTime.now() so future dates can't be selected.
Here's the complete sample that you can try out.
import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.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 MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final format = DateFormat("yyyy-MM-dd HH:mm");
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DateTimeField(
format: format,
autocorrect: true,
readOnly: true,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (DateTime? selectedDateTime) {
if (selectedDateTime != null) {
// If the DateTime difference is negative,
// this indicates that the selected DateTime is in the past
if (selectedDateTime.difference(DateTime.now()).isNegative) {
debugPrint('Selected DateTime is in the past');
} else {
debugPrint('Selected DateTime is in the future');
}
}
},
onShowPicker: (context, currentValue) async {
final date = await showDatePicker(
context: context,
firstDate: DateTime(1900),
initialDate: DateTime.now(),
lastDate: DateTime.now());
if (date != null) {
final time = await showTimePicker(
context: context,
initialTime: TimeOfDay.fromDateTime(
currentValue ?? DateTime.now(),
),
);
return DateTimeField.combine(date, time);
} else {
return currentValue;
}
},
),
],
),
),
);
}
}

Flutter: DateTimeField manual editing problem

I have DateTimeField and there is strange issue while editing the date manually.
Here is the display with default value:
I selected month by double tapping and try to type 08 manually.
When I bring pointer at the end of month 12, and pressed backspace to remove 2 from 12. The month was changed to 01.
When I press backspace in the end of year, to remove 8 from 2018. It was changed to 0201.
Here is the code of that field:
DateTimeField(
format: DateFormat("yyyy-MM-dd hh:mm:ss"),
onSaved: (val) => setState(() => _fromDate = val),
keyboardType: TextInputType.datetime,
onChanged: (DateTime newValue) {
setState(() {
_fromDate = newValue;
});
},
onShowPicker: (context, currentValue) {
return showDatePicker(
context: context,
firstDate: DateTime.now(),
initialDate: currentValue ?? DateTime.now(),
lastDate: DateTime.now().add(new Duration(days: 30))
);
},
);
I have no clue, what's going on with this. Please tell me, what could be wrong?
NOTE:
Using picker for date selection works fine
I've date field in another page, it's in yyyy-MM-dd format, and it works as expected there.
So I've tried the available sample that you've provided. I got a different behavior, I'm not able to edit the value manually. The date picker always open whenever I've tried to edit the form.
Here is the complete minimal code that I've tested base from your code:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
void main() => runApp(BasicDateTimeField());
class BasicDateTimeField extends StatefulWidget {
#override
_BasicDateTimeFieldState createState() => _BasicDateTimeFieldState();
}
class _BasicDateTimeFieldState extends State<BasicDateTimeField> {
#override
Widget build(BuildContext context) {
DateTime _fromDate;
return MaterialApp(
home: Scaffold(
body: Center(
child: DateTimeField(
format: DateFormat("yyyy-MM-dd hh:mm:ss"),
onSaved: (val) => setState(() => _fromDate = val),
keyboardType: TextInputType.datetime,
onChanged: (DateTime newValue) {
setState(() {
_fromDate = newValue;
});
},
onShowPicker: (context, currentValue) {
return showDatePicker(
context: context,
firstDate: DateTime.now(),
initialDate: currentValue ?? DateTime.now(),
lastDate: DateTime.now().add(new Duration(days: 30)));
},
),
),
),
);
}
}
Output:
In the sample, I've used the current version of datetime_picker_formfield: ^2.0.0 plugin.
Perhaps you can use the built-in DateTime picker instead.
Here is a sample demo that I've created for your reference:
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
theme: ThemeData(primaryColor: Colors.blue),
home: MyWidget(),
),
);
}
class MyWidget extends StatefulWidget {
createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
DateTime selectedDate = DateTime.now();
Future<void> _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
initState() {
super.initState();
selectedDate = DateTime.now();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Demo App"),
),
body: ListView(padding: const EdgeInsets.all(16.0), children: [
Container(
height: 100,
child: FlutterLogo(),
),
SizedBox(height: 10.0),
InputDatePickerFormField(
firstDate: DateTime(2015, 8),
lastDate: DateTime(2101),
initialDate: selectedDate,
onDateSubmitted: (date) {
setState(() {
selectedDate = date;
});
},
),
// Text("Selected Date: $selectedDate"),
ElevatedButton(
onPressed: () => _selectDate(context),
child: Text('Select date'),
)
]),
);
}
}
Output:
This behavior means that you can not manually change the date select use your picker to change it

Getting value from form with date picker

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