Reputation: 891
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;
}
}
}
Upvotes: 0
Views: 291
Reputation: 157
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.
Upvotes: 1