How to show Event on Table Calendar using my API on flutter

2,916

Solution 1

_event was not used in the newer version of table_calendar. you can use eventLoader it will display your events on the calendar.

Example:

calendar widget as follows,

TableCalendar<Event>(
            firstDay: kFirstDay,
            lastDay: kLastDay,
            focusedDay: _focusedDay,
            selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
            eventLoader: _getEventsForDay,  // this will load events on calendar
            ),

_getEventsForDay method as follows,

 List<Event> _getEventsForDay(DateTime day) {
    return kEvents[day] ?? [];
  }

kEvents (event list which you need to mark on the calendar) is as follows, you don't need to create this one if you have a list of events.

final kEvents = LinkedHashMap<DateTime, List<Event>>(
  equals: isSameDay,
  hashCode: getHashCode,
)..addAll(_kEventSource);


final _kEventSource = Map.fromIterable(List.generate(50, (index) => index),
    key: (item) => DateTime.utc(kFirstDay.year, kFirstDay.month, item * 5),
    value: (item) => List.generate(
        item % 4 + 1, (index) => Event('Event $item | ${index + 1}')))
  ..addAll({
    kToday: [
      Event('Event 1'),
      Event('Event 2'),
    ],
  });

Event class as follows, (as well you can make it as you prefer)

class Event {
  final String title;

  const Event(this.title);

  @override
  String toString() => title;
}

Hope you got it!

Solution 2

Well folks now I'm going to show you the way I found to show events and cards coming from an api using the table calendar. I don't even need to say that this is the way I found things, feel free to add new things and give tips here. so let's go.

first of all we will show the calendar events, but in this step only the markers, your data coming from the api must contain dates if you are here, in my case the dates come as a String, so let's create the model for them

import 'dart:convert';

class EventsModel {
  final String dataDoJob;
  EventsModel({
    required this.dataDoJob,
  });
  

  Map<String, dynamic> toMap() {
    return {
      'data_acao': dataDoJob,
    };
  }

  factory EventsModel.fromMap(Map<String, dynamic> map) {
    return EventsModel(
      dataDoJob: map['data_acao'],
    );
  }

  String toJson() => json.encode(toMap());

  factory EventsModel.fromJson(String source) => EventsModel.fromMap(json.decode(source));
}

this is my model, as you can see I'm just getting the dates. Now let's do the get method to retrieve this data from the api, I'm using getConnect but you can use the http client you want.

 @override
  Future<List<EventsModel>> getEvents() async {
    SharedPreferences sharedPreferences = await SharedPreferences.getInstance();

    final int? id = sharedPreferences.getInt("idInfluencer");
    final String token = sharedPreferences.getString("token") ?? "";

    final Response result = await _restClient.get<List<EventsModel>>(
        "/job_acoes?influenciador_id=${id.toString()}",
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
          'Authorization': 'Bearer $token'
        }, decoder: (data) {
      if (data != null) {
        return data
            .map<EventsModel>((event) => EventsModel.fromMap(event))
            .toList();
      }
      return <EventsModel>[];
    });
    if (result.hasError) {
      print(result.statusCode);
      throw "Erro ao buscar dados";
    }

    print(result.body);
    print(result.statusCode);

    return result.body;
  }

Well done that we already have a list of dates in hand, but in my case they are strings so I'll have to convert them, like this:

final events = await _jobsServices.getEvents();
        //final dateFormat = DateFormat("yyyy-MM-dd");
        final eventsConvert =
            events.map((date) => (DateTime.parse(date.dataDoJob))).toList();

        eventsList.assignAll(eventsConvert);

        print("Lista de eventos : $eventsList");

        streamController.add(events);

on the first line I'm saving the list in a variable called events and right below I'm converting the strings into date time using the map method, And adding them to an empty list which I created then step by step and this: Create an empty list and add the converted data to it as I did above, my empty list is called eventsList done that we will show this list in the table calendar

class CalendarWidget extends GetView<HomeController> {
  const CalendarWidget({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<List<EventsModel>>(
        stream: controller.streamController.stream,
        builder: (context, snapshot) {
          return Obx(() {
            return TableCalendar(
              eventLoader: (day) => controller.eventsList.where((event) => isSameDay(event,day)).toList(), //THIS IS IMPORTANT
       
              focusedDay: controller.focusedDay.value,
              firstDay: DateTime(2019),
              lastDay: DateTime(2050),
              headerStyle:
                  const HeaderStyle(formatButtonVisible: false), //WEEK VISIBLE
              locale: 'pt_BR',
              daysOfWeekVisible: true,
              calendarFormat: controller.format.value,
              onFormatChanged: (CalendarFormat _format) =>
                  controller.calendarFormat(_format),
              onDaySelected: (DateTime userSelectedDay, DateTime focusedDay) =>
                  controller.selectedDay(userSelectedDay, focusedDay),
              calendarStyle: CalendarStyle(
                  selectedTextStyle: const TextStyle(color: Colors.white),
                  isTodayHighlighted: true,
                  selectedDecoration: BoxDecoration(
                      color: context.buttomThemeClicled,
                      shape: BoxShape.circle)),
              selectedDayPredicate: (DateTime date) {
                return isSameDay(controller.focusedDay.value, date);
              },
            );
          });
        });
  }
}

remembering that i'm using a stateless widget so i need a state manager, and i use getx so it has an obx involving the whole calendar.

in the event loader some people have doubts, you can pass a list or a map on it, in my case I'm working with a list for simplicity's sake, notice that in the event loader I'm doing a simple filtering, comparing the dates of the my calendar with the dates I get from the api, simple isn't it? by doing this your api-based bookmarks will already be shown. Ah, a detail, the stream builder serves to redo my widget whenever there are changes in the api, if you don't know how to work with it, this video will explain: https://www.youtube.com/watch?v=BBelgajHgzY

now let's go to the display part of your events based on days, my events will be displayed on cards like this:

enter image description here

so I built it on a separate page from my home, this part is important because your code will be easier and cleaner, with the widget done, we'll show them on screen like this:

child: Obx(() {
                          return ListView(
                            scrollDirection: Axis.vertical,
                            children: controller.cards
                                .map(
                                  (c) => AgendaCards(
                                    bottomPosition: 80,
                                    leftPositioned: 260,
                                    maxRadius: 5,
                                    rightPositioned: 5,
                                    secondMaxradius: 5,
                                    topPositioned: 20,
                                    model: c,
                                  ),
                                )
                                .toList(),
                          );
                        }));

the widget called calendar cards is nothing more than the card in the photo above, on it I asked for a model called

final JobsDescriptionCardsModel model;

and called him in the constructor

AgendaCards({
    required this.leftPositioned,
    required this.rightPositioned,
    required this.topPositioned,
    required this.bottomPosition,
    required this.maxRadius,
    required this.secondMaxradius,
    required this.model, //HERE
    Key? key,
  }) : super(key: key);

so let's create this model

class JobsDescriptionCardsModel {
  final String descricaoJob;
  final String dataDoJob;
  final String horarioDoJob;
  final int jobId;
  final String nome;
  JobsDescriptionCardsModel({
    required this.descricaoJob,
    required this.dataDoJob,
    required this.horarioDoJob,
    required this.jobId,
    required this.nome,
  });
  

  Map<String, dynamic> toMap() {
    return {
      'descricaoJob': descricaoJob,
      'dataDoJob': dataDoJob,
      'horarioDoJob': horarioDoJob,
      'jobId': jobId,
      'nome': nome,
    };
  }

  factory JobsDescriptionCardsModel.fromMap(Map<String, dynamic> map) {
    return JobsDescriptionCardsModel(
      descricaoJob: map['descricao'] ?? "",
      dataDoJob: map['data_acao'] ?? "",
      horarioDoJob: map['hora_inicial_acao'],
      jobId: map['job_acao_id'] ?? 0,
      nome: map['job'] ["cliente"] ["nome"] ?? "",
    );
  }

  String toJson() => json.encode(toMap());

  factory JobsDescriptionCardsModel.fromJson(String source) => JobsDescriptionCardsModel.fromMap(json.decode(source));
}

and get it on the api

 @override
  Future<List<JobsDescriptionCardsModel>> getJobsDescrition() async {
    SharedPreferences sharedPreferences = await SharedPreferences.getInstance();

    final int? id = sharedPreferences.getInt("idInfluencer");
    final String token = sharedPreferences.getString("token") ?? "";

    final result = await _restClient.get<List<JobsDescriptionCardsModel>>(
        "/job_acoes?influenciador_id=${id.toString()}",
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
          'Authorization': 'Bearer $token'
        }, decoder: (data) {
      if (data != null) {
        return data
            .map<JobsDescriptionCardsModel>(
                (j) => JobsDescriptionCardsModel.fromMap(j))
            .toList();
      }

      return <JobsDescriptionCardsModel>[];
    });

    if (result.hasError) {
      throw ("erro ao buscar dados");
    }
    return result.body ?? <JobsDescriptionCardsModel>[];
  }

the api gives me a list that is important. With the list in hand, we're going to get into the concepts of the table calendar. To proceed and understand what will be done, I recommend that you watch this video: https://www.youtube.com/watch?v=HKuzPQUV21Y&t=291s

having done the configuration of your calendar, I believe you have noticed that when you click on a date and print the variable that has the date data, you will notice that the table calendar gives you a date time as a return, and if you have rendered it the attention in my model i also have a date coming from the api, knowing that we only need to filter the list coming from the api based on the table calendar data like this:

first create an empty list like we did before and a list that will be filtered:

  //LISTA DE JOBS CARDS

  final cards = <JobsDescriptionCardsModel>[].obs;

  //LISTA FILTRADA

  var cardsFiltered = <JobsDescriptionCardsModel>[];

the empty list will be filled with the api data like this:

final jobsCards = await _jobsServices.getJobsDescrition();
        cards.assignAll(jobsCards);

and with the populated list in hand we will filter this list based on the api dates, like this:

cardsFiltered = jobsCards;

        var novaLista = cardsFiltered.where((model) {
          return model.dataDoJob
              .toString()
              .contains(focusedDay.value.toString().substring(1, 10));
        });

see that first I assign my populated list to a new list, then I filtered this list based on my model only in the part that contains the date that I stringed, comparing with my variable that has the date when I click on it remembers ? also converted to string, as the table calendar gives me the date along with other numbers that I believe are time information I got the data only from index 1 to 10 which will give me exactly the date contained in the variable. With that done, the table calendar has a property called onDaySelected, and it will display our filtered list like this:

selectedDay(DateTime selectedDayInfo, DateTime focusDayInfo) {
    userSelectedDay.value = selectedDayInfo;
    focusedDay.value = focusDayInfo;
    print(userSelectedDay.value);
    print(focusedDay.value);

    print("Lista de eventos 2 ${eventsList}");

    var novaLista = cardsFiltered.where((model) {
      return model.dataDoJob
          .toString()
          .contains(focusedDay.value.toString().substring(0, 10));
    });

    cards.assignAll(novaLista);

I created this separate function in a controller and called it in table calendar like this:

onDaySelected: (DateTime userSelectedDay, DateTime focusedDay) =>
                  controller.selectedDay(userSelectedDay, focusedDay),

done that your calendar will already be displaying the default markers and your cards based on the widget you built, remember to pass the data to your cards using the model you requested via the constructor because it contains the api data. I hope I have help and I'm sorry for the google translator english

Share:
2,916
莎拉Sarah
Author by

莎拉Sarah

Updated on December 30, 2022

Comments

  • 莎拉Sarah
    莎拉Sarah over 1 year

    I have UI for show event calendars and I need to show events from my API. but I don't know how to do it. I try to change List on _event but there's no response. And I need to show it on the calendar so my company calendar can show the event.

    this my UI Calendar

    import 'package:intl/intl.dart';
    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    import 'package:urus_flutter/persentation/custom_color.dart';
    import 'package:urus_flutter/persentation/custom_text_style.dart';
    import 'package:table_calendar/table_calendar.dart';
    import 'package:shared_preferences/shared_preferences.dart';
    
    class Calender extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => CalenderState();
    }
    
    class CalenderState extends State<Calender> {
      CalendarController _controller;
      Map<DateTime, List<dynamic>> _events;
      List<dynamic> _selectedEvents;
      DateTime _selectedDate;
      SharedPreferences prefs;
    
    
      @override
      void initState(){
        super.initState();
        _controller = CalendarController();
        _events = {
          DateTime(2021, 6, 22) : ['Meeting URUS', 'Testing Danai Mobile', 'Weekly Report', 'Weekly Meeting'],
          DateTime(2021, 6, 25) : ['Weekly Testing'],
          DateTime(2021, 6, 4) : ['Weekly Testing'],
          DateTime(2021, 6, 11) : ['Weekly Testing'],
          DateTime(2021, 6, 18) : ['Weekly Testing'],
        };
      }
    
    
      Map<String, dynamic> encodeMap(Map<DateTime, dynamic> map) {
        Map<String, dynamic> newMap = {};
        map.forEach((key, value) {
          newMap[key.toString()] = map[key];
        });
        return newMap;
      }
    
      Map<DateTime, dynamic> decodeMap(Map<String, dynamic> map) {
        Map<DateTime, dynamic> newMap = {};
        map.forEach((key, value) {
          newMap[DateTime.parse(key)] = map[key];
        });
        return newMap;
      }
    
    
      Widget build(BuildContext context) {
        return Scaffold(
          body: Stack(
            children: [
              ListView(
                padding: EdgeInsets.only(left: 16, right: 16, top: 52, bottom: 126),
                children: [
                  Text("Kalender Kegiatan",
                    style: CustomTextStlye.proxima_bold_18_black,),
                  Container(
                    child: Padding(
                      padding: const EdgeInsets.only(top: 28.0),
                      child: Text("Kalender Anda",
                              style: CustomTextStlye.proxima_bold_16_black,),
                    ),
                  ),
    
                  SizedBox(
                    height: 20,
                  ),
    
                  Container(
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Container(
                          decoration: BoxDecoration(
                            color: Colors.white,
                            borderRadius: BorderRadius.circular(5.0),
                            boxShadow: [
                              BoxShadow(
                                  offset: Offset(0, -1),
                                  color: CustomColor.border_grey,
                                  blurRadius: 3.0,
                                  spreadRadius: 1.0)
                            ]
                          ),
    
                          child: TableCalendar(
                            initialCalendarFormat: CalendarFormat.month,
                            calendarStyle: CalendarStyle(
                              todayColor: Color(0x9429AAE1),
                              todayStyle: CustomTextStlye.proxima_bold_12_white,
                              selectedColor: Color(0xFF29AAE1),
                              selectedStyle: CustomTextStlye.proxima_bold_12_white,
                              weekdayStyle: CustomTextStlye.proxima_bold_12_black,
                              weekendStyle: CustomTextStlye.proxima_bold_12_red,
                              unavailableStyle: CustomTextStlye.proxima_bold_12,
                              holidayStyle: CustomTextStlye.proxima_bold_12_red,
                              markersColor: Color(0xFFA2CD3A),
                            ),
                            headerStyle: HeaderStyle(
                              centerHeaderTitle: true,
                              formatButtonVisible: false,
                              titleTextStyle: CustomTextStlye.proxima_bold_14_black,
                            ),
                            availableCalendarFormats: const {CalendarFormat.month: '',},
                            startingDayOfWeek: StartingDayOfWeek.monday,
                            calendarController: _controller,
                            events: _events,
                            onDaySelected: (date, events,holidays) {
                              setState(() {
                                _selectedEvents = events;
                                _selectedDate = date;
                              });
                            },
                          ),
                        )
                      ],
                    ),
                  ),
    
                  Container(
                    child:Padding(
                      padding: const EdgeInsets.only(top: 28.0),
                      child: Text("Kegiatan Anda",
                        style: CustomTextStlye.proxima_bold_16_black,),
                    ),
                  ),
    
                  Container(
                    child: _selectedEvents != null ? Column(
                      children: List.generate(_selectedEvents.length, (index) =>
                          Container(
                            padding: const EdgeInsets.all(8.0),
                            child: Container(
                              height: MediaQuery.of(context).size.height/15,
                              decoration: BoxDecoration(
                                  border: Border(bottom: BorderSide(color: Color.fromRGBO(228, 228, 228, 1)))
    
                              ),
                              child:
                              Center(
                                child:
                                    Container(child:
                                    Row(
                                      children: [
                                        Padding(
                                          padding: const EdgeInsets.all(8.0),
                                          child: Container(
                                            padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 10.0),
                                            height: MediaQuery.of(context).size.height/10,
                                            decoration: BoxDecoration(
                                              border: Border.all(color:Color(0xFF29AAE1)),
                                              color:Color(0xFF29AAE1),
                                              borderRadius: BorderRadius.circular(3.0),
                                            ),
                                            child: Text(DateFormat('d').format(_selectedDate),
                                              style: CustomTextStlye.proxima_bold_18_white,
                                            ),
                                          ),
                                        ),
    
                                        Text(_selectedEvents[index],
                                          style: CustomTextStlye.proxima_bold_14_black,
                                        ),
                                      ],
                                    ),
                                    )
    
                              ),
                            ),
                          ),
                      ),
                    ) : Container(),
                  )
                ],
              ),
            ],
          ),
        );
      }
    }
    

    Here my eventCalendarService.dart

    import 'dart:convert';
    import 'dart:io';
    
    import 'package:http/io_client.dart';
    import 'package:urus_flutter/data/eventController.dart';
    import 'package:urus_flutter/data/model/base/event_calendar.dart';
    
    Future<List<Event_Calendar>> fetchEventCalendar(String id, int company_id, {String date}) async {
      String requestBody = '';
      print(requestBody);
    
      final ioc = new HttpClient();
      ioc.badCertificateCallback = (X509Certificate cert, String host, int port) => true;
      final http = new IOClient(ioc);
      final response = await http.get(
        getStringUrl+'staffs/GetCalendar?companyid=$company_id&month=$date',
      );
    
      print(response.statusCode);
      print(response.body);
    
      if (response.statusCode == 200) {
        var parsed = jsonDecode(response.body);
        return List<Event_Calendar>.from(parsed.map((model) => Event_Calendar.fromJson(model)));
      } else {
        throw Exception(response.body);
      }
    }
    

    And here my model event_calendar.dart

    class Event_Calendar {
      final String id;
      final String type;
      final String date;
      final String event_name;
      final int company_id;
    
    
      Event_Calendar(
          {
            this.id,
            this.type,
            this.date,
            this.event_name,
            this.company_id,
          }
      );
    
      factory Event_Calendar.fromJson(Map<String, dynamic> json) {
        return Event_Calendar(
            id: json['id'] as String,
            type: json['type'] as String,
            date: json['date'] as String,
            event_name: json['event_name'] as String,
            company_id: json['company_id'] as int,
        );
      }
    }
    

    I hope anyone can give me an answer on how to show _events to my API. Thank you. And this is the example value from my API

    enter image description here