I have a class, calendar
, which reads from a file asynchronously.
// Copyright 2017 <Abhi Agarwal>
// Refer to LICENSE
// Dart Imports
import 'dart:async';
import 'dart:convert';
// Flutter Imports
import 'package:flutter/services.dart';
// Local Imports
import 'event.dart';
/// Class deals with the school-wide calendar Calendar.
class Calendar {
/// The days off in the year.
List<Event> daysOff = new List<Event>();
/// The half-days, not necessarily days off.
List<Event> halfDays = new List<Event>();
/// Vacations, extended days off.
List<Event> vactations = new List<Event>();
DateTime schoolStart;
DateTime schoolEnd;
DateTime schoolMaxEnd;
Calendar() {
_addDaysOff();
// _addHalfDays();
// _addVacations();
// _addTimes();
}
static const String filePath = "assets/calendar/";
/// Reads a JSON File specified by the Arguments and returns a Decoded Object.
Future<List<Map<String, String>>> _readJson(String fileName) async =>
await JSON.decode(await rootBundle.loadString(filePath + fileName));
Future _addDaysOff() async {
const String fileName = "days_off.json";
final List<Map<String, String>> parsed = await _readJson(fileName);
for (final Map<String, String> item in parsed) {
Event event = new Event(item["name"], DateTime.parse(item["date"]));
daysOff.add(event);
}
}
}
That works fine and all, but the problem arises when I want to run a simple test.
// Copyright 2017 <Abhi Agarwal>
// Refer to LICENSE
import 'package:flutter/material.dart';
import 'definitions/calendar/calendar.dart';
void main() {
Calendar myCalendar = new Calendar();
runApp(new Center(child: new Text(myCalendar.daysOff[0].name)));
}
With flutter run
, I get RangeError (index): Invalid value: Valid value range is empty: 0
. This makes sense, List will only have elements when it is initialized. The question is, how can I place a placeholder there until the data has loaded?
You can refactor your Calendar
class to expose a Future
that completes the when the Calendar
is ready to use:
// Private constructor, use create() to get an instance
Calendar._();
// Future that completes when the new Calendar is ready to use
static Future<Calendar> create() async {
Calendar calendar = Calendar._();
await calendar._addDaysOff();
// await calendar._addHalfDays();
// await calendar._addVacations();
// await calendar._addTimes();
return calendar;
}
Then you can await
the Future
, e.g.:
main() async {
Calendar myCalendar = await Calendar.create();
runApp(new Center(child: new Text(myCalendar.daysOff[0].name)));
}
You can wait for the Calendar
to initialize using a FutureBuilder
if you want the Calendar to be instantiated by a StatefulWidget
somewhere deeper in your Flutter widget tree instead of having it live at the root.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With