mod: 无障碍语义适配

This commit is contained in:
orz12
2024-02-29 21:00:53 +08:00
parent 19117a041a
commit f8e6ec00f9
65 changed files with 683 additions and 390 deletions

View File

@@ -10,6 +10,7 @@ class PBadge extends StatelessWidget {
final String? size;
final String? stack;
final double? fs;
final String? semanticsLabel;
const PBadge({
super.key,
@@ -22,6 +23,7 @@ class PBadge extends StatelessWidget {
this.size = 'medium',
this.stack = 'position',
this.fs = 11,
this.semanticsLabel,
});
@override
@@ -68,6 +70,7 @@ class PBadge extends StatelessWidget {
child: Text(
text!,
style: TextStyle(fontSize: fs ?? fontSize, color: color),
semanticsLabel: semanticsLabel,
),
);
if (stack == 'position') {

View File

@@ -1,5 +1,6 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:hive/hive.dart';
import 'package:PiliPalaX/utils/extension.dart';
import 'package:PiliPalaX/utils/global_data.dart';
@@ -20,6 +21,7 @@ class NetworkImgLayer extends StatelessWidget {
// 图片质量 默认1%
this.quality,
this.origAspectRatio,
this.semanticsLabel,
});
final String? src;
@@ -30,6 +32,7 @@ class NetworkImgLayer extends StatelessWidget {
final Duration? fadeInDuration;
final int? quality;
final double? origAspectRatio;
final String? semanticsLabel;
@override
Widget build(BuildContext context) {
@@ -49,8 +52,7 @@ class NetworkImgLayer extends StatelessWidget {
memCacheWidth = width.cacheSize(context);
// memCacheHeight = height.cacheSize(context);
}
return src != '' && src != null
Widget res = src != '' && src != null
? ClipRRect(
clipBehavior: Clip.antiAlias,
borderRadius: BorderRadius.circular(
@@ -79,6 +81,13 @@ class NetworkImgLayer extends StatelessWidget {
),
)
: placeholder(context);
if (semanticsLabel != null) {
return Semantics(
label: semanticsLabel,
child: res,
);
}
return res;
}
Widget placeholder(BuildContext context) {

View File

@@ -41,6 +41,7 @@ class OverlayPop extends StatelessWidget {
borderRadius:
const BorderRadius.all(Radius.circular(20))),
child: IconButton(
tooltip: '关闭',
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),

View File

@@ -31,6 +31,7 @@ class StatDanMu extends StatelessWidget {
fontSize: size == 'medium' ? 12 : 11,
color: color,
),
semanticsLabel: '${Utils.numFormat(danmu!)}条弹幕',
)
],
);

View File

@@ -5,8 +5,9 @@ class StatView extends StatelessWidget {
final String? theme;
final dynamic view;
final String? size;
final String? goto;
const StatView({Key? key, this.theme, this.view, this.size})
const StatView({Key? key, this.theme, this.view, this.size, this.goto})
: super(key: key);
@override
@@ -20,7 +21,9 @@ class StatView extends StatelessWidget {
return Row(
children: [
Icon(
Icons.play_circle_outlined,
goto == 'picture'
? Icons.remove_red_eye_outlined
: Icons.play_circle_outlined,
size: 13,
color: color,
),
@@ -31,6 +34,8 @@ class StatView extends StatelessWidget {
fontSize: size == 'medium' ? 12 : 11,
color: color,
),
semanticsLabel:
'${Utils.numFormat(view!)}${goto == "picture" ? "浏览" : "播放"}',
),
],
);

View File

@@ -38,93 +38,96 @@ class VideoCardH extends StatelessWidget {
final int aid = videoItem.aid;
final String bvid = videoItem.bvid;
final String heroTag = Utils.makeHeroTag(aid);
return GestureDetector(
onLongPress: () {
if (longPress != null) {
longPress!();
}
},
// onLongPressEnd: (details) {
// if (longPressEnd != null) {
// longPressEnd!();
// }
// },
child: InkWell(
onTap: () async {
try {
final int cid =
videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid);
Get.toNamed('/video?bvid=$bvid&cid=$cid',
arguments: {'videoItem': videoItem, 'heroTag': heroTag});
} catch (err) {
SmartDialog.showToast(err.toString());
}
},
child: Padding(
padding: const EdgeInsets.fromLTRB(
StyleString.safeSpace, 5, StyleString.safeSpace, 5),
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints boxConstraints) {
final double width = (boxConstraints.maxWidth -
StyleString.cardSpace *
6 /
MediaQuery.textScalerOf(context).scale(1.0)) /
2;
return Container(
constraints: const BoxConstraints(minHeight: 88),
height: width / StyleString.aspectRatio,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(
builder: (BuildContext context,
BoxConstraints boxConstraints) {
final double maxWidth = boxConstraints.maxWidth;
final double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: videoItem.pic as String,
width: maxWidth,
height: maxHeight,
),
),
PBadge(
text: Utils.timeFormat(videoItem.duration!),
right: 6.0,
bottom: 6.0,
type: 'gray',
),
// if (videoItem.rcmdReason != null &&
// videoItem.rcmdReason.content != '')
// pBadge(videoItem.rcmdReason.content, context,
// 6.0, 6.0, null, null),
],
);
},
),
),
VideoContent(
videoItem: videoItem,
source: source,
showOwner: showOwner,
showView: showView,
showDanmaku: showDanmaku,
showPubdate: showPubdate,
)
],
),
);
return Semantics(
label: Utils.videoItemSemantics(videoItem),
excludeSemantics: true,
child: GestureDetector(
onLongPress: () {
if (longPress != null) {
longPress!();
}
},
// onLongPressEnd: (details) {
// if (longPressEnd != null) {
// longPressEnd!();
// }
// },
child: InkWell(
onTap: () async {
try {
final int cid = videoItem.cid ??
await SearchHttp.ab2c(aid: aid, bvid: bvid);
Get.toNamed('/video?bvid=$bvid&cid=$cid',
arguments: {'videoItem': videoItem, 'heroTag': heroTag});
} catch (err) {
SmartDialog.showToast(err.toString());
}
},
child: Padding(
padding: const EdgeInsets.fromLTRB(
StyleString.safeSpace, 5, StyleString.safeSpace, 5),
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints boxConstraints) {
final double width = (boxConstraints.maxWidth -
StyleString.cardSpace *
6 /
MediaQuery.textScalerOf(context).scale(1.0)) /
2;
return Container(
constraints: const BoxConstraints(minHeight: 88),
height: width / StyleString.aspectRatio,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(
builder: (BuildContext context,
BoxConstraints boxConstraints) {
final double maxWidth = boxConstraints.maxWidth;
final double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: videoItem.pic as String,
width: maxWidth,
height: maxHeight,
),
),
PBadge(
text: Utils.timeFormat(videoItem.duration!),
right: 6.0,
bottom: 6.0,
type: 'gray',
),
// if (videoItem.rcmdReason != null &&
// videoItem.rcmdReason.content != '')
// pBadge(videoItem.rcmdReason.content, context,
// 6.0, 6.0, null, null),
],
);
},
),
),
VideoContent(
videoItem: videoItem,
source: source,
showOwner: showOwner,
showView: showView,
showDanmaku: showDanmaku,
showPubdate: showPubdate,
)
],
),
);
},
),
),
),
),
),
);
));
}
}
@@ -185,6 +188,7 @@ class VideoContent extends StatelessWidget {
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
semanticsLabel: i['text'] as String,
),
]
],
@@ -235,7 +239,7 @@ class VideoContent extends StatelessWidget {
if (showDanmaku)
StatDanMu(
theme: 'gray',
danmu: videoItem.stat.danmaku as int,
danmu: videoItem.stat.danmu as int,
),
const Spacer(),
if (source == 'normal')

View File

@@ -124,57 +124,62 @@ class VideoCardV extends StatelessWidget {
@override
Widget build(BuildContext context) {
String heroTag = Utils.makeHeroTag(videoItem.id);
return Card(
elevation: 0,
clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
child: GestureDetector(
onLongPress: () {
if (longPress != null) {
longPress!();
}
},
// onLongPressEnd: (details) {
// if (longPressEnd != null) {
// longPressEnd!();
// }
// },
child: InkWell(
onTap: () async => onPushDetail(heroTag),
child: Column(
children: [
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: videoItem.pic,
width: maxWidth,
height: maxHeight,
),
),
if (videoItem.duration > 0)
PBadge(
bottom: 6,
right: 7,
size: 'small',
type: 'gray',
text: Utils.timeFormat(videoItem.duration),
)
],
);
}),
return Semantics(
label: Utils.videoItemSemantics(videoItem),
excludeSemantics: true,
child: Card(
elevation: 0,
clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
child: GestureDetector(
onLongPress: () {
if (longPress != null) {
longPress!();
}
},
// onLongPressEnd: (details) {
// if (longPressEnd != null) {
// longPressEnd!();
// }
// },
child: InkWell(
onTap: () async => onPushDetail(heroTag),
child: Column(
children: [
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: videoItem.pic,
width: maxWidth,
height: maxHeight,
),
),
if (videoItem.duration > 0)
PBadge(
bottom: 6,
right: 7,
size: 'small',
type: 'gray',
text: Utils.timeFormat(videoItem.duration),
// semanticsLabel:
// '时长${Utils.durationReadFormat(Utils.timeFormat(videoItem.duration))}',
)
],
);
}),
),
VideoContent(videoItem: videoItem)
],
),
VideoContent(videoItem: videoItem)
],
),
),
),
),
)),
);
}
}
@@ -195,6 +200,7 @@ class VideoContent extends StatelessWidget {
children: [
Expanded(
child: Text(videoItem.title,
// semanticsLabel: "${videoItem.title}",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
@@ -248,6 +254,7 @@ class VideoContent extends StatelessWidget {
flex: 1,
child: Text(
videoItem.owner.name,
// semanticsLabel: "Up主${videoItem.owner.name}",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
@@ -290,12 +297,14 @@ class VideoStat extends StatelessWidget {
StatView(
theme: 'gray',
view: videoItem.stat.view,
goto: videoItem.goto,
),
const SizedBox(width: 8),
StatDanMu(
theme: 'gray',
danmu: videoItem.stat.danmu,
),
if (videoItem.goto != 'picture')
StatDanMu(
theme: 'gray',
danmu: videoItem.stat.danmu,
),
if (videoItem is RecVideoItemModel) ...<Widget>[
const Spacer(),
RichText(