mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: 动态页图片、图片预览页无障碍适配
This commit is contained in:
@@ -59,40 +59,42 @@ class _ContentState extends State<Content> {
|
|||||||
(pictureItem.height != null && pictureItem.width != null
|
(pictureItem.height != null && pictureItem.width != null
|
||||||
? pictureItem.height! / pictureItem.width!
|
? pictureItem.height! / pictureItem.width!
|
||||||
: 1);
|
: 1);
|
||||||
return GestureDetector(
|
return Semantics(
|
||||||
onTap: () {
|
label: '图片1,共1张',
|
||||||
showDialog(
|
child: GestureDetector(
|
||||||
useSafeArea: false,
|
onTap: () {
|
||||||
context: context,
|
showDialog(
|
||||||
builder: (context) {
|
useSafeArea: false,
|
||||||
return ImagePreview(initialPage: 0, imgList: picList);
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return ImagePreview(initialPage: 0, imgList: picList);
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
child: Container(
|
||||||
},
|
padding: const EdgeInsets.only(top: 4),
|
||||||
child: Container(
|
constraints: BoxConstraints(maxHeight: maxHeight),
|
||||||
padding: const EdgeInsets.only(top: 4),
|
width: box.maxWidth / 2,
|
||||||
constraints: BoxConstraints(maxHeight: maxHeight),
|
height: height,
|
||||||
width: box.maxWidth / 2,
|
child: Stack(
|
||||||
height: height,
|
children: [
|
||||||
child: Stack(
|
Positioned.fill(
|
||||||
children: [
|
child: NetworkImgLayer(
|
||||||
Positioned.fill(
|
src: pictureItem.url,
|
||||||
child: NetworkImgLayer(
|
width: maxWidth / 2,
|
||||||
src: pictureItem.url,
|
height: height,
|
||||||
width: maxWidth / 2,
|
),
|
||||||
height: height,
|
),
|
||||||
),
|
height > Get.size.height * 0.9
|
||||||
),
|
? const PBadge(
|
||||||
height > Get.size.height * 0.9
|
text: '长图',
|
||||||
? const PBadge(
|
right: 8,
|
||||||
text: '长图',
|
bottom: 8,
|
||||||
right: 8,
|
)
|
||||||
bottom: 8,
|
: const SizedBox(),
|
||||||
)
|
],
|
||||||
: const SizedBox(),
|
)),
|
||||||
],
|
));
|
||||||
)),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -106,24 +108,26 @@ class _ContentState extends State<Content> {
|
|||||||
LayoutBuilder(
|
LayoutBuilder(
|
||||||
builder: (context, BoxConstraints box) {
|
builder: (context, BoxConstraints box) {
|
||||||
double maxWidth = box.maxWidth.truncateToDouble();
|
double maxWidth = box.maxWidth.truncateToDouble();
|
||||||
return GestureDetector(
|
return Semantics(
|
||||||
onTap: () {
|
label: '图片${i + 1},共$len张',
|
||||||
showDialog(
|
child: GestureDetector(
|
||||||
useSafeArea: false,
|
onTap: () {
|
||||||
context: context,
|
showDialog(
|
||||||
builder: (context) {
|
useSafeArea: false,
|
||||||
return ImagePreview(initialPage: i, imgList: picList);
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return ImagePreview(initialPage: i, imgList: picList);
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
child: NetworkImgLayer(
|
||||||
},
|
src: pics[i].url,
|
||||||
child: NetworkImgLayer(
|
width: maxWidth,
|
||||||
src: pics[i].url,
|
height: maxWidth,
|
||||||
width: maxWidth,
|
origAspectRatio:
|
||||||
height: maxWidth,
|
pics[i].width!.toInt() / pics[i].height!.toInt(),
|
||||||
origAspectRatio:
|
),
|
||||||
pics[i].width!.toInt() / pics[i].height!.toInt(),
|
));
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -201,7 +205,7 @@ class _ContentState extends State<Content> {
|
|||||||
if (hasPics) ...[
|
if (hasPics) ...[
|
||||||
Text.rich(
|
Text.rich(
|
||||||
picsNodes(),
|
picsNodes(),
|
||||||
semanticsLabel: '动态图片',
|
// semanticsLabel: '动态图片',
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -135,103 +135,107 @@ class _ImagePreviewState extends State<ImagePreview>
|
|||||||
),
|
),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
Semantics(
|
||||||
onLongPress: () => onOpenMenu(),
|
label: '双指缩放、长按保存、左右滑动切换图片',
|
||||||
child: ExtendedImageGesturePageView.builder(
|
child: GestureDetector(
|
||||||
controller: ExtendedPageController(
|
onLongPress: () => onOpenMenu(),
|
||||||
initialPage: _previewController.initialPage.value,
|
child: ExtendedImageGesturePageView.builder(
|
||||||
pageSpacing: 0,
|
controller: ExtendedPageController(
|
||||||
),
|
initialPage: _previewController.initialPage.value,
|
||||||
onPageChanged: (int index) => _previewController.onChange(index),
|
pageSpacing: 0,
|
||||||
canScrollPage: (GestureDetails? gestureDetails) =>
|
),
|
||||||
gestureDetails!.totalScale! <= 1.0,
|
onPageChanged: (int index) =>
|
||||||
itemCount: widget.imgList!.length,
|
_previewController.onChange(index),
|
||||||
itemBuilder: (BuildContext context, int index) {
|
canScrollPage: (GestureDetails? gestureDetails) =>
|
||||||
return ExtendedImage.network(
|
gestureDetails!.totalScale! <= 1.0,
|
||||||
widget.imgList![index],
|
itemCount: widget.imgList!.length,
|
||||||
fit: BoxFit.contain,
|
itemBuilder: (BuildContext context, int index) {
|
||||||
mode: ExtendedImageMode.gesture,
|
return ExtendedImage.network(
|
||||||
onDoubleTap: (ExtendedImageGestureState state) {
|
widget.imgList![index],
|
||||||
final Offset? pointerDownPosition =
|
fit: BoxFit.contain,
|
||||||
state.pointerDownPosition;
|
mode: ExtendedImageMode.gesture,
|
||||||
final double? begin = state.gestureDetails!.totalScale;
|
onDoubleTap: (ExtendedImageGestureState state) {
|
||||||
double end;
|
final Offset? pointerDownPosition =
|
||||||
|
state.pointerDownPosition;
|
||||||
|
final double? begin = state.gestureDetails!.totalScale;
|
||||||
|
double end;
|
||||||
|
|
||||||
//remove old
|
//remove old
|
||||||
_doubleClickAnimation
|
_doubleClickAnimation
|
||||||
?.removeListener(_doubleClickAnimationListener);
|
?.removeListener(_doubleClickAnimationListener);
|
||||||
|
|
||||||
//stop pre
|
//stop pre
|
||||||
_doubleClickAnimationController.stop();
|
_doubleClickAnimationController.stop();
|
||||||
|
|
||||||
//reset to use
|
//reset to use
|
||||||
_doubleClickAnimationController.reset();
|
_doubleClickAnimationController.reset();
|
||||||
|
|
||||||
if (begin == doubleTapScales[0]) {
|
if (begin == doubleTapScales[0]) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_dismissDisabled = true;
|
_dismissDisabled = true;
|
||||||
});
|
});
|
||||||
end = doubleTapScales[1];
|
end = doubleTapScales[1];
|
||||||
} else {
|
} else {
|
||||||
setState(() {
|
setState(() {
|
||||||
_dismissDisabled = false;
|
_dismissDisabled = false;
|
||||||
});
|
});
|
||||||
end = doubleTapScales[0];
|
end = doubleTapScales[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
_doubleClickAnimationListener = () {
|
_doubleClickAnimationListener = () {
|
||||||
state.handleDoubleTap(
|
state.handleDoubleTap(
|
||||||
scale: _doubleClickAnimation!.value,
|
scale: _doubleClickAnimation!.value,
|
||||||
doubleTapPosition: pointerDownPosition);
|
doubleTapPosition: pointerDownPosition);
|
||||||
};
|
};
|
||||||
_doubleClickAnimation = _doubleClickAnimationController
|
_doubleClickAnimation = _doubleClickAnimationController
|
||||||
.drive(Tween<double>(begin: begin, end: end));
|
.drive(Tween<double>(begin: begin, end: end));
|
||||||
|
|
||||||
_doubleClickAnimation!
|
_doubleClickAnimation!
|
||||||
.addListener(_doubleClickAnimationListener);
|
.addListener(_doubleClickAnimationListener);
|
||||||
|
|
||||||
_doubleClickAnimationController.forward();
|
_doubleClickAnimationController.forward();
|
||||||
},
|
},
|
||||||
// ignore: body_might_complete_normally_nullable
|
// ignore: body_might_complete_normally_nullable
|
||||||
loadStateChanged: (ExtendedImageState state) {
|
loadStateChanged: (ExtendedImageState state) {
|
||||||
if (state.extendedImageLoadState == LoadState.loading) {
|
if (state.extendedImageLoadState == LoadState.loading) {
|
||||||
final ImageChunkEvent? loadingProgress =
|
final ImageChunkEvent? loadingProgress =
|
||||||
state.loadingProgress;
|
state.loadingProgress;
|
||||||
final double? progress =
|
final double? progress =
|
||||||
loadingProgress?.expectedTotalBytes != null
|
loadingProgress?.expectedTotalBytes != null
|
||||||
? loadingProgress!.cumulativeBytesLoaded /
|
? loadingProgress!.cumulativeBytesLoaded /
|
||||||
loadingProgress.expectedTotalBytes!
|
loadingProgress.expectedTotalBytes!
|
||||||
: null;
|
: null;
|
||||||
return Center(
|
return Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 150.0,
|
width: 150.0,
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
value: progress,
|
value: progress,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
// const SizedBox(height: 10.0),
|
||||||
// const SizedBox(height: 10.0),
|
// Text('${((progress ?? 0.0) * 100).toInt()}%',),
|
||||||
// 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,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
},
|
);
|
||||||
initGestureConfigHandler: (ExtendedImageState state) {
|
},
|
||||||
return GestureConfig(
|
),
|
||||||
inPageView: true,
|
|
||||||
initialScale: 1.0,
|
|
||||||
maxScale: 5.0,
|
|
||||||
animationMaxScale: 6.0,
|
|
||||||
initialAlignment: InitialAlignment.center,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
|
|||||||
Reference in New Issue
Block a user