Refactored the media view in the event screen, extracted the thumbnail to its own widget ([AsyncThumbnail])
This commit is contained in:
parent
ec505eb27e
commit
d4813bdce9
3 changed files with 113 additions and 43 deletions
|
|
@ -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();
|
||||
|
|
|
|||
74
lib/src/widgets/async_thumbnail.dart
Normal file
74
lib/src/widgets/async_thumbnail.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue