I am trying to make a date range picker like this ,date picker start with on value (today value) then user select the range he need ,in flutter finally I found this package.
But I can't open it when I click on the button as date picker.
I trayed to use another package date range picker but it doesn't help me!
Flutter has now an inbuilt date range picker below is an example of using it
IconButton(
onPressed: () async {
final picked = await showDateRangePicker(
context: context,
lastDate: endDate,
firstDate: new DateTime(2019),
);
if (picked != null && picked != null) {
print(picked);
setState(() {
startDate = picked.start;
endDate = picked.end;
//below have methods that runs once a date range is picked
allWaterBillsFuture = _getAllWaterBillsFuture(
picked.start.toIso8601String(),
picked.end
.add(new Duration(hours: 24))
.toIso8601String());
});
}
},
icon: Icon(
Icons.calendar_today,
color: Colors.white,
),
),
There's a package specifically built for that purpose, date_range_picker
To install it, you should add the following line under dependecies in the pubspec.yaml file:
date_range_picker: ^1.0.5
You should then import the package at the top of the file of the Widget you would like to use the function:
import 'package:date_range_picker/date_range_picker.dart' as DateRangePicker;
Then, you could use the package as follows:
new MaterialButton(
color: Colors.deepOrangeAccent,
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")
)
This is a full example on how you could use it:
import 'package:flutter/material.dart';
import 'package:date_range_picker/date_range_picker.dart' as DateRagePicker;
void main() {
runApp(MaterialApp(home: HomeScreen(), title: 'Flutter Date Range Example'));
}
class HomeScreen extends StatefulWidget {
HomeScreen({Key key}) : super(key: key);
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: MaterialButton(
color: Colors.deepOrangeAccent,
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")),
),
);
}
}
Here, I'm using Flutter inbuilt date range picker, where you should initially give the start and end date, to display the selected range used two elevated buttons where the date range will be shown.in setState, if you click cancel in daterange picker popup, the initial date range will be assigned.
import 'package:flutter/material.dart';
class DateRangeWidget extends StatefulWidget {
DateRangeWidget({Key? key}) : super(key: key);
#override
State<DateRangeWidget> createState() => _DateRangeWidgetState();
}
class _DateRangeWidgetState extends State<DateRangeWidget> {
DateTimeRange dateRange = DateTimeRange(
start: DateTime(2021, 11, 5),
end: DateTime(2022, 12, 10),
);
#override
Widget build(BuildContext context) {
final start = dateRange.start;
final end = dateRange.end;
return Column(children: [
const Text(
'Date Range',
style: TextStyle(fontSize: 16),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: ElevatedButton(
child: Text(
'${start.year}/${start.month}/${start.day}',
),
onPressed: pickDateRange,
),
),
Container(
margin: EdgeInsets.only(left: 20),
child: ElevatedButton(
child: Text(
'${end.year}/${end.month}/${end.day}',
),
onPressed: pickDateRange,
),
),
],
)
]);
}
Future pickDateRange() async {
DateTimeRange? newDateRange = await showDateRangePicker(
context: context,
initialDateRange: dateRange,
firstDate: DateTime(2019),
lastDate: DateTime(2023),
);
setState(() {
dateRange = newDateRange ?? dateRange;
// if (newDateRange == null) return;
// setState(() => dateRange = newDateRange);
});
}
}
[1]: https://i.stack.imgur.com/h1HIN.png
Related
I have the following code with a DateTimeRange and I would like to allow the user to select a range without exceed 15 days. For example a range before today will be 02/02 to 02/16 or lower or after today 02/14 to 03/01 or lower. How can I do that if it's possible ?
class DateRangeView extends StatefulWidget {
late bool isClearClicked;
DateRangeView({Key? key, required this.isClearClicked}) : super(key: key);
#override
DateRangeView createState() => DateRangeViewState();
}
class DateRangeViewState extends State<DateRangeView> {
DateTimeRange? dateRange;
DateTimeRange? initialDateRange;
DateTimeRange? newDateRange;
String getFrom() {
if (dateRange == null) {
return DateTime.now().toRangeDate();
} else {
return dateRange!.start.toRangeDate();
}
}
String getUntil() {
if (dateRange == null) {
return DateTime.now().toRangeDate();
} else {
return dateRange!.end.toRangeDate();
}
}
#override
Widget build(BuildContext context) {
return HeaderWidget(
title: Translation.current.period.toUpperCase(),
child: Row(
children: [
Expanded(
child: ButtonWidget(
text: widget.isClearClicked
? DateTime.now().toRangeDate()
: getFrom(),
onClicked: () => pickDateRange(context),
),
),
Text(Translation.current.untilDateKeyword.toUpperCase(),
style:
TextStyle(fontSize: ThemeSize.text(xl), color: ThemeColor.gray500),
),
Expanded(
child: ButtonWidget(
text: widget.isClearClicked
? DateTime.now().toRangeDate()
: getUntil(),
onClicked: () => pickDateRange(context),
),
),
Icon( Icons.calendar_month_rounded, color: ThemeColor.gray600,),
],
),
);
}
Future pickDateRange(BuildContext context) async {
initialDateRange = DateTimeRange(
start: DateTime.now(),
end: DateTime.now(),
);
newDateRange = await showDateRangePicker(
initialEntryMode: DatePickerEntryMode.calendarOnly,
context: context,
firstDate: DateTime(2022),
lastDate: DateTime(2030),
initialDateRange: dateRange ?? initialDateRange,
builder: (context, Widget? child) => Theme(
data:ThemeData.light().copyWith(
//Header background color
primaryColor: ThemeColor.primaryVariant,
scaffoldBackgroundColor: Colors.grey[50],
dividerColor: Colors.grey,
textTheme: TextTheme(
bodyText2:
TextStyle(color: ThemeColor.gray700, fontSize: ThemeSize.text(xxl)),
),
colorScheme: ColorScheme.fromSwatch().copyWith(
primary: ThemeColor.primaryVariant,
onSurface: Colors.black,
),
),
child: child!,
),
);
if (newDateRange == null) return;
setDateRange();
}
setDateRange() {
setState(() {
dateRange = newDateRange;
widget.isClearClicked = false;
});
Future.delayed(
const Duration(milliseconds: 100), () {
dateRange = initialDateRange;
},
);
}
}
You can use syncfusion_flutter_datepicker library. It has a selectionChanged callback that you can manipulate to limit selectable dates on the go. This is my answer to a similar question
class Home extends StatefulWidget {
const Home({super.key});
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
final DateTime _minDate = DateTime.now();
DateTime _maxDate = DateTime.now().add(const Duration(days: 365));
final Duration _duration = const Duration(days: 14);
DateTime _start = DateTime.now();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SfDateRangePicker(
minDate: _minDate,
maxDate: _maxDate,
selectionMode: DateRangePickerSelectionMode.range,
onSelectionChanged: (DateRangePickerSelectionChangedArgs args) {
if (args.value is PickerDateRange) {
_start = (args.value as PickerDateRange).startDate!;
setState(() {
// limit the maxDate to 14 days from selected date
_maxDate = _start.add(_duration);
});
}
},
)),
);
}
}
You can change this two params to :
firstDate: DateTime.now(),
lastDate: DateTime.now().add(Duration(days: 15)),
I want to pick a date from the calendarIcon(which is implemented with the showDatePicker()) inside a textField widget and feed it to the textField. But it is not working. And I want to manage the UI states using Provider. I Am showing my code below. Am I doing it wrong? or Is there another way to do it.
I tried doing the following way:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class SignUp extends StatelessWidget {
Widget build(BuildContext context) {
final validator = Provider.of<SignUpController>(context);
return Scaffold(
appBar: AppBar(
title: Text("Demo FormValidation Provider"),
),
body: Padding(
padding: EdgeInsets.all(8.0),
child: ListView(children: <Widget>[
TextField(
controller: TextEditingController()..text = "Enter DOB",
onChanged: (text) => {validator.date},
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(Icons.calendar_today_sharp),
onPressed: () => {validator.showCalender(context)},
),
),
)
])),
);
}
}
class SignUpController with ChangeNotifier {
Future<DateTime> showCalender(BuildContext context) {
notifyListeners();
return showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime.now(),
);
}
var date = DateTime.now().toString().split(' ')[0];
void getDate(BuildContext context) async {
var dateL = await showCalender(context);
date = (dateL.toLocal()).toString().split(' ')[0];
notifyListeners();
}
}
define a TextEditingController then pass data to it like this:
//define
TextEditingController text = TextEditingController(text:'');
//pass data
text = TextEditingController(text:data);
first, make ur widget stateful then add didChangeDependebcies() method it will listen.
void didChangeDependencies() {
super.didChangeDependencies();
setState(() {
getImages();
});
}
Future getImages() async {
//get ur data here from provider
}
SOLVED!!!
Finally did it..
Reference: For more information refer to this medium.com article by Pinkesh Darji
Solution
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// UI class
class SignUp extends StatelessWidget {
Widget build(BuildContext context) {
final validator = Provider.of<SignUpController>(context);
return Scaffold(
appBar: AppBar(
title: Text("Demo FormValidation Provider"),
),
body: Padding(
padding: EdgeInsets.all(8.0),
child: ListView(
children: <Widget>[
TextField(
controller: TextEditingController(
text: validator.selectedDate == null
? "Enter Date Of Birth"
: "${validator.selectedDate}".split(' ')[0]),
onChanged: (String value) {
validator.changeDOB(value);
},
decoration: InputDecoration(
labelText: "yyyy-mm-dd",
errorText: validator.dob.error,
suffixIcon: IconButton(
icon: Icon(Icons.calendar_today_sharp),
onPressed: () => {validator.selectDate(context)},
),
),
),
SizedBox(
height: 10,
),
ElevatedButton(
// The ElevatedButton inherits the color of the app.
child: Text("Submit"),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.all(10),
textStyle: TextStyle(
fontSize: 20,
),
),
onPressed: () {
print("Date: " + "${validator.selectedDate}".split(' ')[0]);
},
),
],
),
),
);
}
}
// Controller Class
class SignUpController with ChangeNotifier {
ValidationItem _dob = ValidationItem(null, null);
// Getters
ValidationItem get dob => _dob;
// making the showDatePicker method
DateTime selectedDate;
selectDate(BuildContext context) async {
DateTime picked = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime.now(),
// initialDatePickerMode: DatePickerMode.year,
);
if (picked != null && picked != selectedDate) {
selectedDate = picked;
}
notifyListeners();
}
// Setters
void changeDOB(String value) {
try {
// converting the DateTime data type into a String data type and only getting the
// Date(discarding the time)
value = selectedDate.toString().split(' ')[0];
_dob = ValidationItem(value, null);
} catch (error) {
_dob = ValidationItem(null, "Use Correct Format");
}
notifyListeners();
}
}
// Model Class
class ValidationItem {
final String value;
final String error;
ValidationItem(this.value, this.error);
}
NOTE01: You can use multiple consumers for the provider (or maybe not??)
NOTE02: Above answer will not make the initial text(TextEditingController) editable. It can be achieved using the EditableText widget or maybe some other functions. Try it.
I have an array with certain dates.
I want to disable these dates in the date picker and also change the color. How to do this?
You can use selectableDayPredicate property. For colors, you can change it by themes.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
DateTime selectedDate = DateTime(2020, 1, 14);
// DatePicker will call this function on every day and expect
// a bool output. If it's true, it will draw that day as "enabled"
// and that day will be selectable and vice versa.
bool _predicate(DateTime day) {
if ((day.isAfter(DateTime(2020, 1, 5)) &&
day.isBefore(DateTime(2020, 1, 9)))) {
return true;
}
if ((day.isAfter(DateTime(2020, 1, 10)) &&
day.isBefore(DateTime(2020, 1, 15)))) {
return true;
}
if ((day.isAfter(DateTime(2020, 2, 5)) &&
day.isBefore(DateTime(2020, 2, 17)))) {
return true;
}
return false;
}
Future<void> _selectDate(BuildContext context) async {
final DateTime picked = await showDatePicker(
context: context,
initialDate: selectedDate,
selectableDayPredicate: _predicate,
firstDate: DateTime(2019),
lastDate: DateTime(2021),
builder: (context, child) {
return Theme(
data: ThemeData(
primaryColor: Colors.orangeAccent,
disabledColor: Colors.brown,
textTheme:
TextTheme(body1: TextStyle(color: Colors.blueAccent)),
accentColor: Colors.yellow),
child: child,
);
});
if (picked != null && picked != selectedDate)
setState(() {
selectedDate = picked;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text("${selectedDate.toLocal()}".split(' ')[0]),
SizedBox(
height: 20.0,
),
RaisedButton(
onPressed: () => _selectDate(context),
child: Text('Select date'),
),
],
),
),
);
}
}
I encountered the same problem. After going back and forth, I did the following:
Created a list of specific dates:
static var unavailableDates = ["2020-08-14", "2020-08-20", "2020-08-13","2020-08-21","2020-08-23"];
Created an initial date variable:
static DateTime initialDate = DateTime.now();
formatted the initial date to get rid of the timestamp:
static DateFormat dateFormat = new DateFormat("yyyy-MM-dd");
String formattedDate = dateFormat.format(initialDate);
Put my unavailable dates in order:
unavailableDates.sort(((a, b) => a.compareTo(b)));
Added a way to verify the initial date does not fall on the disabled date. This jumps to the next available date. If you do not have something in place to verify unavailable date and initial date is not the same, you will get an exception:
for(var unavdate in unavailableDates){
if (unavdate.compareTo(formattedDate) == 0) {
formattedDate = unavdate;
fromStringDate = DateTime.parse(formattedDate);
initialDate = fromStringDate.add(new Duration(days: 1));
formattedDate = dateFormat.format(initialDate);
}
}
Created a day predicate function:
bool setDayPredicate(DateTime val) {
//this allows certain dates to be greyed out based on availability
String Dates = dateFormat.format(val); //formatting passed in value
return !unavailableDates.contains(Dates);
}
Put it all together:
date = await showDatePicker(
context: context,
initialDate: initialDate,
firstDate: new DateTime.now(),
lastDate: new DateTime.now().add(new Duration(days: 30)),
selectableDayPredicate: setDayPredicate, });
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
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();
});
}