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

@@ -50,9 +50,10 @@ class _AboutPageState extends State<AboutPage> {
children: [
ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 150),
child: Image.asset(
child: ExcludeSemantics(
child: Image.asset(
'assets/images/logo/logo_android_2.png',
),
)),
),
ListTile(
title: Text('PiliPalaX',
@@ -65,6 +66,7 @@ class _AboutPageState extends State<AboutPage> {
'使用Flutter开发的哔哩哔哩第三方客户端',
textAlign: TextAlign.center,
style: TextStyle(color: Theme.of(context).colorScheme.outline),
semanticsLabel: '与你一起,发现不一样的世界',
),
),
Obx(
@@ -156,7 +158,7 @@ class _AboutPageState extends State<AboutPage> {
var cleanStatus = await CacheManage().clearCacheAll();
if (cleanStatus) {
getCacheSize();
SmartDialog.showToast('清除成功');
SmartDialog.showToast('清除成功');
}
},
title: const Text('清除缓存'),
@@ -207,7 +209,7 @@ class AboutController extends GetxController {
String buildNumber = currentInfo.buildNumber;
//if is android
if (Platform.isAndroid) {
buildNumber = buildNumber.substring(0,buildNumber.length - 1);
buildNumber = buildNumber.substring(0, buildNumber.length - 1);
}
currentVersion.value = "${currentInfo.version}+$buildNumber";
}
@@ -265,6 +267,7 @@ class AboutController extends GetxController {
),
);
}
// 问题反馈
feedback() {
launchUrl(

View File

@@ -15,6 +15,7 @@ import 'package:PiliPalaX/pages/video/detail/introduction/widgets/action_row_ite
import 'package:PiliPalaX/pages/video/detail/introduction/widgets/fav_panel.dart';
import 'package:PiliPalaX/utils/feed_back.dart';
import '../../../utils/utils.dart';
import 'controller.dart';
import 'widgets/intro_detail.dart';
@@ -192,6 +193,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
src: !widget.loadingStatus
? widget.bangumiDetail!.cover!
: bangumiItem!.cover!,
semanticsLabel: '封面',
),
if (bangumiItem != null &&
bangumiItem!.rating != null)
@@ -235,6 +237,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
width: 34,
height: 34,
child: IconButton(
tooltip: '收藏',
style: ButtonStyle(
padding: MaterialStateProperty.all(
EdgeInsets.zero),
@@ -394,18 +397,19 @@ class _BangumiInfoState extends State<BangumiInfo> {
crossAxisCount: 5,
childAspectRatio: 1.25,
children: <Widget>[
Obx(
() => ActionItem(
Obx(() => ActionItem(
icon: const Icon(FontAwesomeIcons.thumbsUp),
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
onTap:
handleState(bangumiIntroController.actionLikeVideo),
selectStatus: bangumiIntroController.hasLike.value,
loadingStatus: false,
semanticsLabel: '点赞',
text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['likes']!.toString()
: bangumiItem!.stat!['likes']!.toString()),
),
? Utils.numFormat(
widget.bangumiDetail!.stat!['likes']!)
: Utils.numFormat(bangumiItem!.stat!['likes']!),
)),
Obx(
() => ActionItem(
icon: const Icon(FontAwesomeIcons.b),
@@ -414,9 +418,10 @@ class _BangumiInfoState extends State<BangumiInfo> {
handleState(bangumiIntroController.actionCoinVideo),
selectStatus: bangumiIntroController.hasCoin.value,
loadingStatus: false,
semanticsLabel: '投币',
text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['coins']!.toString()
: bangumiItem!.stat!['coins']!.toString()),
? Utils.numFormat(widget.bangumiDetail!.stat!['coins']!)
: Utils.numFormat(bangumiItem!.stat!['coins']!)),
),
Obx(
() => ActionItem(
@@ -425,9 +430,10 @@ class _BangumiInfoState extends State<BangumiInfo> {
onTap: () => showFavBottomSheet(),
selectStatus: bangumiIntroController.hasFav.value,
loadingStatus: false,
semanticsLabel: '收藏',
text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['favorite']!.toString()
: bangumiItem!.stat!['favorite']!.toString()),
? Utils.numFormat(widget.bangumiDetail!.stat!['favorite']!)
: Utils.numFormat(bangumiItem!.stat!['favorite']!)),
),
ActionItem(
icon: const Icon(FontAwesomeIcons.comment),
@@ -435,18 +441,20 @@ class _BangumiInfoState extends State<BangumiInfo> {
onTap: () => videoDetailCtr.tabCtr.animateTo(1),
selectStatus: false,
loadingStatus: false,
semanticsLabel: '评论',
text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['reply']!.toString()
: bangumiItem!.stat!['reply']!.toString(),
? Utils.numFormat(widget.bangumiDetail!.stat!['reply']!)
: Utils.numFormat(bangumiItem!.stat!['reply']!),
),
ActionItem(
icon: const Icon(FontAwesomeIcons.shareFromSquare),
onTap: () => bangumiIntroController.actionShareVideo(),
selectStatus: false,
loadingStatus: false,
semanticsLabel: '转发',
text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['share']!.toString()
: bangumiItem!.stat!['share']!.toString()),
? Utils.numFormat(widget.bangumiDetail!.stat!['share']!)
: Utils.numFormat(bangumiItem!.stat!['share']!)),
],
),
),

View File

@@ -98,6 +98,7 @@ class _BangumiPageState extends State<BangumiPage>
style: Theme.of(context).textTheme.titleMedium,
),
IconButton(
tooltip: '刷新',
onPressed: () {
setState(() {
_futureBuilderFutureFollow =

View File

@@ -95,6 +95,7 @@ class _BangumiPanelState extends State<BangumiPanel> {
style: Theme.of(context).textTheme.titleMedium,
),
IconButton(
tooltip: '关闭',
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),

View File

@@ -25,12 +25,15 @@ class _ActionPanelState extends State<ActionPanel> {
late ModuleStatModel stat;
bool isProcessing = false;
void Function()? handleState(Future Function() action) {
return isProcessing ? null : () async {
setState(() => isProcessing = true);
await action();
setState(() => isProcessing = false);
};
return isProcessing
? null
: () async {
setState(() => isProcessing = true);
await action();
setState(() => isProcessing = false);
};
}
@override
void initState() {
super.initState();
@@ -83,12 +86,13 @@ class _ActionPanelState extends State<ActionPanel> {
icon: const Icon(
FontAwesomeIcons.shareFromSquare,
size: 16,
semanticLabel: "转发",
),
style: TextButton.styleFrom(
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
foregroundColor: Theme.of(context).colorScheme.outline,
),
label: Text(stat.forward!.count ?? '转发'),
label: Text(stat.forward!.count ?? ''),
),
),
Expanded(
@@ -99,12 +103,13 @@ class _ActionPanelState extends State<ActionPanel> {
icon: const Icon(
FontAwesomeIcons.comment,
size: 16,
semanticLabel: "评论",
),
style: TextButton.styleFrom(
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
foregroundColor: Theme.of(context).colorScheme.outline,
),
label: Text(stat.comment!.count ?? '评论'),
label: Text(stat.comment!.count ?? ''),
),
),
Expanded(
@@ -117,6 +122,7 @@ class _ActionPanelState extends State<ActionPanel> {
: FontAwesomeIcons.thumbsUp,
size: 16,
color: stat.like!.status! ? primary : color,
semanticLabel: stat.like!.status! ? "已赞": "点赞",
),
style: TextButton.styleFrom(
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
@@ -128,7 +134,7 @@ class _ActionPanelState extends State<ActionPanel> {
return ScaleTransition(scale: animation, child: child);
},
child: Text(
stat.like!.count ?? '点赞',
stat.like!.count ?? '',
key: ValueKey<String>(stat.like!.count ?? '点赞'),
style: TextStyle(
color: stat.like!.status! ? primary : color,

View File

@@ -48,6 +48,7 @@ class AuthorPanel extends StatelessWidget {
children: [
Text(
item.modules.moduleAuthor.name,
// semanticsLabel: "Up主${item.modules.moduleAuthor.name}",
style: TextStyle(
color: item.modules.moduleAuthor!.vip != null &&
item.modules.moduleAuthor!.vip['status'] > 0
@@ -81,6 +82,7 @@ class AuthorPanel extends StatelessWidget {
width: 32,
height: 32,
child: IconButton(
tooltip: '更多',
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),

View File

@@ -87,6 +87,7 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) {
width: width,
height: width / StyleString.aspectRatio,
src: content.cover,
semanticsLabel: content.title,
),
),
if (content.badge != null && type == 'pgc')
@@ -133,7 +134,7 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) {
const SizedBox(width: 10),
Text(content.stat.play + '次围观'),
const SizedBox(width: 10),
Text(content.stat.danmaku + '条弹幕')
Text(content.stat.danmu + '条弹幕')
],
),
),

View File

@@ -78,10 +78,11 @@ class _EmotePanelState extends State<EmotePanel>
overflow: TextOverflow.clip,
maxLines: 1,
)
: Image.network(
e.emote![index].url!,
: NetworkImgLayer(
src: e.emote![index].url!,
width: size * 38,
height: size * 38,
semanticsLabel: e.emote![index].text!,
),
),
),

View File

@@ -49,6 +49,7 @@ class _FavPageState extends State<FavPage> {
onPressed: () => Get.toNamed(
'/favSearch?searchType=1&mediaId=${_favController.favFolderData.value.list!.first.id}'),
icon: const Icon(Icons.search_outlined),
tooltip: '搜索',
),
const SizedBox(width: 6),
],

View File

@@ -96,6 +96,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
),
actions: [
IconButton(
tooltip: '搜索',
onPressed: () =>
Get.toNamed('/favSearch?searchType=0&mediaId=$mediaId'),
icon: const Icon(Icons.search_outlined),

View File

@@ -209,6 +209,7 @@ class VideoContent extends StatelessWidget {
right: 0,
bottom: -4,
child: IconButton(
tooltip: '取消收藏',
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),

View File

@@ -50,6 +50,7 @@ class _FavSearchPageState extends State<FavSearchPage> {
titleSpacing: 0,
actions: [
IconButton(
tooltip: '搜索',
onPressed: () => _favSearchCtr.submit(),
icon: const Icon(Icons.search_outlined, size: 22)),
const SizedBox(width: 10)
@@ -65,6 +66,7 @@ class _FavSearchPageState extends State<FavSearchPage> {
hintText: _favSearchCtr.hintText,
border: InputBorder.none,
suffixIcon: IconButton(
tooltip: '清空',
icon: Icon(
Icons.clear,
size: 22,

View File

@@ -41,6 +41,7 @@ class _FollowPageState extends State<FollowPage> {
IconButton(
onPressed: () => Get.toNamed('/followSearch?mid=$mid'),
icon: const Icon(Icons.search_outlined),
tooltip: '搜索'
),
PopupMenuButton(
icon: const Icon(Icons.more_vert),

View File

@@ -50,6 +50,7 @@ class _FollowSearchPageState extends State<FollowSearchPage> {
titleSpacing: 0,
actions: [
IconButton(
tooltip: '搜索',
onPressed: reRequest,
icon: const Icon(CupertinoIcons.search, size: 22),
),
@@ -65,6 +66,7 @@ class _FollowSearchPageState extends State<FollowSearchPage> {
hintText: _followSearchController.hintText,
border: InputBorder.none,
suffixIcon: IconButton(
tooltip: '清空',
icon: Icon(
Icons.clear,
size: 22,

View File

@@ -76,6 +76,7 @@ class _HistoryPageState extends State<HistoryPage> {
),
actions: [
IconButton(
tooltip: '搜索',
onPressed: () => Get.toNamed('/historySearch'),
icon: const Icon(Icons.search_outlined),
),
@@ -129,6 +130,7 @@ class _HistoryPageState extends State<HistoryPage> {
titleSpacing: 0,
centerTitle: false,
leading: IconButton(
tooltip: '取消',
onPressed: () {
_historyController.enableMultiple.value = false;
for (var item in _historyController.historyList) {

View File

@@ -230,6 +230,7 @@ class HistoryItem extends StatelessWidget {
const Duration(milliseconds: 250),
curve: Curves.easeInOut,
child: IconButton(
tooltip: '取消选择',
style: ButtonStyle(
padding: MaterialStateProperty.all(
EdgeInsets.zero),

View File

@@ -50,6 +50,7 @@ class _HistorySearchPageState extends State<HistorySearchPage> {
titleSpacing: 0,
actions: [
IconButton(
tooltip: '搜索',
onPressed: () => _historySearchCtr.submit(),
icon: const Icon(Icons.search_outlined, size: 22)),
const SizedBox(width: 10)
@@ -65,6 +66,7 @@ class _HistorySearchPageState extends State<HistorySearchPage> {
hintText: _historySearchCtr.hintText,
border: InputBorder.none,
suffixIcon: IconButton(
tooltip: '清空',
icon: Icon(
Icons.clear,
size: 22,

View File

@@ -220,41 +220,46 @@ class UserInfoWidget extends StatelessWidget {
const SizedBox(width: 4),
ClipRect(
child: IconButton(
tooltip: '消息',
onPressed: () => Get.toNamed('/whisper'),
icon: const Icon(Icons.notifications_none),
icon: const Icon(
Icons.notifications_none,
),
),
)
],
const SizedBox(width: 8),
Obx(
() => userLogin.value
? Stack(
children: [
NetworkImgLayer(
type: 'avatar',
width: 34,
height: 34,
src: userFace,
),
Positioned.fill(
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => callback?.call(),
splashColor: Theme.of(context)
.colorScheme
.primaryContainer
.withOpacity(0.3),
borderRadius: const BorderRadius.all(
Radius.circular(50),
),
Semantics(
label: "我的",
child: Obx(
() => userLogin.value
? Stack(
children: [
NetworkImgLayer(
type: 'avatar',
width: 34,
height: 34,
src: userFace,
),
),
Positioned.fill(
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => callback?.call(),
splashColor: Theme.of(context)
.colorScheme
.primaryContainer
.withOpacity(0.3),
borderRadius: const BorderRadius.all(
Radius.circular(50),
),
),
),
)
],
)
],
)
: DefaultUser(callback: () => callback!()),
),
: DefaultUser(callback: () => callback!()),
)),
],
);
}
@@ -270,6 +275,7 @@ class DefaultUser extends StatelessWidget {
width: 38,
height: 38,
child: IconButton(
tooltip: '默认用户头像',
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
backgroundColor: MaterialStateProperty.resolveWith((states) {
@@ -409,6 +415,7 @@ class SearchBar extends StatelessWidget {
Icon(
Icons.search_outlined,
color: colorScheme.onSecondaryContainer,
semanticLabel: '搜索',
),
const SizedBox(width: 10),
Expanded(

View File

@@ -43,6 +43,7 @@ class HomeAppBar extends StatelessWidget {
Hero(
tag: 'searchTag',
child: IconButton(
tooltip: '搜索',
onPressed: () {
Get.toNamed('/search');
},
@@ -72,11 +73,13 @@ class HomeAppBar extends StatelessWidget {
width: 32,
height: 32,
src: userInfo.face,
semanticsLabel: '我的',
),
),
const SizedBox(width: 10),
] else ...[
IconButton(
tooltip: '登录',
onPressed: () => showModalBottomSheet(
context: context,
builder: (_) => const SizedBox(

View File

@@ -136,6 +136,7 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
actions: [
const SizedBox(width: 4),
IconButton(
tooltip: '用内置浏览器打开',
onPressed: () {
Get.toNamed('/webview', parameters: {
'url': url.startsWith('http') ? url : 'https:$url',
@@ -148,6 +149,36 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
PopupMenuButton(
icon: const Icon(Icons.more_vert),
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
PopupMenuItem(
onTap: () => {
_htmlRenderCtr.reqHtml(id),
},
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.refresh, size: 19),
SizedBox(width: 10),
Text('刷新'),
],
),
),
PopupMenuItem(
onTap: () => {
Get.toNamed('/webview', parameters: {
'url': url.startsWith('http') ? url : 'https:$url',
'type': 'url',
'pageTitle': title,
}),
},
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.open_in_new, size: 19),
SizedBox(width: 10),
Text('内置浏览器打开'),
],
),
),
PopupMenuItem(
onTap: () => {
Clipboard.setData(ClipboardData(text: url)),

View File

@@ -89,6 +89,7 @@ class _BottomControlState extends State<BottomControl> {
width: 34,
height: 34,
child: IconButton(
tooltip: '画中画',
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
@@ -114,6 +115,7 @@ class _BottomControlState extends State<BottomControl> {
const SizedBox(width: 4),
],
ComBtn(
tooltip: '全屏切换',
icon: const Icon(
Icons.fullscreen,
size: 20,

View File

@@ -25,6 +25,7 @@ class _LoginPageState extends State<LoginPage> {
leading: Obx(
() => _loginPageCtr.currentIndex.value == 0
? IconButton(
tooltip: '关闭',
onPressed: () async {
_loginPageCtr.mobTextFieldNode.unfocus();
await Future.delayed(const Duration(milliseconds: 200));
@@ -33,6 +34,7 @@ class _LoginPageState extends State<LoginPage> {
icon: const Icon(Icons.close_outlined),
)
: IconButton(
tooltip: '返回',
onPressed: () => _loginPageCtr.previousPage(),
icon: const Icon(Icons.arrow_back),
),
@@ -174,6 +176,7 @@ class _LoginPageState extends State<LoginPage> {
),
const SizedBox(width: 4),
IconButton(
tooltip: '切换至验证码登录',
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.resolveWith(
@@ -265,6 +268,7 @@ class _LoginPageState extends State<LoginPage> {
),
const SizedBox(width: 4),
IconButton(
tooltip: '切换至密码登录',
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.resolveWith(

View File

@@ -149,6 +149,7 @@ class _MediaPageState extends State<MediaPage>
),
),
trailing: IconButton(
tooltip: '刷新',
onPressed: () {
setState(() {
_futureBuilderFuture = mediaController.queryFavFolder();
@@ -189,6 +190,7 @@ class _MediaPageState extends State<MediaPage>
right: 14, bottom: 35),
child: Center(
child: IconButton(
tooltip: '查看更多',
style: ButtonStyle(
padding: MaterialStateProperty.all(
EdgeInsets.zero),

View File

@@ -104,6 +104,7 @@ class _MemberPageState extends State<MemberPage>
),
actions: [
IconButton(
tooltip: '搜索',
onPressed: () => Get.toNamed(
'/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'),
icon: const Icon(Icons.search_outlined),
@@ -310,17 +311,20 @@ class _MemberPageState extends State<MemberPage>
FontAwesomeIcons.venus,
size: 14,
color: Colors.pink,
semanticLabel: _memberController.memberInfo.value.sex,
),
if (_memberController.memberInfo.value.sex == '')
const Icon(
FontAwesomeIcons.mars,
size: 14,
color: Colors.blue,
semanticLabel: _memberController.memberInfo.value.sex,
),
const SizedBox(width: 4),
Image.asset(
'assets/images/lv/lv${_memberController.memberInfo.value.level}.png',
height: 11,
semanticLabel: '等级${_memberController.memberInfo.value.level}',
),
const SizedBox(width: 6),
if (_memberController
@@ -333,6 +337,7 @@ class _MemberPageState extends State<MemberPage>
_memberController.memberInfo.value.vip!
.label!['img_label_uri_hans'],
height: 20,
semanticLabel: _memberController.memberInfo.value.vip!.label!['text'],
),
] else if (_memberController
.memberInfo.value.vip!.status ==
@@ -344,6 +349,7 @@ class _MemberPageState extends State<MemberPage>
_memberController.memberInfo.value.vip!
.label!['img_label_uri_hans_static'],
height: 20,
semanticLabel: _memberController.memberInfo.value.vip!.label!['text'],
),
]
],

View File

@@ -147,28 +147,30 @@ class ProfilePanel extends StatelessWidget {
],
),
),
Column(
children: [
Text(
!loadingStatus
? ctr.userStat!['likes'] != null
? Utils.numFormat(
ctr.userStat!['likes'],
)
: '-'
: '-',
style: const TextStyle(
fontWeight: FontWeight.bold)),
Text(
'获赞',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
)
],
),
InkWell(
onTap: null,
child: Column(
children: [
Text(
!loadingStatus
? ctr.userStat!['likes'] != null
? Utils.numFormat(
ctr.userStat!['likes'],
)
: '-'
: '-',
style: const TextStyle(
fontWeight: FontWeight.bold)),
Text(
'获赞',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
)
],
)),
],
),
),
@@ -221,8 +223,7 @@ class ProfilePanel extends StatelessWidget {
TextButton(
onPressed: () {
Get.toNamed('/webview', parameters: {
'url':
'https://account.bilibili.com/account/home',
'url': 'https://account.bilibili.com/account/home',
'pageTitle': '编辑资料(建议浏览器打开)',
'type': 'url'
});

View File

@@ -43,6 +43,7 @@ class MemberSeasonsPanel extends StatelessWidget {
width: 35,
height: 35,
child: IconButton(
tooltip: '前往',
onPressed: () => Get.toNamed(
'/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'),
style: ButtonStyle(

View File

@@ -52,6 +52,7 @@ class _MemberSearchPageState extends State<MemberSearchPage>
titleSpacing: 0,
actions: [
IconButton(
tooltip: '搜索',
onPressed: () => _memberSearchCtr.submit(),
icon: const Icon(CupertinoIcons.search, size: 22)),
const SizedBox(width: 10)
@@ -67,6 +68,7 @@ class _MemberSearchPageState extends State<MemberSearchPage>
hintText: _memberSearchCtr.hintText,
border: InputBorder.none,
suffixIcon: IconButton(
tooltip: '清空',
icon: Icon(
Icons.clear,
size: 22,

View File

@@ -44,46 +44,54 @@ class _MinePageState extends State<MinePage> {
toolbarHeight: kTextTabBarHeight + 20,
backgroundColor: Colors.transparent,
centerTitle: false,
title: //logo
Row(
children: [
Image.asset(
'assets/images/logo/logo_android_2.png',
width: 40,
),
const SizedBox(width: 5),
Text(
'PiliPalaX',
style: Theme.of(context).textTheme.titleMedium,
),
],
title: ExcludeSemantics(
child: Row(
children: [
Image.asset(
'assets/images/logo/logo_android_2.png',
width: 40,
),
const SizedBox(width: 5),
Text(
'PiliPalaX',
style: Theme.of(context).textTheme.titleMedium,
),
],
),
),
actions: [
IconButton(
tooltip: "${MineController.anonymity ? '退出' : '进入'}无痕模式",
onPressed: () {
MineController.onChangeAnonymity(context);
setState(() {});
},
icon: Icon(
MineController.anonymity
? Icons.visibility_off
: Icons.visibility,
? CupertinoIcons.checkmark_shield
: CupertinoIcons.shield_slash,
size: 22,
),
),
IconButton(
onPressed: () => mineController.onChangeTheme(),
tooltip:
'切换至${mineController.themeType.value == ThemeType.dark ? '浅色' : '深色'}主题',
onPressed: () {
mineController.onChangeTheme();
setState(() {});
},
icon: Icon(
mineController.themeType.value == ThemeType.dark
? Icons.light_mode
: Icons.mode_night,
? CupertinoIcons.moon
: CupertinoIcons.sun_min,
size: 22,
),
),
IconButton(
tooltip: '设置',
onPressed: () => Get.toNamed('/setting', preventDuplicates: false),
icon: const Icon(
Icons.settings,
CupertinoIcons.gear,
),
),
const SizedBox(width: 10),
@@ -140,6 +148,7 @@ class _MinePageState extends State<MinePage> {
child: _mineController.userInfo.value.face != null
? NetworkImgLayer(
src: _mineController.userInfo.value.face,
semanticsLabel: '头像',
width: 85,
height: 85)
: Image.asset('assets/images/noface.jpeg'),
@@ -159,6 +168,8 @@ class _MinePageState extends State<MinePage> {
Image.asset(
'assets/images/lv/lv${_mineController.userInfo.value.levelInfo != null ? _mineController.userInfo.value.levelInfo!.currentLevel : '0'}.png',
height: 10,
semanticLabel:
'等级:${_mineController.userInfo.value.levelInfo != null ? _mineController.userInfo.value.levelInfo!.currentLevel : '0'}',
),
],
),
@@ -207,6 +218,8 @@ class _MinePageState extends State<MinePage> {
color: Theme.of(context).colorScheme.onPrimary,
fontSize: 12,
),
semanticsLabel:
'当前经验${levelInfo.currentExp!},升级需要${levelInfo.nextExp!}',
),
),
),

View File

@@ -64,7 +64,10 @@ class SSearchController extends GetxController {
void submit() {
// ignore: unrelated_type_equality_checks
if (searchKeyWord == '') {
return;
if (hintText == ''){
return;
}
searchKeyWord.value = hintText;
}
List arr = historyCacheList.where((e) => e != searchKeyWord.value).toList();
arr.insert(0, searchKeyWord.value);

View File

@@ -53,6 +53,7 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
titleSpacing: 0,
actions: [
IconButton(
tooltip: '搜索',
onPressed: () => _searchController.submit(),
icon: const Icon(CupertinoIcons.search, size: 22),
),
@@ -69,6 +70,7 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
hintText: _searchController.hintText,
border: InputBorder.none,
suffixIcon: IconButton(
tooltip: '清空',
icon: Icon(
Icons.clear,
size: 22,

View File

@@ -87,6 +87,7 @@ class SearchVideoPanel extends StatelessWidget {
width: 32,
height: 32,
child: IconButton(
tooltip: '筛选',
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),

View File

@@ -87,7 +87,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
errMsg: snapshot.data['msg'],
btnText: snapshot.data['code'] == -404 ||
snapshot.data['code'] == 62002
? '返回上一页'
? '上一页'
: null,
fn: () => Get.back(),
);
@@ -285,8 +285,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
StatDanMu(
theme: 'gray',
danmu: !loadingStatus
? widget.videoDetail!.stat!.danmaku
: videoItem['stat'].danmaku,
? widget.videoDetail!.stat!.danmu
: videoItem['stat'].danmu,
size: 'medium',
),
const SizedBox(width: 10),
@@ -335,17 +335,19 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
Positioned(
right: 10,
top: 6,
child: GestureDetector(
onTap: () async {
final res =
await videoIntroController.aiConclusion();
if (res['status']) {
showAiBottomSheet();
}
},
child:
Image.asset('assets/images/ai.png', height: 22),
),
child: Semantics(
label: 'AI总结',
child: GestureDetector(
onTap: () async {
final res =
await videoIntroController.aiConclusion();
if (res['status']) {
showAiBottomSheet();
}
},
child: Image.asset('assets/images/ai.png',
height: 22),
)),
)
],
),
@@ -406,11 +408,15 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
fadeOutDuration: Duration.zero,
),
const SizedBox(width: 10),
Text(owner.name,
style: const TextStyle(fontSize: 13)),
Text(
owner.name,
style: const TextStyle(fontSize: 13),
// semanticsLabel: "Up主${owner.name}",
),
const SizedBox(width: 6),
Text(
follower,
semanticsLabel: "粉丝数:$follower",
style: TextStyle(
fontSize: t.textTheme.labelSmall!.fontSize,
color: outline,
@@ -498,8 +504,9 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
onTap: handleState(videoIntroController.actionLikeVideo),
selectStatus: videoIntroController.hasLike.value,
loadingStatus: loadingStatus,
semanticsLabel: '点赞',
text: !loadingStatus
? widget.videoDetail!.stat!.like!.toString()
? Utils.numFormat(widget.videoDetail!.stat!.like!)
: '-'),
),
// ActionItem(
@@ -515,8 +522,9 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
onTap: handleState(videoIntroController.actionCoinVideo),
selectStatus: videoIntroController.hasCoin.value,
loadingStatus: loadingStatus,
semanticsLabel: '投币',
text: !loadingStatus
? widget.videoDetail!.stat!.coin!.toString()
? Utils.numFormat(widget.videoDetail!.stat!.coin!)
: '-'),
),
Obx(
@@ -527,8 +535,9 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
onLongPress: () => showFavBottomSheet(type: 'longPress'),
selectStatus: videoIntroController.hasFav.value,
loadingStatus: loadingStatus,
semanticsLabel: '收藏',
text: !loadingStatus
? widget.videoDetail!.stat!.favorite!.toString()
? Utils.numFormat(widget.videoDetail!.stat!.favorite!)
: '-'),
),
ActionItem(
@@ -536,15 +545,19 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
onTap: () => videoDetailCtr.tabCtr.animateTo(1),
selectStatus: false,
loadingStatus: loadingStatus,
semanticsLabel: '评论',
text: !loadingStatus
? widget.videoDetail!.stat!.reply!.toString()
? Utils.numFormat(widget.videoDetail!.stat!.reply!)
: '评论'),
ActionItem(
icon: const Icon(FontAwesomeIcons.shareFromSquare),
onTap: () => videoIntroController.actionShareVideo(),
selectStatus: false,
loadingStatus: loadingStatus,
text: '分享'),
semanticsLabel: '分享',
text: !loadingStatus
? Utils.numFormat(widget.videoDetail!.stat!.share!)
: '分享'),
],
),
);

View File

@@ -10,6 +10,7 @@ class ActionItem extends StatelessWidget {
final bool? loadingStatus;
final String? text;
final bool selectStatus;
final String semanticsLabel;
const ActionItem({
Key? key,
@@ -20,11 +21,15 @@ class ActionItem extends StatelessWidget {
this.loadingStatus,
this.text,
this.selectStatus = false,
required this.semanticsLabel,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return InkWell(
return Semantics(
label: (text ?? "") + (selectStatus ? "" :"") + semanticsLabel,
child:
InkWell(
onTap: () => {
feedBack(),
onTap!(),
@@ -37,11 +42,15 @@ class ActionItem extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 4),
selectStatus
? Icon(selectIcon!.icon!,
size: 18, color: Theme.of(context).colorScheme.primary)
: Icon(icon!.icon!,
size: 18, color: Theme.of(context).colorScheme.outline),
Icon(
selectStatus
? selectIcon!.icon!
: icon!.icon!,
size: 18,
color: selectStatus
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.outline,
),
const SizedBox(height: 6),
AnimatedOpacity(
opacity: loadingStatus! ? 0 : 1,
@@ -59,11 +68,12 @@ class ActionItem extends StatelessWidget {
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.outline,
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize),
semanticsLabel: "",
),
),
),
],
),
);
));
}
}

View File

@@ -33,6 +33,7 @@ class _FavPanelState extends State<FavPanel> {
centerTitle: false,
elevation: 0,
leading: IconButton(
tooltip: '关闭',
onPressed: () => Get.back(),
icon: const Icon(Icons.close_outlined)),
title:

View File

@@ -61,6 +61,7 @@ class _GroupPanelState extends State<GroupPanel> {
centerTitle: false,
elevation: 0,
leading: IconButton(
tooltip: '关闭',
onPressed: () => Get.back(),
icon: const Icon(Icons.close_outlined)),
title:

View File

@@ -61,7 +61,7 @@ class IntroDetail extends StatelessWidget {
const SizedBox(width: 10),
StatDanMu(
theme: 'gray',
danmu: videoDetail!.stat!.danmaku,
danmu: videoDetail!.stat!.danmu,
size: 'medium',
),
const SizedBox(width: 10),

View File

@@ -115,6 +115,7 @@ class _PagesPanelState extends State<PagesPanel> {
.titleMedium,
),
IconButton(
tooltip: '关闭',
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),

View File

@@ -210,28 +210,30 @@ class ReplyItem extends StatelessWidget {
// title
Container(
margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
child: Text.rich(
style: const TextStyle(height: 1.75),
maxLines:
replyItem!.content!.isText! && replyLevel == '1' ? 3 : 999,
overflow: TextOverflow.ellipsis,
TextSpan(
children: [
if (replyItem!.isTop!)
const WidgetSpan(
alignment: PlaceholderAlignment.top,
child: PBadge(
text: 'TOP',
size: 'small',
stack: 'normal',
type: 'line',
fs: 9,
),
),
buildContent(context, replyItem!, replyReply, null),
],
),
),
child: Semantics(
label: replyItem?.content?.message ?? "",
child: Text.rich(
style: const TextStyle(height: 1.75),
maxLines:
replyItem!.content!.isText! && replyLevel == '1' ? 3 : 999,
overflow: TextOverflow.ellipsis,
TextSpan(
children: [
if (replyItem!.isTop!)
const WidgetSpan(
alignment: PlaceholderAlignment.top,
child: PBadge(
text: 'TOP',
size: 'small',
stack: 'normal',
type: 'line',
fs: 9,
),
),
buildContent(context, replyItem!, replyReply, null),
],
),
)),
),
// 操作区域
bottonAction(context, replyItem!.replyControl),

View File

@@ -76,6 +76,7 @@ class _ZanButtonState extends State<ZanButton> {
: FontAwesomeIcons.thumbsUp,
size: 16,
color: widget.replyItem!.action == 1 ? primary : color,
semanticLabel: widget.replyItem!.action == 1 ? '已赞' : '点赞',
),
const SizedBox(width: 4),
AnimatedSwitcher(

View File

@@ -5,6 +5,7 @@ class ToolbarIconButton extends StatelessWidget {
final Icon icon;
final String toolbarType;
final bool selected;
final String tooltip;
const ToolbarIconButton({
super.key,
@@ -12,6 +13,7 @@ class ToolbarIconButton extends StatelessWidget {
required this.icon,
required this.toolbarType,
required this.selected,
required this.tooltip,
});
@override
@@ -20,6 +22,7 @@ class ToolbarIconButton extends StatelessWidget {
width: 36,
height: 36,
child: IconButton(
tooltip: tooltip,
onPressed: onPressed,
icon: icon,
highlightColor: Theme.of(context).colorScheme.secondaryContainer,

View File

@@ -192,6 +192,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ToolbarIconButton(
tooltip: '输入',
onPressed: () {
if (toolbarType == 'emote') {
setState(() {
@@ -206,6 +207,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
),
const SizedBox(width: 20),
ToolbarIconButton(
tooltip: '表情',
onPressed: () {
if (toolbarType == 'input') {
setState(() {

View File

@@ -85,6 +85,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
children: <Widget>[
const Text('评论详情'),
IconButton(
tooltip: '关闭',
icon: const Icon(Icons.close, size: 20),
onPressed: () {
_videoReplyReplyController.currentPage = 0;

View File

@@ -1027,6 +1027,7 @@ class _HeaderControlState extends State<HeaderControl> {
children: [
// SizedBox(width: MediaQuery.of(context).padding.left,),
ComBtn(
tooltip: '上一页',
icon: const Icon(
FontAwesomeIcons.arrowLeft,
size: 15,
@@ -1048,8 +1049,9 @@ class _HeaderControlState extends State<HeaderControl> {
},
),
SizedBox(width: buttonSpace),
if ((videoIntroController.videoDetail.value.title != null) && (isFullScreen ||
(!isFullScreen && isLandscape && !horizontalScreen))) ...[
if ((videoIntroController.videoDetail.value.title != null) &&
(isFullScreen ||
(!isFullScreen && isLandscape && !horizontalScreen))) ...[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -1090,6 +1092,7 @@ class _HeaderControlState extends State<HeaderControl> {
)
] else ...[
ComBtn(
tooltip: '返回主页',
icon: const Icon(
FontAwesomeIcons.house,
size: 15,
@@ -1118,12 +1121,13 @@ class _HeaderControlState extends State<HeaderControl> {
width: 34,
height: 34,
child: IconButton(
tooltip: '发弹幕',
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
onPressed: () => showShootDanmakuSheet(),
icon: const Icon(
Icons.add_card_outlined,
Icons.add_comment_outlined,
size: 19,
color: Colors.white,
),
@@ -1135,6 +1139,7 @@ class _HeaderControlState extends State<HeaderControl> {
height: 34,
child: Obx(
() => IconButton(
tooltip: "${_.isOpenDanmu.value ? '关闭' : '开启'}弹幕",
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
@@ -1143,8 +1148,8 @@ class _HeaderControlState extends State<HeaderControl> {
},
icon: Icon(
_.isOpenDanmu.value
? Icons.subtitles_outlined
: Icons.subtitles_off_outlined,
? Icons.comment_outlined
: Icons.comments_disabled_outlined,
size: 19,
color: Colors.white,
),
@@ -1157,6 +1162,7 @@ class _HeaderControlState extends State<HeaderControl> {
width: 34,
height: 34,
child: IconButton(
tooltip: '画中画',
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
@@ -1182,6 +1188,7 @@ class _HeaderControlState extends State<HeaderControl> {
SizedBox(width: buttonSpace),
],
ComBtn(
tooltip: '更多设置',
icon: const Icon(
Icons.more_vert_outlined,
size: 18,

View File

@@ -27,6 +27,7 @@ class _WebviewPageState extends State<WebviewPage> {
actions: [
const SizedBox(width: 4),
IconButton(
tooltip: '刷新',
onPressed: () {
_webviewController.controller.reload();
},
@@ -34,6 +35,7 @@ class _WebviewPageState extends State<WebviewPage> {
color: Theme.of(context).colorScheme.primary),
),
IconButton(
tooltip: '用外部浏览器打开',
onPressed: () {
launchUrl(Uri.parse(_webviewController.url));
},

View File

@@ -90,6 +90,7 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
width: 34,
height: 34,
child: IconButton(
tooltip: '返回',
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
backgroundColor: MaterialStateProperty.resolveWith(
@@ -160,7 +161,8 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
reverse: true,
itemBuilder: (_, int i) {
return ChatItem(
item: messageList[i], e_infos: _whisperDetailController.eInfos);
item: messageList[i],
e_infos: _whisperDetailController.eInfos);
},
);
}),
@@ -197,6 +199,7 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
// ),
// ),
IconButton(
tooltip: '表情',
onPressed: () {
// if (toolbarType == 'input') {
// setState(() {
@@ -220,22 +223,25 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
.withOpacity(0.08),
borderRadius: BorderRadius.circular(40.0),
),
child: TextField(
readOnly: true,
style: Theme.of(context).textTheme.titleMedium,
controller: _replyContentController,
autofocus: false,
focusNode: replyContentFocusNode,
decoration: const InputDecoration(
border: InputBorder.none, // 移除默认边框
hintText: '开发中 ...', // 提示文本
contentPadding: EdgeInsets.symmetric(
horizontal: 16.0, vertical: 12.0), // 内边距
),
),
child: Semantics(
label: '私信输入框(开发中)',
child: TextField(
readOnly: true,
style: Theme.of(context).textTheme.titleMedium,
controller: _replyContentController,
autofocus: false,
focusNode: replyContentFocusNode,
decoration: const InputDecoration(
border: InputBorder.none, // 移除默认边框
hintText: '开发中 ...', // 提示文本
contentPadding: EdgeInsets.symmetric(
horizontal: 16.0, vertical: 12.0), // 内边距
),
)),
),
),
IconButton(
tooltip: '发送',
// onPressed: _whisperDetailController.sendMsg,
onPressed: null,
icon: Icon(