Our Blog
blog header image
A month of Flutter: post model and mock data
Abraham Williams
December 10, 2018

Category

Development

Now that there is a basic user interface, I'm going to create some mock data to display in it. The mock data will be stored in a JSON file and will include just the basic fields needed right now: id, createdAt, username, imageUrl, and text. For imageUrl I grabbed some random images from Unsplash. text is actually empty as I haven't decided what text to use in the mocks yet.

This is a similar format to what you might expect from a JSON API. In the future there will probably be a User object and maybe an Image object that includes URLs to different sizes, etc.

[
  {
    "id": "7d3d8bd1-b9a6-4e1f-8e4e-dca6f4861441",
    "imageUrl": "https://source.unsplash.com/AEVAMhago-s",
    "createdAt": "2018-12-09T15:35:54.006Z",
    "text": "",
    "username": "woodstock"
  }
]

There are a number of ways to consume JSON in Flutter, but for now I'm going with the simple dart:convert pattern with a Post class.

class Post {
  const Post({
     this.id,
     this.username,
     this.createdAt,
     this.text,
     this.imageUrl,
  });

  Post.fromMap(Map<String, dynamic> data)
      : id = data['id'],
        username = data['username'],
        createdAt = DateTime.parse(data['createdAt']),
        text = data['text'],
        imageUrl = data['imageUrl'];

  final String id;
  final String username;
  final DateTime createdAt;
  final String text;
  final String imageUrl;
}

I'm not a fan of optional values so I require everything. This way I don't have to check if a property exists; I just have to check that the property has a value I can work with.

Along with the default constructor I'm also defining a named constructor, fromMap, that uses an an initializer list. fromMap takes in a Map of the raw data and translates it to class variables. Note that the JSON datetime string is being converted to a Dart DateTime instance.

One downside of this code is there isn't a defined format for what the raw JSON data contains. For example, if I remove DateTime.parse the linter will not complain and there will be a failure at runtime when the createdAt string from the data tries to be set to the DateTime type property in Post. I have not yet found a way to define the key names and value types in a Map.

As the data model will change during development, it's important to make sure Post continues to work correctly. I will start with a pretty simple test. It loads the first mock data object, instantiates a Post instance, and asserts each value is available as expected. This is a plain Dart test (as opposed to a Flutter widget test) so I will have to add the Dart test package to dev_dependencies.

group('Post', () {
  test('fromMap', () async {
    final Post post = Post.fromMap(await postData());

    expect(post.id, '7d3d8bd1-b9a6-4e1f-8e4e-dca6f4861441');
    expect(post.username, 'woodstock');
    expect(post.imageUrl, 'https://source.unsplash.com/AEVAMhago-s');
    expect(post.createdAt, DateTime.parse('2018-12-09T15:35:54.006Z'));
    expect(post.text, '');
  });
});

There is a new helper function, postData, that loads the JSON file, parses it, and returns the first object.

dynamic postData() async {
  final dynamic data = await File('../assets/posts.json').readAsString();
  return json.decode(data).first;
}

I've implemented the mock data in a JSON file instead of creating a List in Dart because that forces me to use the mock data in an asynchronous way. The real data will be async when pulled from Firestore so I am starting with that pattern now.

Code changes