Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wait for a Class to initialize?

Tags:

flutter

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?

like image 291
Abhi Agarwal Avatar asked Jun 10 '17 20:06

Abhi Agarwal


1 Answers

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.

like image 157
Collin Jackson Avatar answered Sep 23 '22 23:09

Collin Jackson