Refactored the media view in the event screen, extracted the thumbnail to its own widget ([AsyncThumbnail])

This commit is contained in:
Mariano Uvalle 2019-04-08 14:48:30 -05:00
parent ec505eb27e
commit d4813bdce9
3 changed files with 113 additions and 43 deletions

View file

@ -2,19 +2,26 @@ import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../utils.dart' show kBlueGradient, getImageThumbnailPath;
import '../utils.dart' show getImageThumbnailPath;
import '../blocs/event_bloc.dart';
import '../models/task_model.dart';
import '../widgets/custom_app_bar.dart';
import '../widgets/fractionally_screen_sized_box.dart';
import '../widgets/gradient_touchable_container.dart';
import '../widgets/loading_indicator.dart';
import '../widgets/task_list_tile.dart';
import '../widgets/async_thumbnail.dart';
/// A screen that shows all the items linked to an event.
class EventScreen extends StatefulWidget {
/// The name of the event this screenn is showing.
final String eventName;
/// Creates a screen that shows all the items linked to an event.
///
/// The tasks and images are showed in different page views controlled by a
/// [TabBar].
EventScreen({
@required this.eventName,
});
@ -64,6 +71,7 @@ class _EventScreenState extends State<EventScreen>
);
}
/// Builds a list of the undone tasks linked to this service.
Widget buildTasksListView() {
return StreamBuilder(
stream: bloc.eventTasks,
@ -96,11 +104,13 @@ class _EventScreenState extends State<EventScreen>
);
}
// TODO: refactor this.
/// Builds the Page view that contains all the thumnails of the pictures
/// linked to this event.
Widget buildMediaView() {
return StreamBuilder(
stream: bloc.imagesPaths,
builder: (BuildContext context, AsyncSnapshot<List<String>> listSnap) {
// Wait until the images paths have been fetched.
if (!listSnap.hasData) {
return Center(
child: LoadingIndicator(),
@ -112,47 +122,16 @@ class _EventScreenState extends State<EventScreen>
padding: EdgeInsets.all(10.0),
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
gradient: kBlueGradient,
),
);
return buildAddPictureButton();
}
// Shift the indices since we added a button that's not contained
// in the original paths list.
final imagePath = listSnap.data[index - 1];
bloc.fetchThumbnail(imagePath);
return StreamBuilder(
stream: bloc.thumbnails,
builder: (BuildContext context,
AsyncSnapshot<Map<String, Future<File>>> thumbailsCacheSnap) {
if (!thumbailsCacheSnap.hasData) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.grey,
),
);
}
return FutureBuilder(
future:
thumbailsCacheSnap.data[getImageThumbnailPath(imagePath)],
builder: (BuildContext context,
AsyncSnapshot<File> thumbnailFileSnap) {
if (!thumbnailFileSnap.hasData) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.grey,
),
);
}
return ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Image.file(thumbnailFileSnap.data),
);
},
);
},
return AsyncThumbnail(
cacheStream: bloc.thumbnails,
cacheId: getImageThumbnailPath(imagePath),
);
},
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
@ -165,6 +144,17 @@ class _EventScreenState extends State<EventScreen>
);
}
Widget buildAddPictureButton() {
return GradientTouchableContainer(
radius: 8,
child: Icon(
Icons.camera_alt,
color: Colors.white,
size: 28,
),
);
}
void dispose() {
bloc.dispose();
super.dispose();

View file

@ -0,0 +1,74 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import '../widgets/loading_indicator.dart';
/// An Widget that displays an image given a stream of a cache and the id for
/// this image.
class AsyncThumbnail extends StatelessWidget {
/// A stream of a cache that maps an image path to the future of its file.
final Observable<Map<String, Future<File>>> cacheStream;
/// The id of the image to be displayed.
///
/// The image will load undefinitely if the the provided id is not contained
/// in the provided cache.
final String cacheId;
/// Creates a widget that displays an image given a stream of a cache and
/// the id for this image.
///
/// Neither [cacheStream] nor [cacheId] can be null.
AsyncThumbnail({
@required this.cacheStream,
@required this.cacheId,
}) : assert(cacheId != null),
assert(cacheStream != null);
Widget build(BuildContext context) {
return StreamBuilder(
stream: cacheStream,
builder: (BuildContext context,
AsyncSnapshot<Map<String, Future<File>>> thumbailsCacheSnap) {
// Wait until the images cache has data.
if (!thumbailsCacheSnap.hasData) {
return _buildThumbnailPlaceholder();
}
return FutureBuilder(
future: thumbailsCacheSnap.data[cacheId],
builder:
(BuildContext context, AsyncSnapshot<File> thumbnailFileSnap) {
// Wait until the future of the file for this image resolves.
if (!thumbnailFileSnap.hasData) {
return _buildThumbnailPlaceholder();
}
return ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Image.file(thumbnailFileSnap.data),
);
},
);
},
);
}
// TODO: Find a better animation for the placeholder.
Widget _buildThumbnailPlaceholder() {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Colors.grey,
),
child: Center(
child: LoadingIndicator(
size: 30,
),
),
);
}
}

View file

@ -5,10 +5,16 @@ import 'package:flutter/material.dart';
///
/// Shows an animation of the logo.
class LoadingIndicator extends StatelessWidget {
final double size;
LoadingIndicator({
this.size = 70,
});
Widget build(BuildContext context) {
return Container(
width: 70,
height: 70,
width: size,
height: size,
child: FlareActor(
'assets/animations/loading_animation_looped.flr',
animation: 'Flip',