Cache photos to the left and right
This commit is contained in:
parent
a659fefa6d
commit
59dcc38ba5
7
lib/events/photo_opened_event.dart
Normal file
7
lib/events/photo_opened_event.dart
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import 'package:photos/models/photo.dart';
|
||||||
|
|
||||||
|
class PhotoOpenedEvent {
|
||||||
|
final Photo photo;
|
||||||
|
|
||||||
|
PhotoOpenedEvent(this.photo);
|
||||||
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:like_button/like_button.dart';
|
import 'package:like_button/like_button.dart';
|
||||||
|
import 'package:photos/core/event_bus.dart';
|
||||||
|
import 'package:photos/events/photo_opened_event.dart';
|
||||||
import 'package:photos/favorite_photos_repository.dart';
|
import 'package:photos/favorite_photos_repository.dart';
|
||||||
import 'package:photos/models/photo.dart';
|
import 'package:photos/models/photo.dart';
|
||||||
|
import 'package:photos/ui/extents_page_view.dart';
|
||||||
import 'package:photos/ui/zoomable_image.dart';
|
import 'package:photos/ui/zoomable_image.dart';
|
||||||
import 'package:photos/utils/share_util.dart';
|
import 'package:photos/utils/share_util.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
@ -52,29 +55,35 @@ class _DetailPageState extends State<DetailPage> {
|
||||||
|
|
||||||
Widget _buildPageView() {
|
Widget _buildPageView() {
|
||||||
_pageController = PageController(initialPage: _selectedIndex);
|
_pageController = PageController(initialPage: _selectedIndex);
|
||||||
return PageView.builder(
|
return ExtentsPageView.extents(
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final photo = _photos[index];
|
final photo = _photos[index];
|
||||||
final image = ZoomableImage(
|
final image = Hero(
|
||||||
|
tag: photo.hashCode,
|
||||||
|
child: ZoomableImage(
|
||||||
photo,
|
photo,
|
||||||
shouldDisableScroll: (value) {
|
shouldDisableScroll: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_shouldDisableScroll = value;
|
_shouldDisableScroll = value;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
if (index == _selectedIndex) {
|
||||||
|
Bus.instance.fire(PhotoOpenedEvent(photo));
|
||||||
|
}
|
||||||
return image;
|
return image;
|
||||||
},
|
},
|
||||||
onPageChanged: (int index) {
|
onPageChanged: (int index) {
|
||||||
setState(() {
|
|
||||||
_selectedIndex = index;
|
_selectedIndex = index;
|
||||||
});
|
Bus.instance.fire(PhotoOpenedEvent(widget.photos[index]));
|
||||||
},
|
},
|
||||||
physics: _shouldDisableScroll
|
physics: _shouldDisableScroll
|
||||||
? NeverScrollableScrollPhysics()
|
? NeverScrollableScrollPhysics()
|
||||||
: PageScrollPhysics(),
|
: PageScrollPhysics(),
|
||||||
controller: _pageController,
|
controller: _pageController,
|
||||||
itemCount: _photos.length,
|
itemCount: _photos.length,
|
||||||
|
extents: 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:photos/core/event_bus.dart';
|
||||||
|
import 'package:photos/events/photo_opened_event.dart';
|
||||||
import 'package:photos/models/photo.dart';
|
import 'package:photos/models/photo.dart';
|
||||||
import 'package:photos/photo_sync_manager.dart';
|
import 'package:photos/photo_sync_manager.dart';
|
||||||
import 'package:photos/ui/detail_page.dart';
|
import 'package:photos/ui/detail_page.dart';
|
||||||
|
@ -32,6 +35,24 @@ class _GalleryState extends State<Gallery> {
|
||||||
List<Photo> _photos;
|
List<Photo> _photos;
|
||||||
RefreshController _refreshController =
|
RefreshController _refreshController =
|
||||||
RefreshController(initialRefresh: false);
|
RefreshController(initialRefresh: false);
|
||||||
|
StreamSubscription<PhotoOpenedEvent> _subscription;
|
||||||
|
Photo _openedPhoto;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_subscription = Bus.instance.on<PhotoOpenedEvent>().listen((event) {
|
||||||
|
setState(() {
|
||||||
|
_openedPhoto = event.photo;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_subscription.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -127,10 +148,9 @@ class _GalleryState extends State<Gallery> {
|
||||||
? Border.all(width: 4.0, color: Colors.blue)
|
? Border.all(width: 4.0, color: Colors.blue)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
child: Hero(
|
child: photo == _openedPhoto
|
||||||
tag: photo.hashCode,
|
? Hero(tag: photo.hashCode, child: ThumbnailWidget(photo))
|
||||||
child: ThumbnailWidget(photo),
|
: ThumbnailWidget(photo),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:extended_image/extended_image.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:photos/core/cache/image_cache.dart';
|
import 'package:photos/core/cache/image_cache.dart';
|
||||||
|
@ -21,7 +22,8 @@ class ZoomableImage extends StatefulWidget {
|
||||||
_ZoomableImageState createState() => _ZoomableImageState();
|
_ZoomableImageState createState() => _ZoomableImageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ZoomableImageState extends State<ZoomableImage> {
|
class _ZoomableImageState extends State<ZoomableImage>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
ImageProvider _imageProvider;
|
ImageProvider _imageProvider;
|
||||||
bool _loadedSmallThumbnail = false;
|
bool _loadedSmallThumbnail = false;
|
||||||
bool _loadingLargeThumbnail = false;
|
bool _loadingLargeThumbnail = false;
|
||||||
|
@ -29,6 +31,10 @@ class _ZoomableImageState extends State<ZoomableImage> {
|
||||||
bool _loadingFinalImage = false;
|
bool _loadingFinalImage = false;
|
||||||
bool _loadedFinalImage = false;
|
bool _loadedFinalImage = false;
|
||||||
ValueChanged<PhotoViewScaleState> _scaleStateChangedCallback;
|
ValueChanged<PhotoViewScaleState> _scaleStateChangedCallback;
|
||||||
|
// AnimationController _animationController;
|
||||||
|
// Animation _animation;
|
||||||
|
// VoidCallback _animationListener;
|
||||||
|
final doubleTapScales = [1.0, 2.0];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -37,6 +43,10 @@ class _ZoomableImageState extends State<ZoomableImage> {
|
||||||
widget.shouldDisableScroll(value != PhotoViewScaleState.initial);
|
widget.shouldDisableScroll(value != PhotoViewScaleState.initial);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// _animationController = AnimationController(
|
||||||
|
// vsync: this,
|
||||||
|
// duration: const Duration(milliseconds: 100),
|
||||||
|
// );
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,12 +59,46 @@ class _ZoomableImageState extends State<ZoomableImage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_imageProvider != null) {
|
if (_imageProvider != null) {
|
||||||
|
// return ExtendedImage(
|
||||||
|
// image: _imageProvider,
|
||||||
|
// gaplessPlayback: true,
|
||||||
|
// mode: ExtendedImageMode.gesture,
|
||||||
|
// enableSlideOutPage: true,
|
||||||
|
// initGestureConfigHandler: (state) {
|
||||||
|
// return GestureConfig(
|
||||||
|
// inPageView: true,
|
||||||
|
// initialScale: 1.0,
|
||||||
|
// minScale: 1.0,
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// onDoubleTap: (ExtendedImageGestureState state) {
|
||||||
|
// var pointerDownPosition = state.pointerDownPosition;
|
||||||
|
// double begin = state.gestureDetails.totalScale;
|
||||||
|
// double end;
|
||||||
|
// _animation?.removeListener(_animationListener);
|
||||||
|
// _animationController.stop();
|
||||||
|
// _animationController.reset();
|
||||||
|
// if (begin == doubleTapScales[0]) {
|
||||||
|
// end = doubleTapScales[1];
|
||||||
|
// } else {
|
||||||
|
// end = doubleTapScales[0];
|
||||||
|
// }
|
||||||
|
// _animationListener = () {
|
||||||
|
// state.handleDoubleTap(
|
||||||
|
// scale: _animation.value,
|
||||||
|
// doubleTapPosition: pointerDownPosition);
|
||||||
|
// };
|
||||||
|
// _animation =
|
||||||
|
// _animationController.drive(Tween<double>(begin: begin, end: end));
|
||||||
|
// _animation.addListener(_animationListener);
|
||||||
|
// _animationController.forward();
|
||||||
|
// },
|
||||||
|
// );
|
||||||
return PhotoView(
|
return PhotoView(
|
||||||
imageProvider: _imageProvider,
|
imageProvider: _imageProvider,
|
||||||
scaleStateChangedCallback: _scaleStateChangedCallback,
|
scaleStateChangedCallback: _scaleStateChangedCallback,
|
||||||
minScale: PhotoViewComputedScale.contained,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
gaplessPlayback: true,
|
gaplessPlayback: true,
|
||||||
heroAttributes: PhotoViewHeroAttributes(tag: widget.photo.hashCode),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return loadWidget;
|
return loadWidget;
|
||||||
|
|
46
pubspec.lock
46
pubspec.lock
|
@ -36,6 +36,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.3"
|
version: "1.1.3"
|
||||||
|
clock:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: clock
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -120,6 +127,27 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
|
extended_image:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: extended_image
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.0"
|
||||||
|
extended_image_library:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: extended_image_library
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.3"
|
||||||
|
fake_async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fake_async
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -191,6 +219,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.1"
|
version: "0.12.1"
|
||||||
|
http_client_helper:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_client_helper
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.1"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -267,7 +302,7 @@ packages:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.4"
|
version: "1.7.0"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -345,13 +380,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.8"
|
version: "1.5.8"
|
||||||
quiver:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: quiver
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.3"
|
|
||||||
sensors:
|
sensors:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -475,7 +503,7 @@ packages:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.15"
|
version: "0.2.16"
|
||||||
toast:
|
toast:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -50,6 +50,7 @@ dependencies:
|
||||||
flutter_typeahead: ^1.8.1
|
flutter_typeahead: ^1.8.1
|
||||||
pull_to_refresh: ^1.5.7
|
pull_to_refresh: ^1.5.7
|
||||||
fluttertoast: ^4.0.1
|
fluttertoast: ^4.0.1
|
||||||
|
extended_image: ^0.9.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in a new issue