A month of Flutter: post model and mock data

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({
    @required this.id,
    @required this.username,
    @required this.createdAt,
    @required this.text,
    @required 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

Posts in this series

  • A month of Flutter
  • A month of Flutter: create the app
  • A month of Flutter: configuring continuous integration
  • A month of Flutter: continuous linting
  • A month of Flutter: upgrading to 1.0
  • A month of Flutter: initial theme
  • A month of Flutter: no content widget
  • A month of Flutter: a list of posts
  • A month of Flutter: extract post item widget
  • A month of Flutter: post model and mock data
  • A month of Flutter: rendering a ListView with StreamBuilder
  • A month of Flutter: Stream transforms and failing tests
  • A month of Flutter: real faker data
  • A month of Flutter: rendering network images
  • A month of Flutter: FABulous authentication
  • A month of Flutter: configure Firebase Auth for Sign in with Google on Android
  • A month of Flutter: configure Firebase Auth for Sign in with Google on iOS
  • A month of Flutter: Sign in with Google
  • A month of Flutter: mocking Firebase Auth in tests
  • A month of Flutter: delicious welcome snackbar
  • A month of Flutter: navigate to user registration
  • A month of Flutter: user registration form
  • A month of Flutter: testing forms
  • A month of Flutter: setting up Firebase Firestore
  • A month of Flutter: awesome adaptive icons
  • A month of Flutter: set up Firestore rules tests
  • A month of Flutter: Firestore create user rules and tests
  • A month of Flutter: WIP save users to Firestore
  • A month of Flutter: user registration refactor with reactive scoped model
  • A month of Flutter: the real hero animation
  • A month of Flutter: a look back

  • Category: Development
    Tags: Flutter, Tutorial