2

I have a json object here -

{
    "error": "0",
    "message": "Got it!",
    "data": [
        {
            "status": false,
            "_id": "5e04a27692928701258b9b06",
            "group_id": "5df8aaae2f85481f6e31db59",
            "date": "2019-12-29T00:00:00.000Z",
            "title": "new task",
            "priority": 5,
            "description": "just a description",
            "tasks": [],
            "created_date": "2019-12-26T12:07:18.301Z",
            "__v": 0
        }
    ]
}

I am using this plugin to implement a calendar in my application - https://github.com/aleksanderwozniak/table_calendar.

I want to fetch the json objects in the format of Map <DateTime, List> (the plugin has an assertion for using Map <DateTime, List> for displaying events) where the "date" parameter should be mapped to "title" parameter.

the plugin uses initState to create a few hard coded events -


 Map<DateTime, List> _events;
  @override
  void initState() {
    super.initState();

    final _selectedDay = DateTime.now();


    _events = {_selectedDay : ["event 1"]};
  }

Could i get some help on how to fetch a json object and convert it to a format of Map<DateTime, List>? Fetching the data on initState to _events ` should be fine.

data model -

class Post {
  dynamic markComplete;
  dynamic groupID;
  dynamic date;
  dynamic taskName;
  dynamic taskID;
//dynamic subtasks;

  dynamic priority;
  dynamic description;

/// IMPLEMENT PARAMETERS AFTER CONFIRMING WITH VAMSHI

  Post({
    this.markComplete,
    this.groupID,
    this.taskID,
    this.date,
    this.taskName,
//    this.subtasks,

    this.priority,
    this.description,
  });

/// MIGHT NEED TO CHANGE JSON VALUES; CONFIRM FROM VAMSHI
  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(
        markComplete : json['status'],
        groupID : json['group_id'],
      taskID: json["_id"],
      date : json['date'],
      taskName : json['title'],
//      subtasks : json['subTasks'],

      priority : json['priority'],
      description : json['description']
    );
  }



  Map toMapFetch() {
    var map = new Map<dynamic, dynamic>();
//    map['status'] = markComplete;
//    map["group_id"] = taskid;
//    map['date'] = date;
    map['title'] = taskName;
//    map['subTasks'] = subtasks;
//
//    map['priority'] = priority;
//    map['description'] = description;


    return map;
  }
}

method to fetch -

Future<Map<DateTime, List>> getTask() async {
  Map<DateTime, List> mapFetch;
  String link = baseURL + fetchTodoByDate;
  var res = await http.post(Uri.encodeFull(link), headers: {"Accept": "application/json"});
  if (res.statusCode == 200) {
 // need help in creating fetch logic here
  }
return mapFetch;
}

data model =

class dataArray {
//  final dynamic status;
  final dynamic id;
  final dynamic groupName;

//  final dynamic description;
//  final dynamic created_date;
//  final dynamic v;


  dataArray(this.groupName, this.id);


  dataArray.fromJson(Map jsonMap)
      : groupName = jsonMap['name'],
        id = jsonMap['_id'];


  Map toMapData(){
    var mapGroup = new Map<String, dynamic>();
    mapGroup["name"] = groupName;
    mapGroup['_id'] = id;
    return mapGroup;

  }

}

updated getTask method =

  Future<List<dataArray>> getTask() async {


    List<dataArray> groupMap;
    String link = baseURL + fetchGroups;
    var res = await http
        .get(Uri.encodeFull(link), headers: {"Accept": "application/json"});
//  print(res.body);
    if (res.statusCode == 200) {
      var data = json.decode(res.body);
      var rest = data["data"] as List;

      final demoJsonMapEntries = rest.map((data) {
        return MapEntry(DateTime.parse(data['created_date']), data['name']);
      });

      demoJsonMapEntries.forEach((e) {
        // Normalize the `date` - this is necessary to ensure proper `Map` behavior
        final key = DateTime.utc(e.key.year, e.key.month, e.key.day, 12);

        _events.update(key, (list) => list..add(e.value), ifAbsent: () => [e.value]);
      });
      print(demoJsonMapEntries);
    }
    print("PRINTING MAP = $groupMap");
 return groupMap;
  }

2 Answers2

3

You can copy paste run full code below
Simulate API call with 3 seconds delay
Suppose you have two event on 12/29 and 12/30 , then parse with Event event = eventFromJson(responseString); and return mapFetch after for loop
You can get full Event class define in full code

code snippet

WidgetsBinding.instance.addPostFrameCallback((_) {
      getTask().then((val) => setState(() {
            _events = val;
          }));

    });

...
Future<Map<DateTime, List>> getTask() async {
    Map<DateTime, List> mapFetch = {};

    await Future.delayed(const Duration(seconds: 3), () {});

    /*String link = baseURL + fetchTodoByDate;
    var res = await http.post(Uri.encodeFull(link), headers: {"Accept": "application/json"});
    if (res.statusCode == 200) {
      // need help in creating fetch logic here
    }*/

    String responseString = '''
    {
    "error": "0",
    "message": "Got it!",
    "data": [
        {
            "status": false,
            "_id": "5e04a27692928701258b9b06",
            "group_id": "5df8aaae2f85481f6e31db59",
            "date": "2019-12-29T00:00:00.000Z",
            "title": "new task",
            "priority": 5,
            "description": "just a description",
            "tasks": [],
            "created_date": "2019-12-26T12:07:18.301Z",
            "__v": 0
        },
        {
            "status": false,
            "_id": "5e04a27692928701258b9b06",
            "group_id": "5df8aaae2f85481f6e31db59",
            "date": "2019-12-30T00:00:00.000Z",
            "title": "abc",
            "priority": 5,
            "description": "just a description",
            "tasks": [],
            "created_date": "2019-12-26T12:07:18.301Z",
            "__v": 0
        }
    ]
}
    ''';

    Event event = eventFromJson(responseString);

    for (int i = 0; i < event.data.length; i++) {
      mapFetch[event.data[i].date] = [event.data[i].title];
    }

    return mapFetch;
  }

working demo

enter image description here

full code

import 'package:flutter/material.dart';
import 'package:table_calendar/table_calendar.dart';
// To parse this JSON data, do
//
//     final event = eventFromJson(jsonString);

import 'dart:convert';

Event eventFromJson(String str) => Event.fromJson(json.decode(str));

String eventToJson(Event data) => json.encode(data.toJson());

class Event {
  String error;
  String message;
  List<Datum> data;

  Event({
    this.error,
    this.message,
    this.data,
  });

  factory Event.fromJson(Map<String, dynamic> json) => Event(
        error: json["error"],
        message: json["message"],
        data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
      );

  Map<String, dynamic> toJson() => {
        "error": error,
        "message": message,
        "data": List<dynamic>.from(data.map((x) => x.toJson())),
      };
}

class Datum {
  bool status;
  String id;
  String groupId;
  DateTime date;
  String title;
  int priority;
  String description;
  List<dynamic> tasks;
  DateTime createdDate;
  int v;

  Datum({
    this.status,
    this.id,
    this.groupId,
    this.date,
    this.title,
    this.priority,
    this.description,
    this.tasks,
    this.createdDate,
    this.v,
  });

  factory Datum.fromJson(Map<String, dynamic> json) => Datum(
        status: json["status"],
        id: json["_id"],
        groupId: json["group_id"],
        date: DateTime.parse(json["date"]),
        title: json["title"],
        priority: json["priority"],
        description: json["description"],
        tasks: List<dynamic>.from(json["tasks"].map((x) => x)),
        createdDate: DateTime.parse(json["created_date"]),
        v: json["__v"],
      );

  Map<String, dynamic> toJson() => {
        "status": status,
        "_id": id,
        "group_id": groupId,
        "date": date.toIso8601String(),
        "title": title,
        "priority": priority,
        "description": description,
        "tasks": List<dynamic>.from(tasks.map((x) => x)),
        "created_date": createdDate.toIso8601String(),
        "__v": v,
      };
}

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  List _selectedEvents;
  int _counter = 0;
  Map<DateTime, List> _events;
  CalendarController _calendarController;
  AnimationController _animationController;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  Future<Map<DateTime, List>> getTask() async {
    Map<DateTime, List> mapFetch = {};

    await Future.delayed(const Duration(seconds: 3), () {});

    /*String link = baseURL + fetchTodoByDate;
    var res = await http.post(Uri.encodeFull(link), headers: {"Accept": "application/json"});
    if (res.statusCode == 200) {
      // need help in creating fetch logic here
    }*/

    String responseString = '''
    {
    "error": "0",
    "message": "Got it!",
    "data": [
        {
            "status": false,
            "_id": "5e04a27692928701258b9b06",
            "group_id": "5df8aaae2f85481f6e31db59",
            "date": "2019-12-29T00:00:00.000Z",
            "title": "new task",
            "priority": 5,
            "description": "just a description",
            "tasks": [],
            "created_date": "2019-12-26T12:07:18.301Z",
            "__v": 0
        },
        {
            "status": false,
            "_id": "5e04a27692928701258b9b06",
            "group_id": "5df8aaae2f85481f6e31db59",
            "date": "2019-12-30T00:00:00.000Z",
            "title": "abc",
            "priority": 5,
            "description": "just a description",
            "tasks": [],
            "created_date": "2019-12-26T12:07:18.301Z",
            "__v": 0
        }
    ]
}
    ''';

    Event event = eventFromJson(responseString);

    for (int i = 0; i < event.data.length; i++) {
      mapFetch[event.data[i].date] = [event.data[i].title];
    }

    return mapFetch;
  }

  void _onDaySelected(DateTime day, List events) {
    print('CALLBACK: _onDaySelected');
    setState(() {
      _selectedEvents = events;
    });
  }

  @override
  void initState() {
    final _selectedDay = DateTime.now();
    _selectedEvents = [];
    _calendarController = CalendarController();
    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 400),
    );

    _animationController.forward();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      getTask().then((val) => setState(() {
            _events = val;
          }));
      //print( ' ${_events.toString()} ');
    });
    super.initState();
  }

  @override
  void dispose() {
    _calendarController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            _buildTableCalendarWithBuilders(),
            const SizedBox(height: 8.0),
            const SizedBox(height: 8.0),
            Expanded(child: _buildEventList()),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }

  Widget _buildTableCalendarWithBuilders() {
    return TableCalendar(
      //locale: 'pl_PL',
      calendarController: _calendarController,
      events: _events,
      //holidays: _holidays,
      initialCalendarFormat: CalendarFormat.month,
      formatAnimation: FormatAnimation.slide,
      startingDayOfWeek: StartingDayOfWeek.sunday,
      availableGestures: AvailableGestures.all,
      availableCalendarFormats: const {
        CalendarFormat.month: '',
        CalendarFormat.week: '',
      },
      calendarStyle: CalendarStyle(
        outsideDaysVisible: false,
        weekendStyle: TextStyle().copyWith(color: Colors.blue[800]),
        holidayStyle: TextStyle().copyWith(color: Colors.blue[800]),
      ),
      daysOfWeekStyle: DaysOfWeekStyle(
        weekendStyle: TextStyle().copyWith(color: Colors.blue[600]),
      ),
      headerStyle: HeaderStyle(
        centerHeaderTitle: true,
        formatButtonVisible: false,
      ),
      builders: CalendarBuilders(
        selectedDayBuilder: (context, date, _) {
          return FadeTransition(
            opacity: Tween(begin: 0.0, end: 1.0).animate(_animationController),
            child: Container(
              margin: const EdgeInsets.all(4.0),
              padding: const EdgeInsets.only(top: 5.0, left: 6.0),
              color: Colors.deepOrange[300],
              width: 100,
              height: 100,
              child: Text(
                '${date.day}',
                style: TextStyle().copyWith(fontSize: 16.0),
              ),
            ),
          );
        },
        todayDayBuilder: (context, date, _) {
          return Container(
            margin: const EdgeInsets.all(4.0),
            padding: const EdgeInsets.only(top: 5.0, left: 6.0),
            color: Colors.amber[400],
            width: 100,
            height: 100,
            child: Text(
              '${date.day}',
              style: TextStyle().copyWith(fontSize: 16.0),
            ),
          );
        },
        markersBuilder: (context, date, events, holidays) {
          final children = <Widget>[];

          if (events.isNotEmpty) {
            children.add(
              Positioned(
                right: 1,
                bottom: 1,
                child: _buildEventsMarker(date, events),
              ),
            );
          }

          if (holidays.isNotEmpty) {
            children.add(
              Positioned(
                right: -2,
                top: -2,
                child: _buildHolidaysMarker(),
              ),
            );
          }

          return children;
        },
      ),
      onDaySelected: (date, events) {
        _onDaySelected(date, events);
        _animationController.forward(from: 0.0);
      },
      onVisibleDaysChanged: _onVisibleDaysChanged,
    );
  }

  void _onVisibleDaysChanged(
      DateTime first, DateTime last, CalendarFormat format) {
    print('CALLBACK: _onVisibleDaysChanged');
  }

  Widget _buildEventsMarker(DateTime date, List events) {
    return AnimatedContainer(
      duration: const Duration(milliseconds: 300),
      decoration: BoxDecoration(
        shape: BoxShape.rectangle,
        color: _calendarController.isSelected(date)
            ? Colors.brown[500]
            : _calendarController.isToday(date)
                ? Colors.brown[300]
                : Colors.blue[400],
      ),
      width: 16.0,
      height: 16.0,
      child: Center(
        child: Text(
          '${events.length}',
          style: TextStyle().copyWith(
            color: Colors.white,
            fontSize: 12.0,
          ),
        ),
      ),
    );
  }

  Widget _buildHolidaysMarker() {
    return Icon(
      Icons.add_box,
      size: 20.0,
      color: Colors.blueGrey[800],
    );
  }

  Widget _buildEventList() {
    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(
                  title: Text(event.toString()),
                  onTap: () => print('$event tapped!'),
                ),
              ))
          .toList(),
    );
  }
}
chunhunghan
  • 51,087
  • 5
  • 102
  • 120
  • Excellent answer! just a quick doubt - Could you just show a quick example on how to pass the responseString when using a URL? i.e. How to send a response body from an API instead of hard coded json objects? I have tried to play around with it a bit but i am not sure how to get it to work. I wish i could provide the API but due to my company policy, i cannot do that. –  Dec 30 '19 at 11:00
  • your getTask() looks fine. just use if (res.statusCode == 200) { Event event = eventFromJson(res.body); } – chunhunghan Dec 31 '19 at 00:46
  • because I do not have your api. all I can do is just simulate response. let me know if it does work with eventFromJson(res.body); – chunhunghan Dec 31 '19 at 01:14
  • i have modified the getTask() function this way - ``` ... String link = baseURL + fetchGroups; var res = await http.get(Uri.encodeFull(link), headers: {"Accept": "application/json"}); print("PRINTING res BODY = ${res.body}"); if (res.statusCode == 200) { Event event = eventFromJson(res.body); for (int i = 0; i < event.data.length; i++) { mapFetch[event.data[i].date] = [event.data[i].title]; } } .... ``` but it gives me this error - ``` [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: Invalid argument(s)``` –  Dec 31 '19 at 07:05
  • your code looks fine. could you identify error line/code? – chunhunghan Dec 31 '19 at 07:23
  • I commented out this line ``` WidgetsBinding.instance.addPostFrameCallback((_) { getTask().then((val) => setState(() { _events = val; })); });``` and the exceptions seems to be gone. But the groupMap seems to be null even after fetching the response.body –  Dec 31 '19 at 14:19
  • groupMap is not one of your field. I guess you mean group_id or groupId – chunhunghan Dec 31 '19 at 23:57
  • GroupMap is the variable which is supposed to hold the data from response.body. What i mean is even though the response.body successfully stores the fetched data, the map which is returned from getTask() is null –  Jan 01 '20 at 04:34
  • could you post full code and content of res.body, thanks. – chunhunghan Jan 02 '20 at 01:24
  • first off, Happy New Year! I have added a modified getTask method based on the suggestion from the author of the calendar plugin.I will also try to get a sample API with the same parameters so that we can try to solve it. –  Jan 02 '20 at 04:13
  • variable groupMap in getTask() did not init or be set, you just do print and return. did you set it in another function? – chunhunghan Jan 02 '20 at 04:25
  • turns out i had made a rookie mistake and named one of the parameters in a wrong way in the data model. But I have updated it and now it successfully fetches the data and reflects in the UI. Thank you so much for helping out! –  Jan 02 '20 at 04:29
  • @RonakUpadhyay, could you post a new question. so more people can help you. thanks. – chunhunghan Mar 10 '20 at 00:37
  • @chunhunghan First of all thank you for your answer it is very helpful. But I am having issue if I have multiple title on a single date. If I have more than two title on single date it is showing only one title on that date. Can you please help me to resolve this? – Rohit Kurzekar Apr 02 '20 at 09:40
  • @chunhunghan How to parse dateTime from below json { "kind": "calendar#events", "etag": "\"p33kd73vkjf3ug0g\"", "items": [ { "status": "confirmed", "summary": "Demo Event", "start": { "dateTime": "2020-04-04T07:00:00+05:30", "timeZone": "Asia/Kolkata" }, "end": { "dateTime": "2020-04-04T08:00:00+05:30", "timeZone": "Asia/Kolkata" }, }, ] } – Rohit Kurzekar Apr 02 '20 at 10:41
  • @RohitKurzekar, you can paste your json string to https://quicktype.io/ and it will give you correct model. – chunhunghan Apr 06 '20 at 00:41
  • @chunhunghan Thank you for reply. Using quicktype.io I have corrected my model that issue is reloved. But I have mutiple events on same date, still it showing only single event in calenadar. I am wating for your reply and one thing I have to tell u that u r legend keep going. I have attached my initSatate() method code - void initState() {final _selectedDay = DateTime.now(); _selectedEvents = [];WidgetsBinding.instance.addPostFrameCallback((_) {getTask().then((val) => setState(() {_events = val;}));}); super.initState();} – Rohit Kurzekar Apr 06 '20 at 10:34
  • @RohitKurzekar, the reason is the time. you can trim your 2020-04-04T07:00:00+05:30 and 2020-04-04T08:00:00+05:30 to 2020-04-04. because Map use date. and this date can not include time. – chunhunghan Apr 07 '20 at 00:53
  • @chunhunghan Can you help me on this issue "https://stackoverflow.com/questions/61228977/how-to-pass-multiple-data-fetched-from-json-to-next-screen-in-flutter" – Rohit Kurzekar Apr 15 '20 at 12:44
0

I think your fetch method should be like this:

  Future<List<Post>> getData() async{
  String link = baseURL + fetchTodoByDate;
  var res = await http.post(Uri.encodeFull(link), headers: {"Accept": "application/json"});

var fetch =  List<Data>();
if (res.statusCode == 200 ) {
  var datesJson = json.decode(res.body);
  for(var dateJson in datesJson){
    fetch.add(Data.fromJson((dateJson)));
  }
}
return fetch;

}

Md.shah
  • 48
  • 1
  • 8