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 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
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 '../blocs/event_bloc.dart';
|
||||||
import '../models/task_model.dart';
|
import '../models/task_model.dart';
|
||||||
import '../widgets/custom_app_bar.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/loading_indicator.dart';
|
||||||
import '../widgets/task_list_tile.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 {
|
class EventScreen extends StatefulWidget {
|
||||||
/// The name of the event this screenn is showing.
|
/// The name of the event this screenn is showing.
|
||||||
final String eventName;
|
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({
|
EventScreen({
|
||||||
@required this.eventName,
|
@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() {
|
Widget buildTasksListView() {
|
||||||
return StreamBuilder(
|
return StreamBuilder(
|
||||||
stream: bloc.eventTasks,
|
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() {
|
Widget buildMediaView() {
|
||||||
return StreamBuilder(
|
return StreamBuilder(
|
||||||
stream: bloc.imagesPaths,
|
stream: bloc.imagesPaths,
|
||||||
builder: (BuildContext context, AsyncSnapshot<List<String>> listSnap) {
|
builder: (BuildContext context, AsyncSnapshot<List<String>> listSnap) {
|
||||||
|
// Wait until the images paths have been fetched.
|
||||||
if (!listSnap.hasData) {
|
if (!listSnap.hasData) {
|
||||||
return Center(
|
return Center(
|
||||||
child: LoadingIndicator(),
|
child: LoadingIndicator(),
|
||||||
|
|
@ -112,47 +122,16 @@ class _EventScreenState extends State<EventScreen>
|
||||||
padding: EdgeInsets.all(10.0),
|
padding: EdgeInsets.all(10.0),
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
return Container(
|
return buildAddPictureButton();
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
|
||||||
gradient: kBlueGradient,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
// Shift the indices since we added a button that's not contained
|
||||||
|
// in the original paths list.
|
||||||
final imagePath = listSnap.data[index - 1];
|
final imagePath = listSnap.data[index - 1];
|
||||||
bloc.fetchThumbnail(imagePath);
|
bloc.fetchThumbnail(imagePath);
|
||||||
return StreamBuilder(
|
|
||||||
stream: bloc.thumbnails,
|
return AsyncThumbnail(
|
||||||
builder: (BuildContext context,
|
cacheStream: bloc.thumbnails,
|
||||||
AsyncSnapshot<Map<String, Future<File>>> thumbailsCacheSnap) {
|
cacheId: getImageThumbnailPath(imagePath),
|
||||||
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),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
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() {
|
void dispose() {
|
||||||
bloc.dispose();
|
bloc.dispose();
|
||||||
super.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.
|
/// Shows an animation of the logo.
|
||||||
class LoadingIndicator extends StatelessWidget {
|
class LoadingIndicator extends StatelessWidget {
|
||||||
|
final double size;
|
||||||
|
|
||||||
|
LoadingIndicator({
|
||||||
|
this.size = 70,
|
||||||
|
});
|
||||||
|
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
width: 70,
|
width: size,
|
||||||
height: 70,
|
height: size,
|
||||||
child: FlareActor(
|
child: FlareActor(
|
||||||
'assets/animations/loading_animation_looped.flr',
|
'assets/animations/loading_animation_looped.flr',
|
||||||
animation: 'Flip',
|
animation: 'Flip',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue