Cache images using ImageProvider interface added to Flutter.
As key, use an identifier consisting of deckId and slideIndex
(for slides.)
Change-Id: I96c5541412f82dd49c564f8433ce6977078d9a5f
diff --git a/dart/FLUTTER_VERSION b/dart/FLUTTER_VERSION
index fb36760..8383605 100644
--- a/dart/FLUTTER_VERSION
+++ b/dart/FLUTTER_VERSION
@@ -1 +1 @@
-a35b214d891efaeddfb4b4270ebe222dfab16a8a
+04dfa0bf87a37eaeef0d85aeff0c7ea7ca04782d
diff --git a/dart/lib/components/deckgrid.dart b/dart/lib/components/deckgrid.dart
index f092be9..441db75 100644
--- a/dart/lib/components/deckgrid.dart
+++ b/dart/lib/components/deckgrid.dart
@@ -11,6 +11,7 @@
import '../models/all.dart' as model;
import '../stores/store.dart';
import '../styles/common.dart' as style;
+import '../utils/image_provider.dart' as imageProvider;
import 'slidelist.dart';
@@ -79,7 +80,8 @@
}
Widget _buildDeckBox(BuildContext context, model.Deck deckData) {
- var thumbnail = _buildThumbnail(deckData.thumbnail);
+ var thumbnail =
+ new AsyncImage(provider: imageProvider.getDeckThumbnailImage(deckData));
// TODO(aghassemi): Add "Opened on" data.
var subtitleWidget =
new Text("Opened on Sep 12, 2015", style: style.Text.subtitleStyle);
@@ -95,7 +97,8 @@
Widget _buildPresentationBox(
BuildContext context, model.PresentationAdvertisement presentationData) {
- var thumbnail = _buildThumbnail(presentationData.deck.thumbnail);
+ var thumbnail = new AsyncImage(
+ provider: imageProvider.getDeckThumbnailImage(presentationData.deck));
var liveBox = new Row([
new Container(
child: new Text("LIVE NOW", style: style.Text.liveNow),
@@ -108,20 +111,6 @@
return box;
}
-// TODO(aghassemi): Cache will be moved to Flutter and will become a map of encoded bytes to
-// decoded bytes. This is just a quick work-around for now.
-Map<int, RawImage> thumbnailCache = new Map();
-Widget _buildThumbnail(List<int> bytes) {
- var key = bytes.hashCode;
- if (thumbnailCache.containsKey(key)) {
- return thumbnailCache[key];
- }
- var thumbnail = new RawImage(bytes: new Uint8List.fromList(bytes));
-
- thumbnailCache[key] = thumbnail;
- return thumbnail;
-}
-
Widget _buildBoxFooter(String title, Widget subtitle) {
var titleWidget = new Text(title, style: style.Text.titleStyle);
titleWidget = _stopWrapping(titleWidget);
diff --git a/dart/lib/components/slidelist.dart b/dart/lib/components/slidelist.dart
index e27dbc7..49ead9b 100644
--- a/dart/lib/components/slidelist.dart
+++ b/dart/lib/components/slidelist.dart
@@ -8,6 +8,7 @@
import '../models/all.dart' as model;
import '../stores/store.dart';
import '../styles/common.dart' as style;
+import '../utils/image_provider.dart' as imageProvider;
import 'slideshow.dart';
@@ -70,7 +71,8 @@
itemExtent: style.Size.listHeight,
items: _slides,
itemBuilder: (context, value, index) =>
- _buildSlide(context, index.toString(), value, onTap: () {
+ _buildSlide(context, config.deckId, index, index.toString(), value,
+ onTap: () {
_store.setCurrSlideNum(config.deckId, index);
Navigator.of(context).push(new MaterialPageRoute(
builder: (context) => new SlideshowPage(config.deckId)));
@@ -78,21 +80,12 @@
}
}
-// TODO(aghassemi): Is this approach okay? Check with Flutter team.
-// Builder gets called a lot by the ScrollableList and building RawImage
-// is expensive so we cache.
-// Expando is a weak map so this does not effect GC.
-Expando<Widget> _weakSlideCache = new Expando<Widget>();
-Widget _buildSlide(BuildContext context, String key, model.Slide slideData,
+Widget _buildSlide(BuildContext context, String deckId, int slideIndex,
+ String key, model.Slide slideData,
{Function onTap}) {
- var cachedWidget = _weakSlideCache[slideData];
- if (cachedWidget != null) {
- return cachedWidget;
- }
-
- var thumbnail = new RawImage(
+ var thumbnail = new AsyncImage(
+ provider: imageProvider.getSlideImage(deckId, slideIndex, slideData),
height: style.Size.listHeight,
- bytes: new Uint8List.fromList(slideData.image),
fit: ImageFit.cover);
thumbnail = new Flexible(child: thumbnail);
@@ -111,6 +104,5 @@
var listItem = new InkWell(key: new Key(key), child: card, onTap: onTap);
- _weakSlideCache[slideData] = listItem;
return listItem;
}
diff --git a/dart/lib/components/slideshow.dart b/dart/lib/components/slideshow.dart
index 284a6f8..700ef5b 100644
--- a/dart/lib/components/slideshow.dart
+++ b/dart/lib/components/slideshow.dart
@@ -10,6 +10,7 @@
import '../models/all.dart' as model;
import '../stores/store.dart';
import '../styles/common.dart' as style;
+import '../utils/image_provider.dart' as imageProvider;
class SlideshowPage extends StatelessComponent {
final String deckId;
@@ -74,8 +75,10 @@
return new Text('Loading');
}
var slideData = _slides[_currSlideNum];
- var image = new RawImage(
- bytes: new Uint8List.fromList(slideData.image), fit: ImageFit.contain);
+ var image = new AsyncImage(
+ provider: imageProvider.getSlideImage(
+ config.deckId, _currSlideNum, slideData),
+ fit: ImageFit.contain);
var navWidgets = [
_buildSlideNav(_currSlideNum - 1),
_buildSlideNav(_currSlideNum + 1)
@@ -89,7 +92,8 @@
var card;
if (slideNum >= 0 && slideNum < _slides.length) {
- card = _buildThumbnailNav(_slides[slideNum], onTap: () {
+ card = _buildThumbnailNav(config.deckId, slideNum, _slides[slideNum],
+ onTap: () {
_store.setCurrSlideNum(config.deckId, slideNum);
});
} else {
@@ -103,10 +107,11 @@
}
}
-Widget _buildThumbnailNav(model.Slide slideData, {Function onTap}) {
- var thumbnail = new RawImage(
+Widget _buildThumbnailNav(String deckId, int slideIndex, model.Slide slideData,
+ {Function onTap}) {
+ var thumbnail = new AsyncImage(
+ provider: imageProvider.getSlideImage(deckId, slideIndex, slideData),
height: style.Size.thumbnailNavHeight,
- bytes: new Uint8List.fromList(slideData.image),
fit: ImageFit.cover);
return new InkWell(child: thumbnail, onTap: onTap);
diff --git a/dart/lib/utils/image_provider.dart b/dart/lib/utils/image_provider.dart
new file mode 100644
index 0000000..abaeb9b
--- /dev/null
+++ b/dart/lib/utils/image_provider.dart
@@ -0,0 +1,35 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:typed_data' show Uint8List;
+import 'dart:ui' as ui;
+
+import 'package:flutter/services.dart';
+
+import '../models/all.dart' as model;
+
+ImageProvider getDeckThumbnailImage(model.Deck deck) {
+ return new _RawImageProvider('thumbnail_${deck.key}', deck.thumbnail);
+}
+
+ImageProvider getSlideImage(String deckId, int slideIndex, model.Slide slide) {
+ return new _RawImageProvider('slide_${deckId}_$slideIndex', slide.image);
+}
+
+class _RawImageProvider implements ImageProvider {
+ final String imageKey;
+ final List<int> imageData;
+
+ _RawImageProvider(this.imageKey, this.imageData);
+
+ Future<ui.Image> loadImage() async {
+ return await decodeImageFromList(new Uint8List.fromList(imageData));
+ }
+
+ bool operator ==(other) =>
+ other is _RawImageProvider && imageKey == other.imageKey;
+ int get hashCode => imageKey.hashCode;
+ String toString() => imageKey;
+}
diff --git a/dart/pubspec.lock b/dart/pubspec.lock
index f3021f6..49d5397 100644
--- a/dart/pubspec.lock
+++ b/dart/pubspec.lock
@@ -186,11 +186,11 @@
sky_engine:
description: sky_engine
source: hosted
- version: "0.0.56"
+ version: "0.0.57"
sky_services:
description: sky_services
source: hosted
- version: "0.0.56"
+ version: "0.0.57"
source_map_stack_trace:
description: source_map_stack_trace
source: hosted