Flutter How to set the maximum date range duration in DateTimeRange - flutter

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)),

Related

local state variable not updating

For some weird reason, my local state variable "_jobApplicationState" is not updating.
I see that it is updated in the database, but its not updating on my page.
If I leave the record and come back, everything works as expected.
I am driving this functionality by pressing the button 'Send inquiry'.
I took out a bunch of code to make it easy to read.
I got this to work for a minute at somepoint. but I forgot to save:(
class JobApplicationView extends StatefulWidget {
const JobApplicationView({Key? key}) : super(key: key);
#override
_JobApplicationViewState createState() => _JobApplicationViewState();
}
// https://youtu.be/VPvVD8t02U8?t=90350
class _JobApplicationViewState extends State<JobApplicationView> {
CloudJobApplication? _jobApplication;
final _formKey = GlobalKey<FormState>();
final currentUser = AuthService.firebase().currentUser!;
late final FirebaseCloudStorage _firebaseService;
//
late String _jobApplicationState;
//
late DateTime _jobApplicationStartDate;
late DateTime _jobApplicationEndDate;
//
bool? isJobCreatorSameAsJobApplicator;
String? _jobCreatorId;
String? _jobApplicatorId;
String? _jobDescription;
List? _jobUserData;
String? _jobAddress;
String? _jobType;
//
#override
void initState() {
super.initState();
_jobApplicationStartDate = DateTime.now();
_jobApplicationEndDate = DateTime.now();
_firebaseService = FirebaseCloudStorage();
// _jobDescriptionController = TextEditingController();
// _jobAreaCodeController = TextEditingController();
// _jobApplicationStateController = TextEditingController();
}
//Future<CloudJobApplication>
createOrGetExistingJob(BuildContext context) async {
final widgetJobApplication = context.getArgument<CloudJobApplication>();
if (widgetJobApplication != null) {
_jobApplication = widgetJobApplication;
_jobApplicationState = widgetJobApplication.jobApplicationState;
_jobApplicatorId = widgetJobApplication.jobApplicatorId;
_jobCreatorId = widgetJobApplication.jobCreatorId;
_jobDescription = widgetJobApplication.jobApplicationDescription;
return widgetJobApplication;
}
print('ELSE TRIGGERED!');
return widgetJobApplication;
}
void _updateJobField(localStateField, jobColumn, jobColumnValue) async {
//* localStateField: local field to update so that the build context is refreshed
//* jobColumn: the name of the column in the db
//* jobColumnValue: the value for the jobColumn
setState(() {
if (localStateField == '_jobApplicationState') {
_jobApplicationState = jobColumnValue;
}
});
await _firebaseService.updateJobApplicationColumn(
documentId: _jobApplication?.documentId as String,
fieldNameColumn: jobColumn,
fieldNameColumnValue: jobColumnValue,
);
}
sendInqury() {
print('setting job applications state!');
print('_jobApplicationState b4:: $_jobApplicationState');
_updateJobField(_jobApplicationState, jobApplicationStateColumn,
jobApplicationStateOpen);
print('_jobApplicationState after:: $_jobApplicationState');
}
#override
void dispose() {
//_deleteJobIfTextIsEmpty();
// _jobDescriptionController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('update job application'),
actions: [],
),
body: FutureBuilder(
future: createOrGetExistingJob(context),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
return Form(
key: _formKey,
child: ListView(
padding: const EdgeInsets.all(32.0),
children: [
//getStateChevrons(_jobApplicationState),
const Divider(
height: 20,
thickness: 5,
indent: 0,
endIndent: 0,
color: Colors.blue,
),
Text(_jobApplicationState),
TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.blue,
padding: const EdgeInsets.all(16.0),
textStyle: const TextStyle(fontSize: 20),
),
onPressed: sendInqury,
child: const Text('Send inquiry'),
)
],
),
);
default:
return const CircularProgressIndicator();
}
},
),
);
}
}
I figured out the answer, here is the answer code:
import 'dart:developer';
import 'package:flutter/material.dart';
import '../../services/cloud/cloud_job_application.dart';
import '/services/auth/auth_service.dart';
import '/utilities/generics/get_arguments.dart';
import '/services/cloud/firebase_cloud_storage.dart';
class JobApplicationView extends StatefulWidget {
const JobApplicationView({Key? key}) : super(key: key);
#override
_JobApplicationViewState createState() => _JobApplicationViewState();
}
// https://youtu.be/VPvVD8t02U8?t=90350
class _JobApplicationViewState extends State<JobApplicationView> {
CloudJobApplication? _jobApplication;
late final FirebaseCloudStorage cloudFunctions;
final _formKey = GlobalKey<FormState>();
final currentUser = AuthService.firebase().currentUser!;
// state varibles
String _jobApplicationState = 'default';
String _jobApplicationSubState = 'default';
late final TextEditingController _jobDescriptionController;
#override
void initState() {
super.initState();
cloudFunctions = FirebaseCloudStorage();
_jobDescriptionController = TextEditingController();
}
//Future<CloudJobApplication>
getExistingJobApplication(BuildContext context) async {
log('getExistingJobApplication()');
if (_jobApplicationState == 'default') {
var widgetJobApplication = context.getArgument<CloudJobApplication>();
log('first time openning job application, returning server data');
_jobApplication = widgetJobApplication;
_jobApplicationState =
widgetJobApplication?.jobApplicationState as String;
_jobDescriptionController.text =
widgetJobApplication?.jobApplicationDescription as String;
return widgetJobApplication;
} else {
log('job application has been updated, returnnig local data');
return cloudFunctions.getJobApplication(_jobApplication!.documentId);
}
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('update job application'),
actions: [],
),
body: FutureBuilder(
future: getExistingJobApplication(context),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
return Form(
key: _formKey,
child: ListView(padding: const EdgeInsets.all(32.0), children: [
Text(_jobApplicationState),
Text(_jobDescriptionController.text),
const Divider(
height: 20,
thickness: 5,
indent: 0,
endIndent: 0,
color: Colors.blue,
),
TextFormField(
controller: _jobDescriptionController,
maxLines: 5,
decoration: InputDecoration(
// enabled: _jobState == jobStateNew ? true : false,
hintText: "The toilet wont flush",
filled: true,
// fillColor: _jobState == jobStateNew ? Colors.white : Colors.grey,
label: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
color: Colors.white,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Padding(padding: EdgeInsets.only(left: 8.0)),
Icon(Icons.info_outline),
Padding(
padding: EdgeInsets.only(left: 8.0, right: 8.0),
child: Text("Job description"),
),
],
),
),
),
validator: (str) =>
str == '' ? "Job description can't be empty" : null,
),
TextButton(
onPressed: () async {
setState(() {
_jobApplicationState = 'Open';
});
await cloudFunctions.updateJobApplication(
documentId: _jobApplication?.documentId as String,
jobDescription: _jobDescriptionController.text,
jobApplicationState: 'Open',
);
},
child: const Text('update state')),
//
]),
);
default:
return const CircularProgressIndicator();
}
},
),
);
}
}
You should separate the UI and logic -> create a jobApplication Model.
Pack all your logic into a ChangeNotifier and notifyListeners on change.
This is also better for performance because it only rebuilds needed parts of the UI.
I can recommend using a ChangeNotifierProvider.
class JobApplicationProvider extends ChangeNotifier {
JobApplication jobapplication = BasicParam.standard;
void setJobApplication(json) async {
jobapplication = JobApplication.fromJson(json);
notifyListeners();
}
}
And in the build Method use it like this:
Widget build(BuildContext context) {
JobApplicationProvider jobApplication= Provider.of(context);
return Text(jobApplication.state);
}

Type mismatch in table_calendar

I am adding the table_calendar plugin to my flutter app. Most things are working so far thanks to the help I have been getting from y'all.
Here is the next issue I am getting. When I click on a date on the calendar I get the following error and then nothing happens.
======== Exception caught by gesture ===============================================================
The following _TypeError was thrown while handling a gesture:
type '_MapStream<QuerySnapshot<Map<String, dynamic>>, List<Event>>' is not a subtype of type 'Map<DateTime, List<dynamic>>'
When the exception was thrown, this was the stack:
#0 _AppointmentCalendarScreenState._onDaySelected.<anonymous closure> (package:tonnah/screens/appointment_calendar.dart:73:29)
Here is the code at line 73
void _onDaySelected(DateTime day, List events, List holidays) {
_eventsStream = _firestoreService.getEventStream(day);
setState(() {
_streamController.add(_eventsStream); <<<<< LINE 73
//_selectedEvents = _eventsStream;
});
}
Here is where I populate _eventsStream:
_eventsStream = _firestoreService.getEventStream(_selectedDay);
Stream<List<Event>> getEventStream(DateTime dateTime) {
return _db.collection('agency').doc(globals.agencyId).collection('event')
.where('eventDate', isGreaterThanOrEqualTo: Timestamp.fromDate(dateTime))
.snapshots().map((snapshot) => snapshot.docs
.map((document) => Event.fromFirestore(document.data()))
.toList());
}
It is also used here to put the event indicator on the calendar:
StreamBuilder(
//stream: _firestoreService.getEventStream(_selectedDay),
stream: _db.collection('agency').doc(globals.agencyId).collection('event')
.where('eventDate', isGreaterThanOrEqualTo: Timestamp.fromDate(_selectedDay))
.snapshots().map((snapshot) => snapshot.docs
.map((document) => Event.fromFirestore(document.data()))
.toList()),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Event> snapData;
//return snapData = snapshot.data;
_eventsStream = snapshot.data;
_eventsMap = convertToMap(_eventsStream);
//_selectedEventsMap = _eventsMap[_selectedDay] ?? [];
return _buildTableCalendar();
} else {
return CircularProgressIndicator();
}
},
),
I know there is a type difference but how do I make them match?
Here is the code from the calendar page:
// Example holidays
final Map<DateTime, List> _holidays = {
DateTime(2020, 1, 1): ['New Year\'s Day'],
DateTime(2020, 1, 6): ['Epiphany'],
DateTime(2020, 2, 14): ['Valentine\'s Day'],
DateTime(2020, 4, 21): ['Easter Sunday'],
DateTime(2020, 4, 22): ['Easter Monday'],
};
//final eventsRef = FirebaseFirestore.instance.collection('agency').doc(globals.agencyId).collection('event');
final _firestoreService = FirestoreService();
bool showSpinner = false;
var _eventsStream;
var _eventsMap;
final _selectedDay = DateTime.now();
class AppointmentCalendarScreen extends StatefulWidget {
AppointmentCalendarScreen({Key key, this.title}) : super(key: key);
final String title;
#override
_AppointmentCalendarScreenState createState() => _AppointmentCalendarScreenState();
}
class _AppointmentCalendarScreenState extends State<AppointmentCalendarScreen> with TickerProviderStateMixin {
//var _eventsList;
//Map<DateTime, List> _selectedEventsMap;
AnimationController _animationController;
CalendarController _calendarController;
StreamController<Map<DateTime, List>> _streamController;
#override
void initState() {
super.initState();
_streamController = StreamController();
_eventsStream = _firestoreService.getEventStream(_selectedDay);
_calendarController = CalendarController();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 400),
);
_animationController.forward();
}
#override
void dispose() {
_animationController.dispose();
_calendarController.dispose();
_streamController.close();
super.dispose();
}
void _onDaySelected(DateTime day, List events, List holidays) {
_eventsStream = _firestoreService.getEventStream(day);
setState(() {
_streamController.add(_eventsStream);
//_selectedEvents = _eventsStream;
});
}
void _onVisibleDaysChanged(DateTime first, DateTime last,
CalendarFormat format) {
}
void _onCalendarCreated(DateTime first, DateTime last,
CalendarFormat format) {
}
#override
Widget build(BuildContext context) {
final eventProvider = Provider.of<EventProvider>(context);
FirebaseFirestore _db = FirebaseFirestore.instance;
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/Appbar_logo.png',
fit: BoxFit.cover, height: 56),
],
),
),
backgroundColor: Colors.white,
resizeToAvoidBottomInset: false,
body: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
StreamBuilder(
stream: _firestoreService.getEventStream(_selectedDay),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Event> snapData;
_eventsStream = snapshot.data;
_eventsMap = convertToMap(_eventsStream);
//_selectedEventsMap = _eventsMap[_selectedDay] ?? [];
return _buildTableCalendar();
} else {
return CircularProgressIndicator();
}
},
),
const SizedBox(height: 8.0),
//_buildButtons(),
ElevatedButton(
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
globals.newAgency = true;
//eventProvider.saveEvent();
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => AddEventScreen()));
setState(() {
showSpinner = false;
});
} catch (e) {
// todo: add better error handling
print(e);
}
},
child: Text('Add Event'),
),
const SizedBox(height: 8.0),
Expanded(child: _buildEventList()),
],
),
);
}
// Simple TableCalendar configuration (using Styles)
Widget _buildTableCalendar() {
return TableCalendar(
calendarController: _calendarController,
locale: 'en_US',
events: _eventsMap,
holidays: _holidays,
startingDayOfWeek: StartingDayOfWeek.sunday,
calendarStyle: CalendarStyle(
selectedColor: Colors.deepOrange[400],
todayColor: Colors.deepOrange[200],
markersColor: Colors.deepPurpleAccent,
outsideDaysVisible: false,
),
headerStyle: HeaderStyle(
formatButtonTextStyle:
TextStyle().copyWith(color: Colors.white, fontSize: 15.0),
formatButtonDecoration: BoxDecoration(
color: Colors.deepOrange[400],
borderRadius: BorderRadius.circular(16.0),
),
),
onDaySelected: _onDaySelected,
onVisibleDaysChanged: _onVisibleDaysChanged,
onCalendarCreated: _onCalendarCreated,
);
}
Widget _buildHolidaysMarker() {
return Icon(
Icons.add_box,
size: 20.0,
color: Colors.blueGrey[800],
);
}
Widget _buildEventList() {
final _db = FirebaseFirestore.instance;
return Container(
child: StreamBuilder<QuerySnapshot>(
//stream: FirestoreService().getEvent(),
stream: _db.collection('agency').doc(globals.agencyId).collection(
'event').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: const Text(
'Loading...',
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
));
} else {
var doc = snapshot.data.docs;
return new ListView.builder(
itemCount: doc.length,
itemBuilder: (BuildContext context, int index) {
Event _event = Event.fromFirestore(
doc[index].data());
return ListTile(
isThreeLine: true,
title: Text(
'${_event.eventName ?? 'n/a'}',
style: TextStyle(
fontWeight: FontWeight.w900,
color: Colors.blueAccent),
),
subtitle: Text.rich(TextSpan(
text:
//'${_event.eventName ?? 'n/a'}, '
'${_event.eventStartTime ?? 'n/a'}, '
'Duration: ${_event.eventDuration ?? 'n/a'}',
children: <TextSpan>[
TextSpan(
text:
'\n${_event.eventDescription ?? 'n/a'}',
style: TextStyle(
fontWeight: FontWeight.w900,
color: Colors.blueGrey),
)
])),
//trailing: Text('MLS#: ${_event.mlsNumber ?? 'n/a'}'),
onTap: () {
globals.newTrxn = false;
/*
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
AddEventScreen(
doc[index].data())));
*/
},
);
}
);
}
},
),
);
}
Map<DateTime, List> convertToMap(List<Event> item) {
Map<DateTime, List> result;
for (int i = 0; i < item.length; i++) {
Event data = item[i];
//get the date and convert it to a DateTime variable
//DateTime currentDate = DateFormat('MM-dd-yyyy - kk:mm').format(data.eventDate);
DateTime currentDate = DateTime(data.eventDate.year, data.eventDate.month, data.eventDate.day, data.eventDate.hour, data.eventDate.minute);
List eventNames = [];
//add the event name to the the eventNames list for the current date.
//search for another event with the same date and populate the eventNames List.
for (int j = 0; j < item.length; j++) {
//create temp calendarItemData object.
Event temp = item[j];
//establish that the temp date is equal to the current date
if (data.eventDate == temp.eventDate) {
//add the event name to the event List.
eventNames.add(temp.eventName);
} //else continue
}
//add the date and the event to the map if the date is not contained in the map
if (result == null) {
result = {
currentDate: eventNames
};
} else {
result[currentDate] = eventNames;
}
return result;
}
}
}
You have to convert on type: Map<DateTime, List>.
Create method which will convert '_MapStream<QuerySnapshot<Map<String, dynamic>>, List>' on 'Map<DateTime, List>'
In your case: convert on 'Map<DateTime, List< "here you should add all data which you want to have in calendar view. Can be map or sth else">>
Actually, you need thre methods:
First: Convert method which I mentioned above. Method exemple below
final int _currentYearInt = DateTime.now().year;
final int _currentMonthInt =
int.parse(addZeroToNumberLowerThanTen(number: DateTime.now().month));
documentsList.forEach((doc) {
Map<DateTime, List<Shift>> _schedule = {};
// "days" is a properties with values: {"10" : [], "12": []}, where "10" is day of month"
doc['days'].forEach((k, v) {
int _shiftDay = int.parse(k);
if (calendarWithMarkedDates[
DateTime.utc(_currentYearInt, _currentMonthInt, _shiftDay)] ==
null) {
calendarWithMarkedDates[
DateTime.utc(_currentYearInt, _currentMonthInt, _shiftDay)] = [];
}
calendarWithMarkedDates[DateTime.utc(_currentYearInt, _currentMonthInt, _shiftDay)]!
.add( "your item. Can be object, map or what you want" );
});
});
Second: "calendarWithMarkedDates" is a LinkedHashMap, which holds all the marked dates. F.e. from server has response dates: 1.07, 5.07, 12.07. All dates after convert to LinkedHashMap are storing in this "calendarWithMarkedDates".
final calendarWithMarkedDates = LinkedHashMap<DateTime, List<Shift>>(
equals: isSameDay,
hashCode: getHashCode,
);
Third: "currentScheduleForDay" it is function which yu hava to invoke like a value of property "eventLoader" in TableCalendar without parameters.
It means:
good way currentScheduleForDay
bad way: currentScheduleForDay()
List<Shift> currentScheduleForDay(DateTime day) {
return currentCalendarWithMarkedDates[day] ?? [];
}
When use method currentScheduleForDay (without "()"), TableCalendar by it self made loops through all dates in month and inovke method for all day.

Make range date picker in flutter

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

Change ListTile Widget at Runtime Problem

https://pub.dev/packages/table_calendar
I created a calendar using the package i linked. Trying to update ListTile's leading image at runtime.
im trying to send data as string so every event has to have a spectacular string. i have to Data class to send variables to next page and it's working but image variable always stays null. Hope that someone can explain why setState function below is not working .Thanks.
class Calender extends StatefulWidget {
#override
_CalenderState createState() => _CalenderState();
}
class Data {
String title, image, subText, subTitle;
Data({this.image, this.title, this.subTitle, this.subText});
}
class _CalenderState extends State<Calender> with TickerProviderStateMixin {
Map<DateTime, List> _events;
List _selectedEvents, names;
String image = " ";
AnimationController _animationController;
CalendarController _calendarController;
#override
void initState() {
print("work");
super.initState();
final _selectedDay = DateTime.now();
_calendarController = CalendarController();
_events = {
_selectedDay: [
'TRY 1',
],
_selectedDay.add(Duration(days: 15)): ['TRY 2'],
_selectedDay.add(Duration(days: 20)): ['TTRY 3'],
};
_selectedEvents = _events[_selectedDay] ?? [];
_animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 400));
}
#override
void dispose() {
_animationController.dispose();
_calendarController.dispose();
super.dispose();
}
void _onDaySelected(DateTime day, List events) {
print('CALLBACK: _onDaySelected');
setState(() {
_selectedEvents = events;
//working
});
}
void _onVisibleDaysChanged(
DateTime first, DateTime last, CalendarFormat format) {
print('CALLBACK: _onVisibleDaysChanged');
}
void _onCalendarCreated(
DateTime first, DateTime last, CalendarFormat format) {
print('CALLBACK: _onCalendarCreated');
}
Data _data;
Data get data {
if (_data == null) {
_data = Data();
}
return _data;
}
#override
Widget build(BuildContext context) {
names = _events.values.toList();
//trying to match strings with elements of list here but not working
setState(() {
if (names[0] == 'TRY 1') {
//image =
image = 'https://www.w3schools.com/w3images/lights.jpg';
// 'https://b.zmtcdn.com/data/pictures/5/18869335/85b9f0bc435132ec116846fafc335030.jpg';
_data.subTitle = "8.01 PM door opening";
}
if (names[1] == 'TRY 2') {
image =
'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__340.jpg';
_data.subTitle = "8.02 PM door opening";
}
if (names[2] == 'TRY 3') {
image =
'https://previews.123rf.com/images/alexis84/alexis841404/alexis84140400557/27773925-planet-earth-and-blue-human-eye-elements-of-this-image-furnished-by-nasa-.jpg';
_data.subTitle = "8.01 PM door opening";
}
});
return SafeArea(
child: Container(
decoration: BoxDecoration(),
child: Scaffold(
body: Column(
children: [
_buildTableCalendar(),
SizedBox(
height: 10,
),
Expanded(
child: _buildEventList(image, data),
)
],
),
),
),
}
Widget _buildTableCalendar() {
return TableCalendar(
calendarController: _calendarController,
events: _events,
startingDayOfWeek: StartingDayOfWeek.monday,
calendarStyle: CalendarStyle(
selectedColor: Colors.deepOrange[400],
todayColor: Colors.deepOrange[200],
markersColor: Colors.brown[700],
outsideDaysVisible: false,
),
headerStyle: HeaderStyle(
formatButtonTextStyle:
TextStyle().copyWith(color: Colors.white, fontSize: 15.0),
formatButtonDecoration: BoxDecoration(
color: Colors.deepOrange[400],
borderRadius: BorderRadius.circular(16.0),
),
),
onDaySelected: _onDaySelected,
onVisibleDaysChanged: _onVisibleDaysChanged,
onCalendarCreated: _onCalendarCreated,
);
}
Widget _buildEventList(String image, Data data) {
print(data.image);
return ListView(
children: _selectedEvents
.map((event) => Container(
decoration: BoxDecoration(
border: Border.all(width: 0.8),
borderRadius: BorderRadius.circular(12.0),
),
margin:
const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
child: ListTile(
subtitle: Text("8 pm"),
leading: CircleAvatar(
//image comes here null
backgroundImage: NetworkImage(image),
),
title: Text(event.toString()),
onTap: () {
setState(() {
if (event.toString() == "Beyond Akaretler") {
print("check1");
setState(() {
data.image =
"https://b.zmtcdn.com/data/pictures/5/18869335/85b9f0bc435132ec116846fafc335030.jpg";
data.title = "TRY1";
});
}
if (event.toString() == "Dorock XL") {
setState(() {
data.image =
"https://www.habervesaire.com/wp-content/uploads/2019/03/dorock-xl.jpg-1547825520-810x456.jpeg";
data.title = "TRY2 ";
});
}
if (event.toString() == "The Wall Kadıköy") {
setState(() {
data.image =
"https://b.zmtcdn.com/data/pictures/2/19165732/302d1418f06c81dca13e7c06bcc44ba9.jpg?fit=around|750:500&crop=750:500;*,*";
data.title = event.toString();
});
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EventPage(
data: data,
)));
});
},
),
))
.toList(),
);
}
}
class EventPage extends StatelessWidget {
final Data data;
const EventPage({Key key, this.data}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage("${data.image}"), fit: BoxFit.cover)),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
backgroundColor: Colors.black,
title: Text("${data.title}"),
),
),
);
}
}

Flutter Navigator.pop() keeps the Dialog Box partially visible with black shadow background

sorry for the long explaination but I think I have to be very clear about the topic.
I have been running in this issue for last couple of weeks. I am using flutter_bloc package.
I have a simple Search page(ProposalSearchPage) with searchbox and listview. Listview gets build based on the search keyword which dispatches KeywordsearchEvent. on Clicking the setting icon it opens of ProposalSearchSetting page which is a dialog box page.
Based on this structure I have two events used in the bloc. One for the keyword search and another for the Advance filter search.
In advance searching page after applying filters. Inside a button there I dispatched FilterSearchEvent and have used Navigation.pop(context) to return to ProposalSearchPage.
Based on the state change The listview renders for both the searches, but for the Adavance filter search, the ProposalSearchSetting dialog box is partially visible. The filter button in there are clickable. It dismiss when I click backarrow button.
I don't know why Navigator.pop is adding page to stack instead of popping the stack.
#PROPOSALSEARCH PAGE
class ProposalSearchPage extends StatefulWidget {
final UserProfileBloc userProfileBloc;
final MenuBloc menuBloc;
final String authToken;
ProposalSearchPage({this.userProfileBloc, this.menuBloc, this.authToken})
: assert(menuBloc != null),
assert(userProfileBloc != null);
#override
_ProposalSearchPageState createState() => _ProposalSearchPageState();
}
class _ProposalSearchPageState extends State<ProposalSearchPage> {
UserProfileBloc get _userProfileBloc => widget.userProfileBloc;
List filteredProposal = [];
String get _authToken => widget.authToken;
MenuBloc get _menuBloc => widget.menuBloc;
ProposalSearchBloc _proposalSearchBloc;
String searchedKeyword = "";
int searchProposalPage = 1;
#override
void initState() {
_proposalSearchBloc =
ProposalSearchBloc(proposalRepository: ProposalListingRepo());
_menuBloc.dispatch(MenuResponseFetchedEvent());
super.initState();
}
#override
void dispose() {
_proposalSearchBloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color(0xff2b57ff),
leading: IconButton(
icon: Icon(Icons.chevron_left),
onPressed: () {
Navigator.of(context).pop();
},
),
actions: <Widget>[
IconButton(
icon: new Icon(CupertinoIcons.gear_big),
onPressed: () {
/* Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProposalSearchSetting(
proposalSearchBloc: _proposalSearchBloc,
menuBloc: _menuBloc,
userProfileBloc: _userProfileBloc,
context: context),
fullscreenDialog: true,
),
);*/
showDialog<FilterProposalPost>(
context: context,
builder: (context) {
return ProposalSearchSetting(
proposalSearchBloc: _proposalSearchBloc,
menuBloc: _menuBloc,
userProfileBloc: _userProfileBloc,
context: context);
});
}),
],
title: Center(
child: Container(
width: 250.0,
height: 35.0,
decoration: BoxDecoration(
color: Colors.black12,
borderRadius: BorderRadius.all(Radius.circular(7.0))),
child: CupertinoTextField(
placeholder: 'search here.',
style: TextStyle(
color: Colors.white,
),
onSubmitted: (keyword) {
print(keyword);
searchedKeyword = keyword;
FilterProposalPost filterProposalPost =
_buildSearchQueryParameter(keyword);
// print(query);
_proposalSearchBloc.proposalFilterPostParam(filterProposalPost);
},
),
),
),
),
body: SearchListing(_proposalSearchBloc, _authToken),
);
}
FilterProposalPost _buildSearchQueryParameter(String keyword) {
return FilterProposalPost(
........
);
}
}
}
class SearchListing extends StatelessWidget {
final ProposalSearchBloc _proposalSearchBloc;
final String _authToken;
SearchListing(this._proposalSearchBloc, this._authToken);
#override
Widget build(BuildContext context) {
return BlocBuilder(
bloc: _proposalSearchBloc,
// ignore: missing_return
builder: (context, state) {
if (state is ProposalSearchFetchingState) {
return Center(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation(Color(0xff2b57ff))),
);
} else if (state is ProposalSearchFetchedState) {
final filteredProposal = state.filteredProposal;
print(filteredProposal.length.toString);
return _buildSearchProposalList(filteredProposal);
}
},
);
}
Widget _buildSearchProposalList(List searchedProposals) {
return ListView.builder(
itemCount: searchedProposals.length + 1,
itemBuilder: (context, position) {
return position >= searchedProposals.length
? _buildLoaderListItem()
: ProposalCardFactory(
proposal: searchedProposals[position],
authToken: _authToken,
);
});
}
Widget _buildLoaderListItem() {
return Center(
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation(Color(0xff2b57ff))));
}
}
#ProposalSearchSettingPage
class ProposalSearchSetting extends StatefulWidget {
final UserProfileBloc userProfileBloc;
final ProposalSearchBloc proposalSearchBloc;
final MenuBloc menuBloc;
final BuildContext context;
final Function() notifyParent;
ProposalSearchSetting({this.notifyParent,
this.proposalSearchBloc,
this.userProfileBloc,
this.menuBloc,
this.context});
#override
_ProposalSearchSettingState createState() =>
_ProposalSearchSettingState();
}
class _ProposalSearchSettingState extends State<ProposalSearchSetting>
with SingleTickerProviderStateMixin {
UserProfileBloc get _userProfileBloc => widget.userProfileBloc;
ProposalSearchBloc get _proposalSearchBloc => widget.proposalSearchBloc;
List<String> selectedOptions = [];
String resultBy;
List<String> industries;
List<String> stages;
List<String> locations;
List<String> languages;
List<String> countries;
List<String> regionsValue = [];
MenuBloc get _menuBloc => widget.menuBloc;
Animation<double> animation;
AnimationController controller;
double startingPoint;
#override
void initState() {
super.initState();
}
#override
void dispose() {
_userProfileBloc.dispose();
_proposalSearchBloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
//double startingPoint = MediaQuery.of(context).size.height;
return MaterialApp(
theme: ThemeData(
buttonTheme: ButtonThemeData(
minWidth: 200.0,
height: 40.0,
buttonColor: Color(0xff2b57ff),
textTheme: ButtonTextTheme.primary)),
home: Scaffold(
body: BlocBuilder(
bloc: _menuBloc,
// ignore: missing_return
builder: (context, state) {
if (state is MenuResponseFetchedState) {
MenuListData _menuListData = state.menuListData;
return Padding(
padding: const EdgeInsets.only(top: 100.0),
child: Center(
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () async {
resultBy = await showDialog(
context: context,
builder: (context) {
return ResultBySearchDialog(
userProfileBloc: _userProfileBloc,
menuListData: _menuListData,
title: 'Result By:',
options: _menuListData.displayBy.values
.toList());
});
},
color: Color(0xff2b57ff),
child: Text(
'RESULT BY',
style: TextStyle(fontFamily: 'MyRaidPro'),
),
),
SizedBox(
height: 20,
),
RaisedButton(
onPressed: () async {
countries = await showDialog(
context: context,
builder: (context) {
return CountrySearchDialog(
userProfileBloc: _userProfileBloc,
menuListData: _menuListData,
title: 'Select Countries',
selectedOptions: selectedOptions,
onSelectedOptionListChanged: (options) {
selectedOptions = options;
print(selectedOptions);
});
});
},
color: Color(0xff2b57ff),
child: Text(
'COUNTRY',
style: TextStyle(fontFamily: 'MyRaidPro'),
),
),
SizedBox(
height: 20,
),
RaisedButton(
onPressed: () async {
industries = await showDialog(
context: context,
builder: (context) {
return IndustrySearchDialog(
menuListData: _menuListData,
title: 'Select Industries',
options: _menuListData.industries.values
.toList(),
selectedOptions: selectedOptions,
onSelectedOptionListChanged: (options) {
selectedOptions = options;
print(selectedOptions);
});
});
},
child: Text(
'INDUSTRIES',
style: TextStyle(fontFamily: 'MyRaidPro'),
),
),
SizedBox(
height: 20,
),
RaisedButton(
onPressed: () async {
stages = await showDialog(
context: context,
builder: (context) {
return StageSearchDialog(
context: context,
menuListData: _menuListData,
title: 'Select Stages',
options:
_menuListData.stages.values.toList(),
selectedOptions: selectedOptions,
onSelectedOptionListChanged: (options) {
selectedOptions = options;
print(selectedOptions);
});
});
},
child: Text(
'STAGES',
style: TextStyle(fontFamily: 'MyRaidPro'),
),
),
SizedBox(
height: 20,
),
RaisedButton(
onPressed: () async {
languages = await showDialog(
context: context,
builder: (context) {
return LanguageSearchDialog(
menuListData: _menuListData,
title: 'Select Languages',
options: _menuListData.languages.values
.toList(),
selectedOptions: selectedOptions,
onSelectedOptionListChanged: (options) {
selectedOptions = options;
print(selectedOptions);
});
});
},
child: Text(
'LANGUAGES',
style: TextStyle(fontFamily: 'MyRaidPro'),
),
),
SizedBox(
height: 20,
),
RaisedButton(
onPressed: () async {
locations = await showDialog(
context: context,
builder: (context) {
return LocationSearchDialog(
menuListData: _menuListData,
title: 'Select Locations',
options: _menuListData.locations.values
.toList(),
selectedOptions: selectedOptions,
onSelectedOptionListChanged: (options) {
selectedOptions = options;
print(selectedOptions);
});
});
},
child: Text(
'LOCATIONS',
style: TextStyle(fontFamily: 'MyRaidPro'),
),
),
SizedBox(
height: 40,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
ButtonTheme(
textTheme: ButtonTextTheme.primary,
minWidth: 60,
child: RaisedButton(
onPressed: () {
Navigator.of(this.widget.context).pop();
},
color: Color(0xff2b57ff),
child: Text(
'Cancel',
style: TextStyle(fontFamily: 'MyRaidPro'),
),
),
),
ButtonTheme(
textTheme: ButtonTextTheme.primary,
minWidth: 60,
child: RaisedButton(
onPressed: () {
_proposalSearchBloc.dispatch(ProposalFilterFetchEvent(advanceFilter:
FilterProposalPost(......)));
Navigator.pop(context);
print(("value from dialog" +
industries.toString()));
print(("value from dialog" +
stages.toString()));
print(("value from dialog" +
locations.toString()));
print(("value from dialog" +
languages.toString()));
},
color: Color(0xff2b57ff),
child: Text(
'Apply',
style: TextStyle(fontFamily: 'MyRaidPro'),
),
),
)
],
)
],
),
),
),
);
}
},
),
),
);
}
}
#BLOC
class ProposalSearchBloc
extends Bloc<ProposalSearchEvent, ProposalSearchState> {
final ProposalListingRepo proposalRepository;
List keywordSearchedProposalList = List();
List filteredProposalList = List();
ProposalSearchBloc({this.proposalRepository});
void proposalFilterPostParam(FilterProposalPost filterProposalPost) {
dispatch(ProposalSearchFetchEvent(filterProposalPost: filterProposalPost));
}
#override
ProposalSearchState get initialState => ProposalSearchFetchingState();
#override
Stream<ProposalSearchState> mapEventToState(event) async* {
try {
var filteredProposal;
print("proposal search even fired first time");
if (event is ProposalSearchFetchEvent) {
filteredProposal =
await proposalRepository.filterProposal(event.filterProposalPost);
} else if (event is ProposalFilterFetchEvent) {
print("filter event");
filteredProposal =
await proposalRepository.filterProposal(event.advanceFilter);
filteredProposalList.addAll(filteredProposal);
yield ProposalSearchFetchedState(filteredProposal: filteredProposal);
}
} catch (_) {
//print(error.toString());
yield ProposalSearchErrorState();
}
}
}
Finally I was able to solve the problem. I just forgot to use try catch blok in my bloc. This solves most of the problems of reloading the previous page after changing configuration from another page (DialogBox Box probably). I just had to make few changes in the Bloc code as:
#override
Stream<ProposalSearchState> mapEventToState(event) async* {
yield ProposalSearchFetchingState();
if (event is ProposalSearchFetchEvent) {
try {
print("proposal search");
filteredProposal =
await proposalRepository.filterProposal(event.filterProposalPost);
yield ProposalSearchFetchedState(
searchedProposal: filteredProposal);
} catch (error) {
print(error);
}
if (event is ProposalFilterFetchEvent) {
try {
print("proposal filtered");
filteredProposal =
await proposalRepository.filterProposal(event.filterProposalPost);
yield ProposalFilteredFetchedState(
filteredProposal: filteredProposal);
} catch (error) {
print(error.toString());
}
}
}
}