Originally published on bendyworks.com.
Now that I've got a Post model with mock data and a StreamBuilder for rendering, it all needs to be wired together.
The main changes I'm making are in _MyHomePageState
where I'm replacing the stream of int
s with a call to the new _loadPosts
method.
Stream<List<Post>> _loadPosts(BuildContext context) {
return DefaultAssetBundle.of(context)
.loadString('assets/posts.json')
.then<List<dynamic>>((String value) => json.decode(value))
.asStream()
.map(_convertToPosts);
}
~~~{% endraw %}
{% raw %}`_loadPosts`{% endraw %} uses [{% raw %}`DefaultAssetBundle.of`{% endraw %}](https://docs.flutter.io/flutter/widgets/DefaultAssetBundle/of.html) to get the most appropriate [{% raw %}`AssetBundle`](https://docs.flutter.io/flutter/services/AssetBundle-class.html) for loading files from the `assets` directory.
Reading the {% raw %}`String`{% endraw %} contents of a file is asynchronous, after which the JSON contents can be decoded. The loading and decoding will happen as a {% raw %}`Future`{% endraw %} but I want to be working on a stream so I'll use [`asStream`](https://api.dartlang.org/stable/2.1.0/dart-async/Future/asStream.html) to convert the `Future` to a `Stream`.
Finally the Stream will then get transformed using [`map`](https://api.dartlang.org/stable/2.1.0/dart-async/Stream/map.html) and another new method `_convertToPosts`.
~~~dart
List<Post> _convertToPosts(List<dynamic> data) {
return data.map((dynamic item) => Post.fromMap(item)).toList();
}
~~~
Every time an event comes down the stream, `map` will call the convert method on the value. In this case the value is a `List` of items and `List`'s [{% raw %}`map`{% endraw %}](https://api.dartlang.org/stable/2.1.0/dart-core/Iterable/map.html) will pass each item to `Post.fromMap`. Note that `map` on a `List` is different from `map` on a `Stream`. `map` on a `List` is lazy so we'll force it to execute with a final [`toList`](https://api.dartlang.org/stable/2.1.0/dart-core/Iterable/toList.html).
The rest of the changes are going thought the code base and updating {% raw %}`List<int>`{% endraw %} types to be {% raw %}`List<Post>`{% endraw %} types and passing {% raw %}`Post`{% endraw %} instances into the rendering {% raw %}`PostItem`{% endraw %}s. I'll also replace the hard coded `'Prim Birb'` text with the dynamic `username` from the `Post` instances.
![Screenshot of rendered mock usernames](https://thepracticaldev.s3.amazonaws.com/i/lmfoehbinrds1zn7kvaw.png)
As I was updating the tests to use the new mock JSON data, I kept running into failures. I was seeing tests timing out or the `PostsList` error handling showing errors instead of content. After some digging it turns out that the code in `testWidgets` [can't access](https://github.com/flutter/flutter/issues/8490) files or assets. With [CI configured](https://bendyworks.com/blog/a-month-of-flutter-configuring-continuous-integration) I can't merge code without passing tests so come back tomorrow to see how I fix them.
## Code changes
- [#29 Turn mock posts into Stream](https://github.com/abraham/birb/pull/29)