mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-03-24 16:04:43 +01:00
Extract image viewer
This commit is contained in:
parent
4c4ccccf2f
commit
609e2c5bd9
2 changed files with 274 additions and 195 deletions
219
lib/widget/image_viewer.dart
Normal file
219
lib/widget/image_viewer.dart
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:nc_photos/account.dart';
|
||||||
|
import 'package:nc_photos/api/api.dart';
|
||||||
|
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||||
|
import 'package:nc_photos/entity/file.dart';
|
||||||
|
import 'package:nc_photos/k.dart' as k;
|
||||||
|
import 'package:nc_photos/widget/cached_network_image_mod.dart' as mod;
|
||||||
|
|
||||||
|
class ImageViewer extends StatefulWidget {
|
||||||
|
ImageViewer({
|
||||||
|
@required this.account,
|
||||||
|
@required this.file,
|
||||||
|
this.canZoom,
|
||||||
|
this.onLoaded,
|
||||||
|
this.onHeightChanged,
|
||||||
|
this.onZoomStarted,
|
||||||
|
this.onZoomEnded,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
createState() => _ImageViewerState();
|
||||||
|
|
||||||
|
static void preloadImage(Account account, File file) {
|
||||||
|
DefaultCacheManager().getFileStream(
|
||||||
|
_getImageUrl(account, file),
|
||||||
|
headers: {
|
||||||
|
"Authorization": Api.getAuthorizationHeaderValue(account),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Account account;
|
||||||
|
final File file;
|
||||||
|
final bool canZoom;
|
||||||
|
final VoidCallback onLoaded;
|
||||||
|
final void Function(double height) onHeightChanged;
|
||||||
|
final VoidCallback onZoomStarted;
|
||||||
|
final VoidCallback onZoomEnded;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ImageViewerState extends State<ImageViewer>
|
||||||
|
with TickerProviderStateMixin {
|
||||||
|
@override
|
||||||
|
build(BuildContext context) {
|
||||||
|
final content = InteractiveViewer(
|
||||||
|
minScale: 1.0,
|
||||||
|
maxScale: 3.0,
|
||||||
|
transformationController: _transformationController,
|
||||||
|
panEnabled: widget.canZoom,
|
||||||
|
scaleEnabled: widget.canZoom,
|
||||||
|
// allow the image to be zoomed to fill the whole screen
|
||||||
|
child: Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: MediaQuery.of(context).size.height,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: NotificationListener<SizeChangedLayoutNotification>(
|
||||||
|
onNotification: (_) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (_key.currentContext != null) {
|
||||||
|
widget.onHeightChanged?.call(_key.currentContext.size.height);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
child: SizeChangedLayoutNotifier(
|
||||||
|
child: mod.CachedNetworkImage(
|
||||||
|
key: _key,
|
||||||
|
imageUrl: _getImageUrl(widget.account, widget.file),
|
||||||
|
httpHeaders: {
|
||||||
|
"Authorization":
|
||||||
|
Api.getAuthorizationHeaderValue(widget.account),
|
||||||
|
},
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
fadeInDuration: const Duration(),
|
||||||
|
filterQuality: FilterQuality.high,
|
||||||
|
imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet,
|
||||||
|
imageBuilder: (context, child, imageProvider) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_onItemLoaded();
|
||||||
|
});
|
||||||
|
SizeChangedLayoutNotification().dispatch(context);
|
||||||
|
return child;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (widget.canZoom) {
|
||||||
|
return Listener(
|
||||||
|
onPointerDown: (_) {
|
||||||
|
++_finger;
|
||||||
|
if (_finger >= 2) {
|
||||||
|
_setIsZooming(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onPointerUp: (event) {
|
||||||
|
--_finger;
|
||||||
|
if (_finger < 2) {
|
||||||
|
_setIsZooming(false);
|
||||||
|
}
|
||||||
|
_prevFingerPosition = event.position;
|
||||||
|
},
|
||||||
|
child: GestureDetector(
|
||||||
|
onDoubleTap: () {
|
||||||
|
if (_isZoomed) {
|
||||||
|
// restore transformation
|
||||||
|
_autoZoomOut();
|
||||||
|
} else {
|
||||||
|
_autoZoomIn();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: content,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_transformationController.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onItemLoaded() {
|
||||||
|
if (!_isLoaded) {
|
||||||
|
_log.info("[_onItemLoaded]");
|
||||||
|
_isLoaded = true;
|
||||||
|
widget.onLoaded?.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setIsZooming(bool flag) {
|
||||||
|
_isZooming = flag;
|
||||||
|
final next = _isZoomed;
|
||||||
|
if (next != _wasZoomed) {
|
||||||
|
_wasZoomed = next;
|
||||||
|
_log.info("[_setIsZooming] Is zoomed: $next");
|
||||||
|
if (next) {
|
||||||
|
widget.onZoomStarted?.call();
|
||||||
|
} else {
|
||||||
|
widget.onZoomEnded?.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get _isZoomed =>
|
||||||
|
_isZooming || _transformationController.value.getMaxScaleOnAxis() != 1.0;
|
||||||
|
|
||||||
|
/// Called when double tapping the image to zoom in to the default level
|
||||||
|
void _autoZoomIn() {
|
||||||
|
final animController =
|
||||||
|
AnimationController(duration: k.animationDurationShort, vsync: this);
|
||||||
|
final originX = -_prevFingerPosition.dx / 2;
|
||||||
|
final originY = -_prevFingerPosition.dy / 2;
|
||||||
|
final anim = Matrix4Tween(
|
||||||
|
begin: Matrix4.identity(),
|
||||||
|
end: Matrix4.identity()
|
||||||
|
..scale(2.0)
|
||||||
|
..translate(originX, originY))
|
||||||
|
.animate(animController);
|
||||||
|
animController
|
||||||
|
..addListener(() {
|
||||||
|
_transformationController.value = anim.value;
|
||||||
|
})
|
||||||
|
..addStatusListener((status) {
|
||||||
|
if (status == AnimationStatus.completed) {
|
||||||
|
_setIsZooming(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
..forward();
|
||||||
|
_setIsZooming(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when double tapping the zoomed image to zoom out
|
||||||
|
void _autoZoomOut() {
|
||||||
|
final animController =
|
||||||
|
AnimationController(duration: k.animationDurationShort, vsync: this);
|
||||||
|
final anim = Matrix4Tween(
|
||||||
|
begin: _transformationController.value, end: Matrix4.identity())
|
||||||
|
.animate(animController);
|
||||||
|
animController
|
||||||
|
..addListener(() {
|
||||||
|
_transformationController.value = anim.value;
|
||||||
|
})
|
||||||
|
..addStatusListener((status) {
|
||||||
|
if (status == AnimationStatus.completed) {
|
||||||
|
_setIsZooming(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
..forward();
|
||||||
|
_setIsZooming(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
final _key = GlobalKey();
|
||||||
|
final _transformationController = TransformationController();
|
||||||
|
|
||||||
|
var _isLoaded = false;
|
||||||
|
var _isZooming = false;
|
||||||
|
var _wasZoomed = false;
|
||||||
|
|
||||||
|
int _finger = 0;
|
||||||
|
var _prevFingerPosition = Offset(0, 0);
|
||||||
|
|
||||||
|
static final _log = Logger("widget.image_viewer._ImageViewerState");
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getImageUrl(Account account, File file) => api_util.getFilePreviewUrl(
|
||||||
|
account,
|
||||||
|
file,
|
||||||
|
width: 1080,
|
||||||
|
height: 1080,
|
||||||
|
a: true,
|
||||||
|
);
|
|
@ -1,16 +1,12 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:nc_photos/account.dart';
|
import 'package:nc_photos/account.dart';
|
||||||
import 'package:nc_photos/api/api.dart';
|
|
||||||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
|
||||||
import 'package:nc_photos/entity/album.dart';
|
import 'package:nc_photos/entity/album.dart';
|
||||||
import 'package:nc_photos/entity/file.dart';
|
import 'package:nc_photos/entity/file.dart';
|
||||||
import 'package:nc_photos/exception.dart';
|
import 'package:nc_photos/exception.dart';
|
||||||
|
@ -23,7 +19,7 @@ import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||||
import 'package:nc_photos/snack_bar_manager.dart';
|
import 'package:nc_photos/snack_bar_manager.dart';
|
||||||
import 'package:nc_photos/theme.dart';
|
import 'package:nc_photos/theme.dart';
|
||||||
import 'package:nc_photos/use_case/remove.dart';
|
import 'package:nc_photos/use_case/remove.dart';
|
||||||
import 'package:nc_photos/widget/cached_network_image_mod.dart' as mod;
|
import 'package:nc_photos/widget/image_viewer.dart';
|
||||||
import 'package:nc_photos/widget/viewer_detail_pane.dart';
|
import 'package:nc_photos/widget/viewer_detail_pane.dart';
|
||||||
|
|
||||||
class ViewerArguments {
|
class ViewerArguments {
|
||||||
|
@ -60,7 +56,7 @@ class Viewer extends StatefulWidget {
|
||||||
final int startIndex;
|
final int startIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ViewerState extends State<Viewer> with TickerProviderStateMixin {
|
class _ViewerState extends State<Viewer> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -107,58 +103,33 @@ class _ViewerState extends State<Viewer> with TickerProviderStateMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildContent(BuildContext context) {
|
Widget _buildContent(BuildContext context) {
|
||||||
return Listener(
|
return GestureDetector(
|
||||||
onPointerDown: (event) {
|
onTap: () {
|
||||||
++_finger;
|
setState(() {
|
||||||
if (_finger >= 2 && _canZoom()) {
|
_setShowActionBar(!_isShowAppBar);
|
||||||
_setIsZooming(true);
|
});
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onPointerUp: (event) {
|
child: Stack(
|
||||||
--_finger;
|
children: [
|
||||||
if (_finger < 2) {
|
Container(color: Colors.black),
|
||||||
_setIsZooming(false);
|
if (!_pageController.hasClients ||
|
||||||
}
|
!_pageStates[_pageController.page.round()].hasLoaded)
|
||||||
_prevFingerPosition = event.position;
|
Align(
|
||||||
},
|
alignment: Alignment.center,
|
||||||
child: GestureDetector(
|
child: const CircularProgressIndicator(),
|
||||||
onTap: () {
|
|
||||||
setState(() {
|
|
||||||
_setShowActionBar(!_isShowAppBar);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onDoubleTap: () {
|
|
||||||
if (_canZoom()) {
|
|
||||||
if (_isZoomed()) {
|
|
||||||
// restore transformation
|
|
||||||
_autoZoomOut();
|
|
||||||
} else {
|
|
||||||
_autoZoomIn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Container(color: Colors.black),
|
|
||||||
if (!_pageController.hasClients ||
|
|
||||||
!_pageStates[_pageController.page.round()].hasPreloaded)
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: const CircularProgressIndicator(),
|
|
||||||
),
|
|
||||||
PageView.builder(
|
|
||||||
controller: _pageController,
|
|
||||||
itemCount: widget.streamFiles.length,
|
|
||||||
itemBuilder: _buildPage,
|
|
||||||
physics: !platform_k.isWeb && _canSwitchPage()
|
|
||||||
? null
|
|
||||||
: const NeverScrollableScrollPhysics(),
|
|
||||||
),
|
),
|
||||||
if (platform_k.isWeb) ..._buildNavigationButtons(context),
|
PageView.builder(
|
||||||
_buildBottomAppBar(context),
|
controller: _pageController,
|
||||||
_buildAppBar(context),
|
itemCount: widget.streamFiles.length,
|
||||||
],
|
itemBuilder: _buildPage,
|
||||||
),
|
physics: !platform_k.isWeb && _canSwitchPage()
|
||||||
|
? null
|
||||||
|
: const NeverScrollableScrollPhysics(),
|
||||||
|
),
|
||||||
|
if (platform_k.isWeb) ..._buildNavigationButtons(context),
|
||||||
|
_buildBottomAppBar(context),
|
||||||
|
_buildAppBar(context),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -413,50 +384,22 @@ class _ViewerState extends State<Viewer> with TickerProviderStateMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildItemView(BuildContext context, int index) {
|
Widget _buildItemView(BuildContext context, int index) {
|
||||||
return InteractiveViewer(
|
return ImageViewer(
|
||||||
minScale: 1.0,
|
account: widget.account,
|
||||||
maxScale: 3.0,
|
file: widget.streamFiles[index],
|
||||||
transformationController: _transformationController,
|
canZoom: _canZoom(),
|
||||||
panEnabled: _canZoom(),
|
onLoaded: () => _onImageLoaded(index),
|
||||||
scaleEnabled: _canZoom(),
|
onHeightChanged: (height) => _updateItemHeight(index, height),
|
||||||
// allow the image to be zoomed to fill the whole screen
|
onZoomStarted: () {
|
||||||
child: Container(
|
setState(() {
|
||||||
width: MediaQuery.of(context).size.width,
|
_isZoomed = true;
|
||||||
height: MediaQuery.of(context).size.height,
|
});
|
||||||
alignment: Alignment.center,
|
},
|
||||||
child: NotificationListener<SizeChangedLayoutNotification>(
|
onZoomEnded: () {
|
||||||
onNotification: (_) {
|
setState(() {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
_isZoomed = false;
|
||||||
if (_pageStates[index].key.currentContext != null) {
|
});
|
||||||
_updateItemHeight(
|
},
|
||||||
index, _pageStates[index].key.currentContext.size.height);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
child: SizeChangedLayoutNotifier(
|
|
||||||
child: mod.CachedNetworkImage(
|
|
||||||
key: _pageStates[index].key,
|
|
||||||
imageUrl: _getImageUrl(widget.account, widget.streamFiles[index]),
|
|
||||||
httpHeaders: {
|
|
||||||
"Authorization":
|
|
||||||
Api.getAuthorizationHeaderValue(widget.account),
|
|
||||||
},
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
fadeInDuration: const Duration(),
|
|
||||||
filterQuality: FilterQuality.high,
|
|
||||||
imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet,
|
|
||||||
imageBuilder: (context, child, imageProvider) {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
_onItemLoaded(index);
|
|
||||||
});
|
|
||||||
SizeChangedLayoutNotification().dispatch(context);
|
|
||||||
return child;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,30 +434,22 @@ class _ViewerState extends State<Viewer> with TickerProviderStateMixin {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onItemLoaded(int index) {
|
void _onImageLoaded(int index) {
|
||||||
// currently pageview doesn't pre-load pages, we do it manually
|
// currently pageview doesn't pre-load pages, we do it manually
|
||||||
// don't pre-load if user already navigated away
|
// don't pre-load if user already navigated away
|
||||||
if (_pageController.page.round() == index &&
|
if (_pageController.page.round() == index &&
|
||||||
!_pageStates[index].hasPreloaded) {
|
!_pageStates[index].hasLoaded) {
|
||||||
_log.info("[_onItemLoaded] Pre-loading nearby items");
|
_log.info("[_onImageLoaded] Pre-loading nearby images");
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
DefaultCacheManager().getFileStream(
|
final prevFile = widget.streamFiles[index - 1];
|
||||||
_getImageUrl(widget.account, widget.streamFiles[index - 1]),
|
ImageViewer.preloadImage(widget.account, prevFile);
|
||||||
headers: {
|
|
||||||
"Authorization": Api.getAuthorizationHeaderValue(widget.account),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (index + 1 < widget.streamFiles.length) {
|
if (index + 1 < widget.streamFiles.length) {
|
||||||
DefaultCacheManager().getFileStream(
|
final nextFile = widget.streamFiles[index + 1];
|
||||||
_getImageUrl(widget.account, widget.streamFiles[index + 1]),
|
ImageViewer.preloadImage(widget.account, nextFile);
|
||||||
headers: {
|
|
||||||
"Authorization": Api.getAuthorizationHeaderValue(widget.account),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_pageStates[index].hasPreloaded = true;
|
_pageStates[index].hasLoaded = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -721,67 +656,6 @@ class _ViewerState extends State<Viewer> with TickerProviderStateMixin {
|
||||||
_isClosingDetailPane = false;
|
_isClosingDetailPane = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setIsZooming(bool flag) {
|
|
||||||
_isZooming = flag;
|
|
||||||
final next = _isZoomed();
|
|
||||||
if (next != _wasZoomed) {
|
|
||||||
_wasZoomed = next;
|
|
||||||
setState(() {
|
|
||||||
_log.info("[_setIsZooming] Is zoomed: $next");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isZoomed() {
|
|
||||||
return _isZooming ||
|
|
||||||
_transformationController.value.getMaxScaleOnAxis() != 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when double tapping the image to zoom in to the default level
|
|
||||||
void _autoZoomIn() {
|
|
||||||
final animController =
|
|
||||||
AnimationController(duration: k.animationDurationShort, vsync: this);
|
|
||||||
final originX = -_prevFingerPosition.dx / 2;
|
|
||||||
final originY = -_prevFingerPosition.dy / 2;
|
|
||||||
final anim = Matrix4Tween(
|
|
||||||
begin: Matrix4.identity(),
|
|
||||||
end: Matrix4.identity()
|
|
||||||
..scale(2.0)
|
|
||||||
..translate(originX, originY))
|
|
||||||
.animate(animController);
|
|
||||||
animController
|
|
||||||
..addListener(() {
|
|
||||||
_transformationController.value = anim.value;
|
|
||||||
})
|
|
||||||
..addStatusListener((status) {
|
|
||||||
if (status == AnimationStatus.completed) {
|
|
||||||
_setIsZooming(false);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
..forward();
|
|
||||||
_setIsZooming(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when double tapping the zoomed image to zoom out
|
|
||||||
void _autoZoomOut() {
|
|
||||||
final animController =
|
|
||||||
AnimationController(duration: k.animationDurationShort, vsync: this);
|
|
||||||
final anim = Matrix4Tween(
|
|
||||||
begin: _transformationController.value, end: Matrix4.identity())
|
|
||||||
.animate(animController);
|
|
||||||
animController
|
|
||||||
..addListener(() {
|
|
||||||
_transformationController.value = anim.value;
|
|
||||||
})
|
|
||||||
..addStatusListener((status) {
|
|
||||||
if (status == AnimationStatus.completed) {
|
|
||||||
_setIsZooming(false);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
..forward();
|
|
||||||
_setIsZooming(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Switch to the previous image in the stream
|
/// Switch to the previous image in the stream
|
||||||
void _switchToPrevImage() {
|
void _switchToPrevImage() {
|
||||||
_pageController
|
_pageController
|
||||||
|
@ -849,18 +723,10 @@ class _ViewerState extends State<Viewer> with TickerProviderStateMixin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _canSwitchPage() => !_isZoomed();
|
bool _canSwitchPage() => !_isZoomed;
|
||||||
bool _canOpenDetailPane() => !_isZoomed();
|
bool _canOpenDetailPane() => !_isZoomed;
|
||||||
bool _canZoom() => !_isDetailPaneActive;
|
bool _canZoom() => !_isDetailPaneActive;
|
||||||
|
|
||||||
String _getImageUrl(Account account, File file) => api_util.getFilePreviewUrl(
|
|
||||||
account,
|
|
||||||
file,
|
|
||||||
width: 1080,
|
|
||||||
height: 1080,
|
|
||||||
a: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
var _hasInit = false;
|
var _hasInit = false;
|
||||||
|
|
||||||
var _isShowAppBar = true;
|
var _isShowAppBar = true;
|
||||||
|
@ -875,12 +741,7 @@ class _ViewerState extends State<Viewer> with TickerProviderStateMixin {
|
||||||
var _isShowRight = false;
|
var _isShowRight = false;
|
||||||
var _isShowLeft = false;
|
var _isShowLeft = false;
|
||||||
|
|
||||||
var _isZooming = false;
|
var _isZoomed = false;
|
||||||
var _wasZoomed = false;
|
|
||||||
final _transformationController = TransformationController();
|
|
||||||
|
|
||||||
int _finger = 0;
|
|
||||||
Offset _prevFingerPosition;
|
|
||||||
|
|
||||||
PageController _pageController;
|
PageController _pageController;
|
||||||
final _pageStates = <int, _PageState>{};
|
final _pageStates = <int, _PageState>{};
|
||||||
|
@ -896,6 +757,5 @@ class _PageState {
|
||||||
|
|
||||||
ScrollController scrollController;
|
ScrollController scrollController;
|
||||||
double itemHeight;
|
double itemHeight;
|
||||||
bool hasPreloaded = false;
|
bool hasLoaded = false;
|
||||||
GlobalKey key = GlobalKey();
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue