Make image view swipable
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:myapp/core/constants.dart' as Constants;
|
||||
import 'package:myapp/db/db_helper.dart';
|
||||
|
||||
import 'models/face.dart';
|
||||
import 'models/photo.dart';
|
||||
import 'models/search_result.dart';
|
||||
|
||||
class FaceSearchManager {
|
||||
@@ -23,14 +25,13 @@ class FaceSearchManager {
|
||||
.catchError(_onError);
|
||||
}
|
||||
|
||||
Future<List<SearchResult>> getFaceSearchResults(Face face) {
|
||||
return _dio
|
||||
.get(Constants.ENDPOINT + "/search/face/" + face.faceID.toString(),
|
||||
queryParameters: {"user": Constants.USER})
|
||||
.then((response) => (response.data["results"] as List)
|
||||
.map((result) => SearchResult(result))
|
||||
.toList())
|
||||
.catchError(_onError);
|
||||
Future<List<Photo>> getFaceSearchResults(Face face) async {
|
||||
var futures = _dio.get(
|
||||
Constants.ENDPOINT + "/search/face/" + face.faceID.toString(),
|
||||
queryParameters: {"user": Constants.USER}).then((response) => (response
|
||||
.data["results"] as List)
|
||||
.map((result) => (DatabaseHelper.instance.getPhotoByPath(result))));
|
||||
return Future.wait(await futures);
|
||||
}
|
||||
|
||||
void _onError(error) {
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:myapp/core/lru_map.dart';
|
||||
import 'package:myapp/models/photo.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:share_extend/share_extend.dart';
|
||||
import 'extents_page_view.dart';
|
||||
|
||||
class DetailPage extends StatelessWidget {
|
||||
final Photo photo;
|
||||
class DetailPage extends StatefulWidget {
|
||||
final List<Photo> photos;
|
||||
int selectedIndex;
|
||||
|
||||
const DetailPage(this.photo, {Key key}) : super(key: key);
|
||||
DetailPage(this.photos, this.selectedIndex, {Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_DetailPageState createState() => _DetailPageState();
|
||||
}
|
||||
|
||||
class _DetailPageState extends State<DetailPage> {
|
||||
bool _shouldDisableScroll = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Logger().i(photo.localPath);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
actions: <Widget>[
|
||||
@@ -22,32 +29,53 @@ class DetailPage extends StatelessWidget {
|
||||
IconButton(
|
||||
icon: Icon(Icons.share),
|
||||
onPressed: () {
|
||||
ShareExtend.share(photo.localPath, "image");
|
||||
ShareExtend.share(
|
||||
widget.photos[widget.selectedIndex].localPath, "image");
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Center(
|
||||
child: Container(
|
||||
child: _buildContent(context),
|
||||
child: ExtentsPageView.extents(
|
||||
itemBuilder: (context, index) {
|
||||
return _buildItem(context, widget.photos[index]);
|
||||
},
|
||||
onPageChanged: (int index) {
|
||||
widget.selectedIndex = index;
|
||||
},
|
||||
physics: _shouldDisableScroll
|
||||
? NeverScrollableScrollPhysics()
|
||||
: PageScrollPhysics(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContent(BuildContext context) {
|
||||
Widget _buildItem(BuildContext context, Photo photo) {
|
||||
var image = ImageLruCache.getData(photo.localPath) == null
|
||||
? Image.file(
|
||||
File(photo.localPath),
|
||||
filterQuality: FilterQuality.low,
|
||||
)
|
||||
: ImageLruCache.getData(photo.localPath);
|
||||
return GestureDetector(
|
||||
onVerticalDragUpdate: (details) {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: PhotoView(
|
||||
imageProvider: image.image,
|
||||
));
|
||||
ValueChanged<PhotoViewScaleState> scaleStateChangedCallback = (value) {
|
||||
var shouldDisableScroll;
|
||||
if (value == PhotoViewScaleState.initial) {
|
||||
shouldDisableScroll = false;
|
||||
} else {
|
||||
shouldDisableScroll = true;
|
||||
}
|
||||
if (shouldDisableScroll != _shouldDisableScroll) {
|
||||
setState(() {
|
||||
_shouldDisableScroll = shouldDisableScroll;
|
||||
});
|
||||
}
|
||||
};
|
||||
return PhotoView(
|
||||
imageProvider: image.image,
|
||||
scaleStateChangedCallback: scaleStateChangedCallback,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
381
lib/ui/extents_page_view.dart
Normal file
381
lib/ui/extents_page_view.dart
Normal file
@@ -0,0 +1,381 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart' hide PageView;
|
||||
|
||||
/// This is copy-pasted from the Flutter framework with a support added for building
|
||||
/// pages off screen using [Viewport.cacheExtents] and a [LayoutBuilder]
|
||||
///
|
||||
/// Based on commit 3932ffb1cd5dfa0c3891c60977ee4f9cd70ade66 on channel dev
|
||||
|
||||
// Having this global (mutable) page controller is a bit of a hack. We need it
|
||||
// to plumb in the factory for _PagePosition, but it will end up accumulating
|
||||
// a large list of scroll positions. As long as you don't try to actually
|
||||
// control the scroll positions, everything should be fine.
|
||||
final PageController _defaultPageController = PageController();
|
||||
const PageScrollPhysics _kPagePhysics = PageScrollPhysics();
|
||||
|
||||
/// A scrollable list that works page by page.
|
||||
///
|
||||
/// Each child of a page view is forced to be the same size as the viewport.
|
||||
///
|
||||
/// You can use a [PageController] to control which page is visible in the view.
|
||||
/// In addition to being able to control the pixel offset of the content inside
|
||||
/// the [PageView], a [PageController] also lets you control the offset in terms
|
||||
/// of pages, which are increments of the viewport size.
|
||||
///
|
||||
/// The [PageController] can also be used to control the
|
||||
/// [PageController.initialPage], which determines which page is shown when the
|
||||
/// [PageView] is first constructed, and the [PageController.viewportFraction],
|
||||
/// which determines the size of the pages as a fraction of the viewport size.
|
||||
///
|
||||
/// {@youtube 560 315 https://www.youtube.com/watch?v=J1gE9xvph-A}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [PageController], which controls which page is visible in the view.
|
||||
/// * [SingleChildScrollView], when you need to make a single child scrollable.
|
||||
/// * [ListView], for a scrollable list of boxes.
|
||||
/// * [GridView], for a scrollable grid of boxes.
|
||||
/// * [ScrollNotification] and [NotificationListener], which can be used to watch
|
||||
/// the scroll position without using a [ScrollController].
|
||||
class ExtentsPageView extends StatefulWidget {
|
||||
/// Creates a scrollable list that works page by page from an explicit [List]
|
||||
/// of widgets.
|
||||
///
|
||||
/// This constructor is appropriate for page views with a small number of
|
||||
/// children because constructing the [List] requires doing work for every
|
||||
/// child that could possibly be displayed in the page view, instead of just
|
||||
/// those children that are actually visible.
|
||||
ExtentsPageView({
|
||||
Key key,
|
||||
this.scrollDirection = Axis.horizontal,
|
||||
this.reverse = false,
|
||||
PageController controller,
|
||||
this.physics,
|
||||
this.pageSnapping = true,
|
||||
this.onPageChanged,
|
||||
List<Widget> children = const <Widget>[],
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
}) : controller = controller ?? _defaultPageController,
|
||||
childrenDelegate = SliverChildListDelegate(children),
|
||||
extents = 0,
|
||||
super(key: key);
|
||||
|
||||
/// Creates a scrollable list that works page by page using widgets that are
|
||||
/// created on demand.
|
||||
///
|
||||
/// This constructor is appropriate for page views with a large (or infinite)
|
||||
/// number of children because the builder is called only for those children
|
||||
/// that are actually visible.
|
||||
///
|
||||
/// Providing a non-null [itemCount] lets the [PageView] compute the maximum
|
||||
/// scroll extent.
|
||||
///
|
||||
/// [itemBuilder] will be called only with indices greater than or equal to
|
||||
/// zero and less than [itemCount].
|
||||
///
|
||||
/// [PageView.builder] by default does not support child reordering. If
|
||||
/// you are planning to change child order at a later time, consider using
|
||||
/// [PageView] or [PageView.custom].
|
||||
ExtentsPageView.builder({
|
||||
Key key,
|
||||
this.scrollDirection = Axis.horizontal,
|
||||
this.reverse = false,
|
||||
PageController controller,
|
||||
this.physics,
|
||||
this.pageSnapping = true,
|
||||
this.onPageChanged,
|
||||
@required IndexedWidgetBuilder itemBuilder,
|
||||
int itemCount,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
}) : controller = controller ?? _defaultPageController,
|
||||
childrenDelegate =
|
||||
SliverChildBuilderDelegate(itemBuilder, childCount: itemCount),
|
||||
extents = 0,
|
||||
super(key: key);
|
||||
|
||||
ExtentsPageView.extents({
|
||||
Key key,
|
||||
this.extents = 1,
|
||||
this.scrollDirection = Axis.horizontal,
|
||||
this.reverse = false,
|
||||
PageController controller,
|
||||
this.physics,
|
||||
this.pageSnapping = true,
|
||||
this.onPageChanged,
|
||||
@required IndexedWidgetBuilder itemBuilder,
|
||||
int itemCount,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
}) : controller = controller ?? _defaultPageController,
|
||||
childrenDelegate = SliverChildBuilderDelegate(
|
||||
itemBuilder,
|
||||
childCount: itemCount,
|
||||
addAutomaticKeepAlives: false,
|
||||
addRepaintBoundaries: false,
|
||||
),
|
||||
super(key: key);
|
||||
|
||||
/// Creates a scrollable list that works page by page with a custom child
|
||||
/// model.
|
||||
///
|
||||
/// {@tool sample}
|
||||
///
|
||||
/// This [PageView] uses a custom [SliverChildBuilderDelegate] to support child
|
||||
/// reordering.
|
||||
///
|
||||
/// ```dart
|
||||
/// class MyPageView extends StatefulWidget {
|
||||
/// @override
|
||||
/// _MyPageViewState createState() => _MyPageViewState();
|
||||
/// }
|
||||
///
|
||||
/// class _MyPageViewState extends State<MyPageView> {
|
||||
/// List<String> items = <String>['1', '2', '3', '4', '5'];
|
||||
///
|
||||
/// void _reverse() {
|
||||
/// setState(() {
|
||||
/// items = items.reversed.toList();
|
||||
/// });
|
||||
/// }
|
||||
///
|
||||
/// @override
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return Scaffold(
|
||||
/// body: SafeArea(
|
||||
/// child: PageView.custom(
|
||||
/// childrenDelegate: SliverChildBuilderDelegate(
|
||||
/// (BuildContext context, int index) {
|
||||
/// return KeepAlive(
|
||||
/// data: items[index],
|
||||
/// key: ValueKey<String>(items[index]),
|
||||
/// );
|
||||
/// },
|
||||
/// childCount: items.length,
|
||||
/// findChildIndexCallback: (Key key) {
|
||||
/// final ValueKey valueKey = key;
|
||||
/// final String data = valueKey.value;
|
||||
/// return items.indexOf(data);
|
||||
/// }
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// bottomNavigationBar: BottomAppBar(
|
||||
/// child: Row(
|
||||
/// mainAxisAlignment: MainAxisAlignment.center,
|
||||
/// children: <Widget>[
|
||||
/// FlatButton(
|
||||
/// onPressed: () => _reverse(),
|
||||
/// child: Text('Reverse items'),
|
||||
/// ),
|
||||
/// ],
|
||||
/// ),
|
||||
/// ),
|
||||
/// );
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// class KeepAlive extends StatefulWidget {
|
||||
/// const KeepAlive({Key key, this.data}) : super(key: key);
|
||||
///
|
||||
/// final String data;
|
||||
///
|
||||
/// @override
|
||||
/// _KeepAliveState createState() => _KeepAliveState();
|
||||
/// }
|
||||
///
|
||||
/// class _KeepAliveState extends State<KeepAlive> with AutomaticKeepAliveClientMixin{
|
||||
/// @override
|
||||
/// bool get wantKeepAlive => true;
|
||||
///
|
||||
/// @override
|
||||
/// Widget build(BuildContext context) {
|
||||
/// super.build(context);
|
||||
/// return Text(widget.data);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
ExtentsPageView.custom({
|
||||
Key key,
|
||||
this.scrollDirection = Axis.horizontal,
|
||||
this.reverse = false,
|
||||
PageController controller,
|
||||
this.physics,
|
||||
this.pageSnapping = true,
|
||||
this.onPageChanged,
|
||||
@required this.childrenDelegate,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
}) : assert(childrenDelegate != null),
|
||||
extents = 0,
|
||||
controller = controller ?? _defaultPageController,
|
||||
super(key: key);
|
||||
|
||||
/// The number of pages to build off screen.
|
||||
///
|
||||
/// For example, a value of `1` builds one page ahead and one page behind,
|
||||
/// for a total of three built pages.
|
||||
///
|
||||
/// This is especially useful for making sure heavyweight widgets have a chance
|
||||
/// to load off-screen before the user pulls it into the viewport.
|
||||
final int extents;
|
||||
|
||||
/// The axis along which the page view scrolls.
|
||||
///
|
||||
/// Defaults to [Axis.horizontal].
|
||||
final Axis scrollDirection;
|
||||
|
||||
/// Whether the page view scrolls in the reading direction.
|
||||
///
|
||||
/// For example, if the reading direction is left-to-right and
|
||||
/// [scrollDirection] is [Axis.horizontal], then the page view scrolls from
|
||||
/// left to right when [reverse] is false and from right to left when
|
||||
/// [reverse] is true.
|
||||
///
|
||||
/// Similarly, if [scrollDirection] is [Axis.vertical], then the page view
|
||||
/// scrolls from top to bottom when [reverse] is false and from bottom to top
|
||||
/// when [reverse] is true.
|
||||
///
|
||||
/// Defaults to false.
|
||||
final bool reverse;
|
||||
|
||||
/// An object that can be used to control the position to which this page
|
||||
/// view is scrolled.
|
||||
final PageController controller;
|
||||
|
||||
/// How the page view should respond to user input.
|
||||
///
|
||||
/// For example, determines how the page view continues to animate after the
|
||||
/// user stops dragging the page view.
|
||||
///
|
||||
/// The physics are modified to snap to page boundaries using
|
||||
/// [PageScrollPhysics] prior to being used.
|
||||
///
|
||||
/// Defaults to matching platform conventions.
|
||||
final ScrollPhysics physics;
|
||||
|
||||
/// Set to false to disable page snapping, useful for custom scroll behavior.
|
||||
final bool pageSnapping;
|
||||
|
||||
/// Called whenever the page in the center of the viewport changes.
|
||||
final ValueChanged<int> onPageChanged;
|
||||
|
||||
/// A delegate that provides the children for the [PageView].
|
||||
///
|
||||
/// The [PageView.custom] constructor lets you specify this delegate
|
||||
/// explicitly. The [PageView] and [PageView.builder] constructors create a
|
||||
/// [childrenDelegate] that wraps the given [List] and [IndexedWidgetBuilder],
|
||||
/// respectively.
|
||||
final SliverChildDelegate childrenDelegate;
|
||||
|
||||
/// {@macro flutter.widgets.scrollable.dragStartBehavior}
|
||||
final DragStartBehavior dragStartBehavior;
|
||||
|
||||
@override
|
||||
_PageViewState createState() => _PageViewState();
|
||||
}
|
||||
|
||||
class _PageViewState extends State<ExtentsPageView> {
|
||||
int _lastReportedPage = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_lastReportedPage = widget.controller.initialPage;
|
||||
}
|
||||
|
||||
AxisDirection _getDirection(BuildContext context) {
|
||||
switch (widget.scrollDirection) {
|
||||
case Axis.horizontal:
|
||||
assert(debugCheckHasDirectionality(context));
|
||||
final TextDirection textDirection = Directionality.of(context);
|
||||
final AxisDirection axisDirection =
|
||||
textDirectionToAxisDirection(textDirection);
|
||||
return widget.reverse
|
||||
? flipAxisDirection(axisDirection)
|
||||
: axisDirection;
|
||||
case Axis.vertical:
|
||||
return widget.reverse ? AxisDirection.up : AxisDirection.down;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final AxisDirection axisDirection = _getDirection(context);
|
||||
final ScrollPhysics physics = widget.pageSnapping
|
||||
? _kPagePhysics.applyTo(widget.physics)
|
||||
: widget.physics;
|
||||
|
||||
return NotificationListener<ScrollNotification>(
|
||||
onNotification: (ScrollNotification notification) {
|
||||
if (notification.depth == 0 &&
|
||||
widget.onPageChanged != null &&
|
||||
notification is ScrollUpdateNotification) {
|
||||
final PageMetrics metrics = notification.metrics;
|
||||
final int currentPage = metrics.page.round();
|
||||
if (currentPage != _lastReportedPage) {
|
||||
_lastReportedPage = currentPage;
|
||||
widget.onPageChanged(currentPage);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: Scrollable(
|
||||
dragStartBehavior: widget.dragStartBehavior,
|
||||
axisDirection: axisDirection,
|
||||
controller: widget.controller,
|
||||
physics: physics,
|
||||
viewportBuilder: (BuildContext context, ViewportOffset position) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
assert(constraints.hasBoundedHeight);
|
||||
assert(constraints.hasBoundedWidth);
|
||||
|
||||
double cacheExtent;
|
||||
|
||||
switch (widget.scrollDirection) {
|
||||
case Axis.vertical:
|
||||
cacheExtent = constraints.maxHeight * widget.extents;
|
||||
break;
|
||||
|
||||
case Axis.horizontal:
|
||||
default:
|
||||
cacheExtent = constraints.maxWidth * widget.extents;
|
||||
break;
|
||||
}
|
||||
|
||||
return Viewport(
|
||||
cacheExtent: cacheExtent,
|
||||
axisDirection: axisDirection,
|
||||
offset: position,
|
||||
slivers: <Widget>[
|
||||
SliverFillViewport(
|
||||
viewportFraction: widget.controller.viewportFraction,
|
||||
delegate: widget.childrenDelegate,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder description) {
|
||||
super.debugFillProperties(description);
|
||||
description
|
||||
.add(EnumProperty<Axis>('scrollDirection', widget.scrollDirection));
|
||||
description.add(
|
||||
FlagProperty('reverse', value: widget.reverse, ifTrue: 'reversed'));
|
||||
description.add(DiagnosticsProperty<PageController>(
|
||||
'controller', widget.controller,
|
||||
showName: false));
|
||||
description.add(DiagnosticsProperty<ScrollPhysics>(
|
||||
'physics', widget.physics,
|
||||
showName: false));
|
||||
description.add(FlagProperty('pageSnapping',
|
||||
value: widget.pageSnapping, ifFalse: 'snapping disabled'));
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:myapp/db/db_helper.dart';
|
||||
import 'package:myapp/face_search_manager.dart';
|
||||
import 'package:myapp/models/face.dart';
|
||||
import 'package:myapp/models/photo.dart';
|
||||
import 'package:myapp/models/search_result.dart';
|
||||
import 'package:myapp/ui/circular_network_image_widget.dart';
|
||||
import 'package:myapp/core/constants.dart' as Constants;
|
||||
import 'package:myapp/ui/image_widget.dart';
|
||||
import 'package:myapp/ui/network_image_detail_page.dart';
|
||||
|
||||
import 'detail_page.dart';
|
||||
|
||||
@@ -38,14 +35,14 @@ class FaceSearchResultsPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
FutureBuilder<List<SearchResult>> _getBody() {
|
||||
return FutureBuilder<List<SearchResult>>(
|
||||
FutureBuilder<List<Photo>> _getBody() {
|
||||
return FutureBuilder<List<Photo>>(
|
||||
future: _faceSearchManager.getFaceSearchResults(_face),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return GridView.builder(
|
||||
itemBuilder: (_, index) =>
|
||||
_buildItem(context, snapshot.data[index].path),
|
||||
_buildItem(context, snapshot.data, index),
|
||||
itemCount: snapshot.data.length,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 4,
|
||||
@@ -57,40 +54,18 @@ class FaceSearchResultsPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildItem(BuildContext context, String path) {
|
||||
Widget _buildItem(BuildContext context, List<Photo> photos, int index) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
_routeToDetailPage(path, context);
|
||||
_routeToDetailPage(photos, index, context);
|
||||
},
|
||||
child: _getImage(path),
|
||||
child: ImageWidget(photos[index]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _getImage(String path) {
|
||||
return FutureBuilder<Photo>(
|
||||
future: DatabaseHelper.instance.getPhotoByPath(path),
|
||||
builder: (_, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return ImageWidget(snapshot.data);
|
||||
} else if (snapshot.hasError) {
|
||||
return Container(
|
||||
margin: EdgeInsets.all(2),
|
||||
child: Image.network(Constants.ENDPOINT + "/" + path,
|
||||
height: 124, width: 124, fit: BoxFit.cover),
|
||||
);
|
||||
} else {
|
||||
return Text("Loading...");
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _routeToDetailPage(String path, BuildContext context) async {
|
||||
Widget page = NetworkImageDetailPage(path);
|
||||
var photo = await DatabaseHelper.instance.getPhotoByPath(path);
|
||||
if (photo != null) {
|
||||
page = DetailPage(photo);
|
||||
}
|
||||
void _routeToDetailPage(
|
||||
List<Photo> photos, int index, BuildContext context) async {
|
||||
var page = DetailPage(photos, index);
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
|
||||
@@ -160,7 +160,7 @@ class _GalleryState extends State<Gallery> {
|
||||
}
|
||||
|
||||
void routeToDetailPage(Photo photo, BuildContext context) {
|
||||
final page = DetailPage(photo);
|
||||
final page = DetailPage(photoLoader.photos, photoLoader.photos.indexOf(photo));
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
|
||||
Reference in New Issue
Block a user