Remove buggy delete
This commit is contained in:
parent
5b14f3ca6e
commit
0d63576118
4 changed files with 387 additions and 75 deletions
|
@ -2,28 +2,22 @@ import 'package:flutter/cupertino.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:myapp/core/lru_map.dart';
|
||||
import 'package:myapp/db/db_helper.dart';
|
||||
import 'package:myapp/models/photo.dart';
|
||||
import 'package:myapp/photo_loader.dart';
|
||||
import 'package:myapp/ui/extents_page_view.dart';
|
||||
import 'package:myapp/ui/zoomable_image.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:myapp/utils/share_util.dart';
|
||||
|
||||
class DetailPage extends StatefulWidget {
|
||||
final List<Photo> photos;
|
||||
final int selectedIndex;
|
||||
final Function(Photo) onPhotoDeleted;
|
||||
|
||||
DetailPage(this.photos, this.selectedIndex, {this.onPhotoDeleted, key})
|
||||
: super(key: key);
|
||||
DetailPage(this.photos, this.selectedIndex, {key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_DetailPageState createState() => _DetailPageState();
|
||||
}
|
||||
|
||||
class _DetailPageState extends State<DetailPage> {
|
||||
PhotoLoader get photoLoader => Provider.of<PhotoLoader>(context);
|
||||
bool _shouldDisableScroll = false;
|
||||
List<Photo> _photos;
|
||||
int _selectedIndex = 0;
|
||||
|
@ -56,9 +50,9 @@ class _DetailPageState extends State<DetailPage> {
|
|||
);
|
||||
}
|
||||
|
||||
PageView _buildPageView() {
|
||||
Widget _buildPageView() {
|
||||
_pageController = PageController(initialPage: _selectedIndex);
|
||||
return PageView.builder(
|
||||
return ExtentsPageView.extents(
|
||||
itemBuilder: (context, index) {
|
||||
final photo = _photos[index];
|
||||
if (_cachedImages.get(photo.generatedId) != null) {
|
||||
|
@ -75,6 +69,7 @@ class _DetailPageState extends State<DetailPage> {
|
|||
_cachedImages.put(photo.generatedId, image);
|
||||
return image;
|
||||
},
|
||||
extents: 1,
|
||||
onPageChanged: (int index) {
|
||||
Logger().i("onPageChanged to " + index.toString());
|
||||
_selectedIndex = index;
|
||||
|
@ -90,12 +85,6 @@ class _DetailPageState extends State<DetailPage> {
|
|||
AppBar _buildAppBar() {
|
||||
return AppBar(
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete),
|
||||
onPressed: () {
|
||||
_showDeletePhotosSheet(context, _photos[_selectedIndex]);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.share),
|
||||
onPressed: () async {
|
||||
|
@ -105,55 +94,4 @@ class _DetailPageState extends State<DetailPage> {
|
|||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _showDeletePhotosSheet(BuildContext context, Photo photo) {
|
||||
final action = CupertinoActionSheet(
|
||||
actions: <Widget>[
|
||||
CupertinoActionSheetAction(
|
||||
child: Text("Delete on device"),
|
||||
isDestructiveAction: true,
|
||||
onPressed: () async {
|
||||
await _deletePhoto(context, photo, false);
|
||||
},
|
||||
),
|
||||
CupertinoActionSheetAction(
|
||||
child: Text("Delete everywhere [WiP]"),
|
||||
isDestructiveAction: true,
|
||||
onPressed: () async {
|
||||
await _deletePhoto(context, photo, true);
|
||||
},
|
||||
)
|
||||
],
|
||||
cancelButton: CupertinoActionSheetAction(
|
||||
child: Text("Cancel"),
|
||||
onPressed: () {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
},
|
||||
),
|
||||
);
|
||||
showCupertinoModalPopup(context: context, builder: (_) => action);
|
||||
}
|
||||
|
||||
Future _deletePhoto(
|
||||
BuildContext context, Photo photo, bool deleteEverywhere) async {
|
||||
await PhotoManager.editor.deleteWithIds([photo.localId]);
|
||||
|
||||
deleteEverywhere
|
||||
? await DatabaseHelper.instance.markPhotoForDeletion(photo)
|
||||
: await DatabaseHelper.instance.deletePhoto(photo);
|
||||
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
|
||||
_pageController
|
||||
.nextPage(duration: Duration(milliseconds: 250), curve: Curves.ease)
|
||||
.then((value) {
|
||||
if (widget.onPhotoDeleted != null) {
|
||||
widget.onPhotoDeleted(photo);
|
||||
}
|
||||
_pageController.previousPage(
|
||||
duration: Duration(milliseconds: 1), curve: Curves.linear); // h4ck
|
||||
});
|
||||
|
||||
photoLoader.reloadPhotos();
|
||||
}
|
||||
}
|
||||
|
|
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'));
|
||||
}
|
||||
}
|
|
@ -133,12 +133,6 @@ class _GalleryState extends State<Gallery> {
|
|||
final page = DetailPage(
|
||||
_photos,
|
||||
_photos.indexOf(photo),
|
||||
onPhotoDeleted: (photo) {
|
||||
setState(() {
|
||||
Logger().i("Photo deleted! ");
|
||||
_photos.remove(photo);
|
||||
});
|
||||
},
|
||||
);
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
|
|
|
@ -25,7 +25,6 @@ class GalleryAppBarWidget extends StatefulWidget
|
|||
}
|
||||
|
||||
class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
||||
PhotoLoader get photoLoader => Provider.of<PhotoLoader>(context);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.selectedPhotos.isEmpty) {
|
||||
|
@ -106,7 +105,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
: await DatabaseHelper.instance.deletePhoto(photo);
|
||||
}
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
photoLoader.reloadPhotos();
|
||||
PhotoLoader.instance.reloadPhotos();
|
||||
if (widget.onPhotosDeleted != null) {
|
||||
widget.onPhotosDeleted(widget.selectedPhotos.toList());
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue