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.