fix: 图片预览放大、取消下滑关闭图片预览
This commit is contained in:
@ -135,115 +135,103 @@ class _ImagePreviewState extends State<ImagePreview>
|
|||||||
),
|
),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
DismissiblePage(
|
GestureDetector(
|
||||||
backgroundColor: Colors.transparent,
|
onLongPress: () => onOpenMenu(),
|
||||||
onDismissed: () {
|
child: ExtendedImageGesturePageView.builder(
|
||||||
Navigator.of(context).pop();
|
controller: ExtendedPageController(
|
||||||
},
|
initialPage: _previewController.initialPage.value,
|
||||||
// Note that scrollable widget inside DismissiblePage might limit the functionality
|
pageSpacing: 0,
|
||||||
// If scroll direction matches DismissiblePage direction
|
|
||||||
direction: DismissiblePageDismissDirection.down,
|
|
||||||
disabled: _dismissDisabled,
|
|
||||||
isFullScreen: true,
|
|
||||||
child: GestureDetector(
|
|
||||||
onLongPress: () => onOpenMenu(),
|
|
||||||
child: ExtendedImageGesturePageView.builder(
|
|
||||||
controller: ExtendedPageController(
|
|
||||||
initialPage: _previewController.initialPage.value,
|
|
||||||
pageSpacing: 0,
|
|
||||||
),
|
|
||||||
onPageChanged: (int index) =>
|
|
||||||
_previewController.onChange(index),
|
|
||||||
canScrollPage: (GestureDetails? gestureDetails) =>
|
|
||||||
gestureDetails!.totalScale! <= 1.0,
|
|
||||||
itemCount: widget.imgList!.length,
|
|
||||||
itemBuilder: (BuildContext context, int index) {
|
|
||||||
return ExtendedImage.network(
|
|
||||||
widget.imgList![index],
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
mode: ExtendedImageMode.gesture,
|
|
||||||
onDoubleTap: (ExtendedImageGestureState state) {
|
|
||||||
final Offset? pointerDownPosition =
|
|
||||||
state.pointerDownPosition;
|
|
||||||
final double? begin = state.gestureDetails!.totalScale;
|
|
||||||
double end;
|
|
||||||
|
|
||||||
//remove old
|
|
||||||
_doubleClickAnimation
|
|
||||||
?.removeListener(_doubleClickAnimationListener);
|
|
||||||
|
|
||||||
//stop pre
|
|
||||||
_doubleClickAnimationController.stop();
|
|
||||||
|
|
||||||
//reset to use
|
|
||||||
_doubleClickAnimationController.reset();
|
|
||||||
|
|
||||||
if (begin == doubleTapScales[0]) {
|
|
||||||
setState(() {
|
|
||||||
_dismissDisabled = true;
|
|
||||||
});
|
|
||||||
end = doubleTapScales[1];
|
|
||||||
} else {
|
|
||||||
setState(() {
|
|
||||||
_dismissDisabled = false;
|
|
||||||
});
|
|
||||||
end = doubleTapScales[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
_doubleClickAnimationListener = () {
|
|
||||||
state.handleDoubleTap(
|
|
||||||
scale: _doubleClickAnimation!.value,
|
|
||||||
doubleTapPosition: pointerDownPosition);
|
|
||||||
};
|
|
||||||
_doubleClickAnimation = _doubleClickAnimationController
|
|
||||||
.drive(Tween<double>(begin: begin, end: end));
|
|
||||||
|
|
||||||
_doubleClickAnimation!
|
|
||||||
.addListener(_doubleClickAnimationListener);
|
|
||||||
|
|
||||||
_doubleClickAnimationController.forward();
|
|
||||||
},
|
|
||||||
// ignore: body_might_complete_normally_nullable
|
|
||||||
loadStateChanged: (ExtendedImageState state) {
|
|
||||||
if (state.extendedImageLoadState == LoadState.loading) {
|
|
||||||
final ImageChunkEvent? loadingProgress =
|
|
||||||
state.loadingProgress;
|
|
||||||
final double? progress =
|
|
||||||
loadingProgress?.expectedTotalBytes != null
|
|
||||||
? loadingProgress!.cumulativeBytesLoaded /
|
|
||||||
loadingProgress.expectedTotalBytes!
|
|
||||||
: null;
|
|
||||||
return Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
SizedBox(
|
|
||||||
width: 150.0,
|
|
||||||
child: LinearProgressIndicator(
|
|
||||||
value: progress,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// const SizedBox(height: 10.0),
|
|
||||||
// Text('${((progress ?? 0.0) * 100).toInt()}%',),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
initGestureConfigHandler: (ExtendedImageState state) {
|
|
||||||
return GestureConfig(
|
|
||||||
inPageView: true,
|
|
||||||
initialScale: 1.0,
|
|
||||||
maxScale: 5.0,
|
|
||||||
animationMaxScale: 6.0,
|
|
||||||
initialAlignment: InitialAlignment.center,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
onPageChanged: (int index) => _previewController.onChange(index),
|
||||||
|
canScrollPage: (GestureDetails? gestureDetails) =>
|
||||||
|
gestureDetails!.totalScale! <= 1.0,
|
||||||
|
itemCount: widget.imgList!.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return ExtendedImage.network(
|
||||||
|
widget.imgList![index],
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
mode: ExtendedImageMode.gesture,
|
||||||
|
onDoubleTap: (ExtendedImageGestureState state) {
|
||||||
|
final Offset? pointerDownPosition =
|
||||||
|
state.pointerDownPosition;
|
||||||
|
final double? begin = state.gestureDetails!.totalScale;
|
||||||
|
double end;
|
||||||
|
|
||||||
|
//remove old
|
||||||
|
_doubleClickAnimation
|
||||||
|
?.removeListener(_doubleClickAnimationListener);
|
||||||
|
|
||||||
|
//stop pre
|
||||||
|
_doubleClickAnimationController.stop();
|
||||||
|
|
||||||
|
//reset to use
|
||||||
|
_doubleClickAnimationController.reset();
|
||||||
|
|
||||||
|
if (begin == doubleTapScales[0]) {
|
||||||
|
setState(() {
|
||||||
|
_dismissDisabled = true;
|
||||||
|
});
|
||||||
|
end = doubleTapScales[1];
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_dismissDisabled = false;
|
||||||
|
});
|
||||||
|
end = doubleTapScales[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
_doubleClickAnimationListener = () {
|
||||||
|
state.handleDoubleTap(
|
||||||
|
scale: _doubleClickAnimation!.value,
|
||||||
|
doubleTapPosition: pointerDownPosition);
|
||||||
|
};
|
||||||
|
_doubleClickAnimation = _doubleClickAnimationController
|
||||||
|
.drive(Tween<double>(begin: begin, end: end));
|
||||||
|
|
||||||
|
_doubleClickAnimation!
|
||||||
|
.addListener(_doubleClickAnimationListener);
|
||||||
|
|
||||||
|
_doubleClickAnimationController.forward();
|
||||||
|
},
|
||||||
|
// ignore: body_might_complete_normally_nullable
|
||||||
|
loadStateChanged: (ExtendedImageState state) {
|
||||||
|
if (state.extendedImageLoadState == LoadState.loading) {
|
||||||
|
final ImageChunkEvent? loadingProgress =
|
||||||
|
state.loadingProgress;
|
||||||
|
final double? progress =
|
||||||
|
loadingProgress?.expectedTotalBytes != null
|
||||||
|
? loadingProgress!.cumulativeBytesLoaded /
|
||||||
|
loadingProgress.expectedTotalBytes!
|
||||||
|
: null;
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(
|
||||||
|
width: 150.0,
|
||||||
|
child: LinearProgressIndicator(
|
||||||
|
value: progress,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// const SizedBox(height: 10.0),
|
||||||
|
// Text('${((progress ?? 0.0) * 100).toInt()}%',),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initGestureConfigHandler: (ExtendedImageState state) {
|
||||||
|
return GestureConfig(
|
||||||
|
inPageView: true,
|
||||||
|
initialScale: 1.0,
|
||||||
|
maxScale: 5.0,
|
||||||
|
animationMaxScale: 6.0,
|
||||||
|
initialAlignment: InitialAlignment.center,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
@ -251,33 +239,49 @@ class _ImagePreviewState extends State<ImagePreview>
|
|||||||
right: 0,
|
right: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 30),
|
left: 20,
|
||||||
decoration: const BoxDecoration(
|
right: 20,
|
||||||
gradient: LinearGradient(
|
bottom: MediaQuery.of(context).padding.bottom + 30),
|
||||||
begin: Alignment.topCenter,
|
decoration: const BoxDecoration(
|
||||||
end: Alignment.bottomCenter,
|
gradient: LinearGradient(
|
||||||
colors: <Color>[
|
begin: Alignment.topCenter,
|
||||||
Colors.transparent,
|
end: Alignment.bottomCenter,
|
||||||
Colors.black87,
|
colors: <Color>[
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.black87,
|
||||||
|
],
|
||||||
|
tileMode: TileMode.mirror,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
widget.imgList!.length > 1
|
||||||
|
? Obx(
|
||||||
|
() => Text.rich(
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
TextSpan(
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white, fontSize: 16),
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: _previewController.currentPage
|
||||||
|
.toString()),
|
||||||
|
const TextSpan(text: ' / '),
|
||||||
|
TextSpan(
|
||||||
|
text:
|
||||||
|
widget.imgList!.length.toString()),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
icon: const Icon(Icons.close, color: Colors.white),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
tileMode: TileMode.mirror,
|
)),
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Obx(
|
|
||||||
() => Text.rich(
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
TextSpan(
|
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 15),
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: _previewController.currentPage.toString()),
|
|
||||||
const TextSpan(text: ' / '),
|
|
||||||
TextSpan(text: widget.imgList!.length.toString()),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user