feat: 动态页图片、图片预览页无障碍适配

This commit is contained in:
orz12
2024-03-13 20:02:59 +08:00
parent ab2fb39952
commit d3c2a9d8f3
2 changed files with 147 additions and 139 deletions

View File

@@ -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: '动态图片',
), ),
] ]
], ],

View File

@@ -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(