mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
mod: 无障碍语义适配
This commit is contained in:
@@ -10,6 +10,7 @@ class PBadge extends StatelessWidget {
|
|||||||
final String? size;
|
final String? size;
|
||||||
final String? stack;
|
final String? stack;
|
||||||
final double? fs;
|
final double? fs;
|
||||||
|
final String? semanticsLabel;
|
||||||
|
|
||||||
const PBadge({
|
const PBadge({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -22,6 +23,7 @@ class PBadge extends StatelessWidget {
|
|||||||
this.size = 'medium',
|
this.size = 'medium',
|
||||||
this.stack = 'position',
|
this.stack = 'position',
|
||||||
this.fs = 11,
|
this.fs = 11,
|
||||||
|
this.semanticsLabel,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -68,6 +70,7 @@ class PBadge extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
text!,
|
text!,
|
||||||
style: TextStyle(fontSize: fs ?? fontSize, color: color),
|
style: TextStyle(fontSize: fs ?? fontSize, color: color),
|
||||||
|
semanticsLabel: semanticsLabel,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (stack == 'position') {
|
if (stack == 'position') {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:PiliPalaX/utils/extension.dart';
|
import 'package:PiliPalaX/utils/extension.dart';
|
||||||
import 'package:PiliPalaX/utils/global_data.dart';
|
import 'package:PiliPalaX/utils/global_data.dart';
|
||||||
@@ -20,6 +21,7 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
// 图片质量 默认1%
|
// 图片质量 默认1%
|
||||||
this.quality,
|
this.quality,
|
||||||
this.origAspectRatio,
|
this.origAspectRatio,
|
||||||
|
this.semanticsLabel,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String? src;
|
final String? src;
|
||||||
@@ -30,6 +32,7 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
final Duration? fadeInDuration;
|
final Duration? fadeInDuration;
|
||||||
final int? quality;
|
final int? quality;
|
||||||
final double? origAspectRatio;
|
final double? origAspectRatio;
|
||||||
|
final String? semanticsLabel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -49,8 +52,7 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
memCacheWidth = width.cacheSize(context);
|
memCacheWidth = width.cacheSize(context);
|
||||||
// memCacheHeight = height.cacheSize(context);
|
// memCacheHeight = height.cacheSize(context);
|
||||||
}
|
}
|
||||||
|
Widget res = src != '' && src != null
|
||||||
return src != '' && src != null
|
|
||||||
? ClipRRect(
|
? ClipRRect(
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
@@ -79,6 +81,13 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
: placeholder(context);
|
: placeholder(context);
|
||||||
|
if (semanticsLabel != null) {
|
||||||
|
return Semantics(
|
||||||
|
label: semanticsLabel,
|
||||||
|
child: res,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget placeholder(BuildContext context) {
|
Widget placeholder(BuildContext context) {
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class OverlayPop extends StatelessWidget {
|
|||||||
borderRadius:
|
borderRadius:
|
||||||
const BorderRadius.all(Radius.circular(20))),
|
const BorderRadius.all(Radius.circular(20))),
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '关闭',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class StatDanMu extends StatelessWidget {
|
|||||||
fontSize: size == 'medium' ? 12 : 11,
|
fontSize: size == 'medium' ? 12 : 11,
|
||||||
color: color,
|
color: color,
|
||||||
),
|
),
|
||||||
|
semanticsLabel: '${Utils.numFormat(danmu!)}条弹幕',
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ class StatView extends StatelessWidget {
|
|||||||
final String? theme;
|
final String? theme;
|
||||||
final dynamic view;
|
final dynamic view;
|
||||||
final String? size;
|
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);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -20,7 +21,9 @@ class StatView extends StatelessWidget {
|
|||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Icons.play_circle_outlined,
|
goto == 'picture'
|
||||||
|
? Icons.remove_red_eye_outlined
|
||||||
|
: Icons.play_circle_outlined,
|
||||||
size: 13,
|
size: 13,
|
||||||
color: color,
|
color: color,
|
||||||
),
|
),
|
||||||
@@ -31,6 +34,8 @@ class StatView extends StatelessWidget {
|
|||||||
fontSize: size == 'medium' ? 12 : 11,
|
fontSize: size == 'medium' ? 12 : 11,
|
||||||
color: color,
|
color: color,
|
||||||
),
|
),
|
||||||
|
semanticsLabel:
|
||||||
|
'${Utils.numFormat(view!)}次${goto == "picture" ? "浏览" : "播放"}',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -38,93 +38,96 @@ class VideoCardH extends StatelessWidget {
|
|||||||
final int aid = videoItem.aid;
|
final int aid = videoItem.aid;
|
||||||
final String bvid = videoItem.bvid;
|
final String bvid = videoItem.bvid;
|
||||||
final String heroTag = Utils.makeHeroTag(aid);
|
final String heroTag = Utils.makeHeroTag(aid);
|
||||||
return GestureDetector(
|
return Semantics(
|
||||||
onLongPress: () {
|
label: Utils.videoItemSemantics(videoItem),
|
||||||
if (longPress != null) {
|
excludeSemantics: true,
|
||||||
longPress!();
|
child: GestureDetector(
|
||||||
}
|
onLongPress: () {
|
||||||
},
|
if (longPress != null) {
|
||||||
// onLongPressEnd: (details) {
|
longPress!();
|
||||||
// if (longPressEnd != null) {
|
}
|
||||||
// longPressEnd!();
|
},
|
||||||
// }
|
// onLongPressEnd: (details) {
|
||||||
// },
|
// if (longPressEnd != null) {
|
||||||
child: InkWell(
|
// longPressEnd!();
|
||||||
onTap: () async {
|
// }
|
||||||
try {
|
// },
|
||||||
final int cid =
|
child: InkWell(
|
||||||
videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
onTap: () async {
|
||||||
Get.toNamed('/video?bvid=$bvid&cid=$cid',
|
try {
|
||||||
arguments: {'videoItem': videoItem, 'heroTag': heroTag});
|
final int cid = videoItem.cid ??
|
||||||
} catch (err) {
|
await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
||||||
SmartDialog.showToast(err.toString());
|
Get.toNamed('/video?bvid=$bvid&cid=$cid',
|
||||||
}
|
arguments: {'videoItem': videoItem, 'heroTag': heroTag});
|
||||||
},
|
} catch (err) {
|
||||||
child: Padding(
|
SmartDialog.showToast(err.toString());
|
||||||
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,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
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.primary
|
||||||
: Theme.of(context).colorScheme.onSurface,
|
: Theme.of(context).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
|
semanticsLabel: i['text'] as String,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -235,7 +239,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
if (showDanmaku)
|
if (showDanmaku)
|
||||||
StatDanMu(
|
StatDanMu(
|
||||||
theme: 'gray',
|
theme: 'gray',
|
||||||
danmu: videoItem.stat.danmaku as int,
|
danmu: videoItem.stat.danmu as int,
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
if (source == 'normal')
|
if (source == 'normal')
|
||||||
|
|||||||
@@ -124,57 +124,62 @@ class VideoCardV extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
String heroTag = Utils.makeHeroTag(videoItem.id);
|
String heroTag = Utils.makeHeroTag(videoItem.id);
|
||||||
return Card(
|
return Semantics(
|
||||||
elevation: 0,
|
label: Utils.videoItemSemantics(videoItem),
|
||||||
clipBehavior: Clip.hardEdge,
|
excludeSemantics: true,
|
||||||
margin: EdgeInsets.zero,
|
child: Card(
|
||||||
child: GestureDetector(
|
elevation: 0,
|
||||||
onLongPress: () {
|
clipBehavior: Clip.hardEdge,
|
||||||
if (longPress != null) {
|
margin: EdgeInsets.zero,
|
||||||
longPress!();
|
child: GestureDetector(
|
||||||
}
|
onLongPress: () {
|
||||||
},
|
if (longPress != null) {
|
||||||
// onLongPressEnd: (details) {
|
longPress!();
|
||||||
// if (longPressEnd != null) {
|
}
|
||||||
// longPressEnd!();
|
},
|
||||||
// }
|
// onLongPressEnd: (details) {
|
||||||
// },
|
// if (longPressEnd != null) {
|
||||||
child: InkWell(
|
// longPressEnd!();
|
||||||
onTap: () async => onPushDetail(heroTag),
|
// }
|
||||||
child: Column(
|
// },
|
||||||
children: [
|
child: InkWell(
|
||||||
AspectRatio(
|
onTap: () async => onPushDetail(heroTag),
|
||||||
aspectRatio: StyleString.aspectRatio,
|
child: Column(
|
||||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
children: [
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
AspectRatio(
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
aspectRatio: StyleString.aspectRatio,
|
||||||
return Stack(
|
child: LayoutBuilder(builder: (context, boxConstraints) {
|
||||||
children: [
|
double maxWidth = boxConstraints.maxWidth;
|
||||||
Hero(
|
double maxHeight = boxConstraints.maxHeight;
|
||||||
tag: heroTag,
|
return Stack(
|
||||||
child: NetworkImgLayer(
|
children: [
|
||||||
src: videoItem.pic,
|
Hero(
|
||||||
width: maxWidth,
|
tag: heroTag,
|
||||||
height: maxHeight,
|
child: NetworkImgLayer(
|
||||||
),
|
src: videoItem.pic,
|
||||||
),
|
width: maxWidth,
|
||||||
if (videoItem.duration > 0)
|
height: maxHeight,
|
||||||
PBadge(
|
),
|
||||||
bottom: 6,
|
),
|
||||||
right: 7,
|
if (videoItem.duration > 0)
|
||||||
size: 'small',
|
PBadge(
|
||||||
type: 'gray',
|
bottom: 6,
|
||||||
text: Utils.timeFormat(videoItem.duration),
|
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: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(videoItem.title,
|
child: Text(videoItem.title,
|
||||||
|
// semanticsLabel: "${videoItem.title}",
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
@@ -248,6 +254,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
child: Text(
|
child: Text(
|
||||||
videoItem.owner.name,
|
videoItem.owner.name,
|
||||||
|
// semanticsLabel: "Up主:${videoItem.owner.name}",
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -290,12 +297,14 @@ class VideoStat extends StatelessWidget {
|
|||||||
StatView(
|
StatView(
|
||||||
theme: 'gray',
|
theme: 'gray',
|
||||||
view: videoItem.stat.view,
|
view: videoItem.stat.view,
|
||||||
|
goto: videoItem.goto,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
StatDanMu(
|
if (videoItem.goto != 'picture')
|
||||||
theme: 'gray',
|
StatDanMu(
|
||||||
danmu: videoItem.stat.danmu,
|
theme: 'gray',
|
||||||
),
|
danmu: videoItem.stat.danmu,
|
||||||
|
),
|
||||||
if (videoItem is RecVideoItemModel) ...<Widget>[
|
if (videoItem is RecVideoItemModel) ...<Widget>[
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
RichText(
|
RichText(
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ class MyApp extends StatelessWidget {
|
|||||||
// 图片缓存
|
// 图片缓存
|
||||||
// PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20;
|
// PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20;
|
||||||
return GetMaterialApp(
|
return GetMaterialApp(
|
||||||
|
// showSemanticsDebugger: true,
|
||||||
title: 'PiliPalaX',
|
title: 'PiliPalaX',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
// fontFamily: 'HarmonyOS',
|
// fontFamily: 'HarmonyOS',
|
||||||
|
|||||||
@@ -826,15 +826,15 @@ class Like {
|
|||||||
|
|
||||||
class Stat {
|
class Stat {
|
||||||
Stat({
|
Stat({
|
||||||
this.danmaku,
|
this.danmu,
|
||||||
this.play,
|
this.play,
|
||||||
});
|
});
|
||||||
|
|
||||||
String? danmaku;
|
String? danmu;
|
||||||
String? play;
|
String? play;
|
||||||
|
|
||||||
Stat.fromJson(Map<String, dynamic> json) {
|
Stat.fromJson(Map<String, dynamic> json) {
|
||||||
danmaku = json['danmaku'];
|
danmu = json['danmaku'];
|
||||||
play = json['play'];
|
play = json['play'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,15 +134,15 @@ class VListItemModel {
|
|||||||
class Stat {
|
class Stat {
|
||||||
Stat({
|
Stat({
|
||||||
this.view,
|
this.view,
|
||||||
this.danmaku,
|
this.danmu,
|
||||||
});
|
});
|
||||||
|
|
||||||
int? view;
|
int? view;
|
||||||
int? danmaku;
|
int? danmu;
|
||||||
|
|
||||||
Stat.fromJson(Map<String, dynamic> json) {
|
Stat.fromJson(Map<String, dynamic> json) {
|
||||||
view = json["play"];
|
view = json["play"];
|
||||||
danmaku = json['video_review'];
|
danmu = json['video_review'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class Stat {
|
|||||||
Stat({
|
Stat({
|
||||||
this.aid,
|
this.aid,
|
||||||
this.view,
|
this.view,
|
||||||
this.danmaku,
|
this.danmu,
|
||||||
this.reply,
|
this.reply,
|
||||||
this.favorite,
|
this.favorite,
|
||||||
this.coin,
|
this.coin,
|
||||||
@@ -105,7 +105,7 @@ class Stat {
|
|||||||
|
|
||||||
int? aid;
|
int? aid;
|
||||||
int? view;
|
int? view;
|
||||||
int? danmaku;
|
int? danmu;
|
||||||
int? reply;
|
int? reply;
|
||||||
int? favorite;
|
int? favorite;
|
||||||
int? coin;
|
int? coin;
|
||||||
@@ -120,7 +120,7 @@ class Stat {
|
|||||||
Stat.fromJson(Map<String, dynamic> json) {
|
Stat.fromJson(Map<String, dynamic> json) {
|
||||||
aid = json["aid"];
|
aid = json["aid"];
|
||||||
view = json["view"];
|
view = json["view"];
|
||||||
danmaku = json['danmaku'];
|
danmu = json['danmaku'];
|
||||||
reply = json["reply"];
|
reply = json["reply"];
|
||||||
favorite = json["favorite"];
|
favorite = json["favorite"];
|
||||||
coin = json['coin'];
|
coin = json['coin'];
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ class SearchVideoItemModel {
|
|||||||
class Stat {
|
class Stat {
|
||||||
Stat({
|
Stat({
|
||||||
this.view,
|
this.view,
|
||||||
this.danmaku,
|
this.danmu,
|
||||||
this.favorite,
|
this.favorite,
|
||||||
this.reply,
|
this.reply,
|
||||||
this.like,
|
this.like,
|
||||||
@@ -107,7 +107,7 @@ class Stat {
|
|||||||
// 播放量
|
// 播放量
|
||||||
int? view;
|
int? view;
|
||||||
// 弹幕数
|
// 弹幕数
|
||||||
int? danmaku;
|
int? danmu;
|
||||||
// 收藏数
|
// 收藏数
|
||||||
int? favorite;
|
int? favorite;
|
||||||
// 评论数
|
// 评论数
|
||||||
@@ -117,7 +117,7 @@ class Stat {
|
|||||||
|
|
||||||
Stat.fromJson(Map<String, dynamic> json) {
|
Stat.fromJson(Map<String, dynamic> json) {
|
||||||
view = json['play'];
|
view = json['play'];
|
||||||
danmaku = json['danmaku'];
|
danmu = json['danmaku'];
|
||||||
favorite = json['favorite'];
|
favorite = json['favorite'];
|
||||||
reply = json['review'];
|
reply = json['review'];
|
||||||
like = json['like'];
|
like = json['like'];
|
||||||
|
|||||||
@@ -107,14 +107,14 @@ class FavDetailItemData {
|
|||||||
class Stat {
|
class Stat {
|
||||||
Stat({
|
Stat({
|
||||||
this.view,
|
this.view,
|
||||||
this.danmaku,
|
this.danmu,
|
||||||
});
|
});
|
||||||
|
|
||||||
int? view;
|
int? view;
|
||||||
int? danmaku;
|
int? danmu;
|
||||||
|
|
||||||
Stat.fromJson(Map<String, dynamic> json) {
|
Stat.fromJson(Map<String, dynamic> json) {
|
||||||
view = json['play'];
|
view = json['play'];
|
||||||
danmaku = json['danmaku'];
|
danmu = json['danmaku'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -427,7 +427,7 @@ class Part {
|
|||||||
class Stat {
|
class Stat {
|
||||||
int? aid;
|
int? aid;
|
||||||
int? view;
|
int? view;
|
||||||
int? danmaku;
|
int? danmu;
|
||||||
int? reply;
|
int? reply;
|
||||||
int? favorite;
|
int? favorite;
|
||||||
int? coin;
|
int? coin;
|
||||||
@@ -442,7 +442,7 @@ class Stat {
|
|||||||
Stat({
|
Stat({
|
||||||
this.aid,
|
this.aid,
|
||||||
this.view,
|
this.view,
|
||||||
this.danmaku,
|
this.danmu,
|
||||||
this.reply,
|
this.reply,
|
||||||
this.favorite,
|
this.favorite,
|
||||||
this.coin,
|
this.coin,
|
||||||
@@ -462,7 +462,7 @@ class Stat {
|
|||||||
Stat.fromJson(Map<String, dynamic> json) {
|
Stat.fromJson(Map<String, dynamic> json) {
|
||||||
aid = json["aid"];
|
aid = json["aid"];
|
||||||
view = json["view"];
|
view = json["view"];
|
||||||
danmaku = json["danmaku"];
|
danmu = json["danmaku"];
|
||||||
reply = json["reply"];
|
reply = json["reply"];
|
||||||
favorite = json["favorite"];
|
favorite = json["favorite"];
|
||||||
coin = json["coin"];
|
coin = json["coin"];
|
||||||
@@ -480,7 +480,7 @@ class Stat {
|
|||||||
|
|
||||||
data["aid"] = aid;
|
data["aid"] = aid;
|
||||||
data["view"] = view;
|
data["view"] = view;
|
||||||
data["danmaku"] = danmaku;
|
data["danmaku"] = danmu;
|
||||||
data["reply"] = reply;
|
data["reply"] = reply;
|
||||||
data["favorite"] = favorite;
|
data["favorite"] = favorite;
|
||||||
data["coin"] = coin;
|
data["coin"] = coin;
|
||||||
|
|||||||
@@ -50,9 +50,10 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
children: [
|
children: [
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxHeight: 150),
|
constraints: const BoxConstraints(maxHeight: 150),
|
||||||
child: Image.asset(
|
child: ExcludeSemantics(
|
||||||
|
child: Image.asset(
|
||||||
'assets/images/logo/logo_android_2.png',
|
'assets/images/logo/logo_android_2.png',
|
||||||
),
|
)),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text('PiliPalaX',
|
title: Text('PiliPalaX',
|
||||||
@@ -65,6 +66,7 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
'使用Flutter开发的哔哩哔哩第三方客户端',
|
'使用Flutter开发的哔哩哔哩第三方客户端',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
|
semanticsLabel: '与你一起,发现不一样的世界',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Obx(
|
Obx(
|
||||||
@@ -156,7 +158,7 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
var cleanStatus = await CacheManage().clearCacheAll();
|
var cleanStatus = await CacheManage().clearCacheAll();
|
||||||
if (cleanStatus) {
|
if (cleanStatus) {
|
||||||
getCacheSize();
|
getCacheSize();
|
||||||
SmartDialog.showToast('清除成功');
|
SmartDialog.showToast('清除成功');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title: const Text('清除缓存'),
|
title: const Text('清除缓存'),
|
||||||
@@ -207,7 +209,7 @@ class AboutController extends GetxController {
|
|||||||
String buildNumber = currentInfo.buildNumber;
|
String buildNumber = currentInfo.buildNumber;
|
||||||
//if is android
|
//if is android
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
buildNumber = buildNumber.substring(0,buildNumber.length - 1);
|
buildNumber = buildNumber.substring(0, buildNumber.length - 1);
|
||||||
}
|
}
|
||||||
currentVersion.value = "${currentInfo.version}+$buildNumber";
|
currentVersion.value = "${currentInfo.version}+$buildNumber";
|
||||||
}
|
}
|
||||||
@@ -265,6 +267,7 @@ class AboutController extends GetxController {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 问题反馈
|
// 问题反馈
|
||||||
feedback() {
|
feedback() {
|
||||||
launchUrl(
|
launchUrl(
|
||||||
|
|||||||
@@ -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/pages/video/detail/introduction/widgets/fav_panel.dart';
|
||||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||||
|
|
||||||
|
import '../../../utils/utils.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'widgets/intro_detail.dart';
|
import 'widgets/intro_detail.dart';
|
||||||
|
|
||||||
@@ -192,6 +193,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
src: !widget.loadingStatus
|
src: !widget.loadingStatus
|
||||||
? widget.bangumiDetail!.cover!
|
? widget.bangumiDetail!.cover!
|
||||||
: bangumiItem!.cover!,
|
: bangumiItem!.cover!,
|
||||||
|
semanticsLabel: '封面',
|
||||||
),
|
),
|
||||||
if (bangumiItem != null &&
|
if (bangumiItem != null &&
|
||||||
bangumiItem!.rating != null)
|
bangumiItem!.rating != null)
|
||||||
@@ -235,6 +237,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '收藏',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(
|
padding: MaterialStateProperty.all(
|
||||||
EdgeInsets.zero),
|
EdgeInsets.zero),
|
||||||
@@ -394,18 +397,19 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
crossAxisCount: 5,
|
crossAxisCount: 5,
|
||||||
childAspectRatio: 1.25,
|
childAspectRatio: 1.25,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Obx(
|
Obx(() => ActionItem(
|
||||||
() => ActionItem(
|
|
||||||
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
||||||
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
|
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
|
||||||
onTap:
|
onTap:
|
||||||
handleState(bangumiIntroController.actionLikeVideo),
|
handleState(bangumiIntroController.actionLikeVideo),
|
||||||
selectStatus: bangumiIntroController.hasLike.value,
|
selectStatus: bangumiIntroController.hasLike.value,
|
||||||
loadingStatus: false,
|
loadingStatus: false,
|
||||||
|
semanticsLabel: '点赞',
|
||||||
text: !widget.loadingStatus
|
text: !widget.loadingStatus
|
||||||
? widget.bangumiDetail!.stat!['likes']!.toString()
|
? Utils.numFormat(
|
||||||
: bangumiItem!.stat!['likes']!.toString()),
|
widget.bangumiDetail!.stat!['likes']!)
|
||||||
),
|
: Utils.numFormat(bangumiItem!.stat!['likes']!),
|
||||||
|
)),
|
||||||
Obx(
|
Obx(
|
||||||
() => ActionItem(
|
() => ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.b),
|
icon: const Icon(FontAwesomeIcons.b),
|
||||||
@@ -414,9 +418,10 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
handleState(bangumiIntroController.actionCoinVideo),
|
handleState(bangumiIntroController.actionCoinVideo),
|
||||||
selectStatus: bangumiIntroController.hasCoin.value,
|
selectStatus: bangumiIntroController.hasCoin.value,
|
||||||
loadingStatus: false,
|
loadingStatus: false,
|
||||||
|
semanticsLabel: '投币',
|
||||||
text: !widget.loadingStatus
|
text: !widget.loadingStatus
|
||||||
? widget.bangumiDetail!.stat!['coins']!.toString()
|
? Utils.numFormat(widget.bangumiDetail!.stat!['coins']!)
|
||||||
: bangumiItem!.stat!['coins']!.toString()),
|
: Utils.numFormat(bangumiItem!.stat!['coins']!)),
|
||||||
),
|
),
|
||||||
Obx(
|
Obx(
|
||||||
() => ActionItem(
|
() => ActionItem(
|
||||||
@@ -425,9 +430,10 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
onTap: () => showFavBottomSheet(),
|
onTap: () => showFavBottomSheet(),
|
||||||
selectStatus: bangumiIntroController.hasFav.value,
|
selectStatus: bangumiIntroController.hasFav.value,
|
||||||
loadingStatus: false,
|
loadingStatus: false,
|
||||||
|
semanticsLabel: '收藏',
|
||||||
text: !widget.loadingStatus
|
text: !widget.loadingStatus
|
||||||
? widget.bangumiDetail!.stat!['favorite']!.toString()
|
? Utils.numFormat(widget.bangumiDetail!.stat!['favorite']!)
|
||||||
: bangumiItem!.stat!['favorite']!.toString()),
|
: Utils.numFormat(bangumiItem!.stat!['favorite']!)),
|
||||||
),
|
),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.comment),
|
icon: const Icon(FontAwesomeIcons.comment),
|
||||||
@@ -435,18 +441,20 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
onTap: () => videoDetailCtr.tabCtr.animateTo(1),
|
onTap: () => videoDetailCtr.tabCtr.animateTo(1),
|
||||||
selectStatus: false,
|
selectStatus: false,
|
||||||
loadingStatus: false,
|
loadingStatus: false,
|
||||||
|
semanticsLabel: '评论',
|
||||||
text: !widget.loadingStatus
|
text: !widget.loadingStatus
|
||||||
? widget.bangumiDetail!.stat!['reply']!.toString()
|
? Utils.numFormat(widget.bangumiDetail!.stat!['reply']!)
|
||||||
: bangumiItem!.stat!['reply']!.toString(),
|
: Utils.numFormat(bangumiItem!.stat!['reply']!),
|
||||||
),
|
),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
||||||
onTap: () => bangumiIntroController.actionShareVideo(),
|
onTap: () => bangumiIntroController.actionShareVideo(),
|
||||||
selectStatus: false,
|
selectStatus: false,
|
||||||
loadingStatus: false,
|
loadingStatus: false,
|
||||||
|
semanticsLabel: '转发',
|
||||||
text: !widget.loadingStatus
|
text: !widget.loadingStatus
|
||||||
? widget.bangumiDetail!.stat!['share']!.toString()
|
? Utils.numFormat(widget.bangumiDetail!.stat!['share']!)
|
||||||
: bangumiItem!.stat!['share']!.toString()),
|
: Utils.numFormat(bangumiItem!.stat!['share']!)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '刷新',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_futureBuilderFutureFollow =
|
_futureBuilderFutureFollow =
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '关闭',
|
||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -25,12 +25,15 @@ class _ActionPanelState extends State<ActionPanel> {
|
|||||||
late ModuleStatModel stat;
|
late ModuleStatModel stat;
|
||||||
bool isProcessing = false;
|
bool isProcessing = false;
|
||||||
void Function()? handleState(Future Function() action) {
|
void Function()? handleState(Future Function() action) {
|
||||||
return isProcessing ? null : () async {
|
return isProcessing
|
||||||
setState(() => isProcessing = true);
|
? null
|
||||||
await action();
|
: () async {
|
||||||
setState(() => isProcessing = false);
|
setState(() => isProcessing = true);
|
||||||
};
|
await action();
|
||||||
|
setState(() => isProcessing = false);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -83,12 +86,13 @@ class _ActionPanelState extends State<ActionPanel> {
|
|||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
FontAwesomeIcons.shareFromSquare,
|
FontAwesomeIcons.shareFromSquare,
|
||||||
size: 16,
|
size: 16,
|
||||||
|
semanticLabel: "转发",
|
||||||
),
|
),
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
|
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
|
||||||
foregroundColor: Theme.of(context).colorScheme.outline,
|
foregroundColor: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
label: Text(stat.forward!.count ?? '转发'),
|
label: Text(stat.forward!.count ?? ''),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -99,12 +103,13 @@ class _ActionPanelState extends State<ActionPanel> {
|
|||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
FontAwesomeIcons.comment,
|
FontAwesomeIcons.comment,
|
||||||
size: 16,
|
size: 16,
|
||||||
|
semanticLabel: "评论",
|
||||||
),
|
),
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
|
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
|
||||||
foregroundColor: Theme.of(context).colorScheme.outline,
|
foregroundColor: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
label: Text(stat.comment!.count ?? '评论'),
|
label: Text(stat.comment!.count ?? ''),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -117,6 +122,7 @@ class _ActionPanelState extends State<ActionPanel> {
|
|||||||
: FontAwesomeIcons.thumbsUp,
|
: FontAwesomeIcons.thumbsUp,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: stat.like!.status! ? primary : color,
|
color: stat.like!.status! ? primary : color,
|
||||||
|
semanticLabel: stat.like!.status! ? "已赞": "点赞",
|
||||||
),
|
),
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
|
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
|
||||||
@@ -128,7 +134,7 @@ class _ActionPanelState extends State<ActionPanel> {
|
|||||||
return ScaleTransition(scale: animation, child: child);
|
return ScaleTransition(scale: animation, child: child);
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
stat.like!.count ?? '点赞',
|
stat.like!.count ?? '',
|
||||||
key: ValueKey<String>(stat.like!.count ?? '点赞'),
|
key: ValueKey<String>(stat.like!.count ?? '点赞'),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: stat.like!.status! ? primary : color,
|
color: stat.like!.status! ? primary : color,
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ class AuthorPanel extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
item.modules.moduleAuthor.name,
|
item.modules.moduleAuthor.name,
|
||||||
|
// semanticsLabel: "Up主:${item.modules.moduleAuthor.name}",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: item.modules.moduleAuthor!.vip != null &&
|
color: item.modules.moduleAuthor!.vip != null &&
|
||||||
item.modules.moduleAuthor!.vip['status'] > 0
|
item.modules.moduleAuthor!.vip['status'] > 0
|
||||||
@@ -81,6 +82,7 @@ class AuthorPanel extends StatelessWidget {
|
|||||||
width: 32,
|
width: 32,
|
||||||
height: 32,
|
height: 32,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '更多',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) {
|
|||||||
width: width,
|
width: width,
|
||||||
height: width / StyleString.aspectRatio,
|
height: width / StyleString.aspectRatio,
|
||||||
src: content.cover,
|
src: content.cover,
|
||||||
|
semanticsLabel: content.title,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (content.badge != null && type == 'pgc')
|
if (content.badge != null && type == 'pgc')
|
||||||
@@ -133,7 +134,7 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) {
|
|||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Text(content.stat.play + '次围观'),
|
Text(content.stat.play + '次围观'),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Text(content.stat.danmaku + '条弹幕')
|
Text(content.stat.danmu + '条弹幕')
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -78,10 +78,11 @@ class _EmotePanelState extends State<EmotePanel>
|
|||||||
overflow: TextOverflow.clip,
|
overflow: TextOverflow.clip,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
)
|
)
|
||||||
: Image.network(
|
: NetworkImgLayer(
|
||||||
e.emote![index].url!,
|
src: e.emote![index].url!,
|
||||||
width: size * 38,
|
width: size * 38,
|
||||||
height: size * 38,
|
height: size * 38,
|
||||||
|
semanticsLabel: e.emote![index].text!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ class _FavPageState extends State<FavPage> {
|
|||||||
onPressed: () => Get.toNamed(
|
onPressed: () => Get.toNamed(
|
||||||
'/favSearch?searchType=1&mediaId=${_favController.favFolderData.value.list!.first.id}'),
|
'/favSearch?searchType=1&mediaId=${_favController.favFolderData.value.list!.first.id}'),
|
||||||
icon: const Icon(Icons.search_outlined),
|
icon: const Icon(Icons.search_outlined),
|
||||||
|
tooltip: '搜索',
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '搜索',
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
Get.toNamed('/favSearch?searchType=0&mediaId=$mediaId'),
|
Get.toNamed('/favSearch?searchType=0&mediaId=$mediaId'),
|
||||||
icon: const Icon(Icons.search_outlined),
|
icon: const Icon(Icons.search_outlined),
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
right: 0,
|
right: 0,
|
||||||
bottom: -4,
|
bottom: -4,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '取消收藏',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
|||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '搜索',
|
||||||
onPressed: () => _favSearchCtr.submit(),
|
onPressed: () => _favSearchCtr.submit(),
|
||||||
icon: const Icon(Icons.search_outlined, size: 22)),
|
icon: const Icon(Icons.search_outlined, size: 22)),
|
||||||
const SizedBox(width: 10)
|
const SizedBox(width: 10)
|
||||||
@@ -65,6 +66,7 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
|||||||
hintText: _favSearchCtr.hintText,
|
hintText: _favSearchCtr.hintText,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
|
tooltip: '清空',
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.clear,
|
Icons.clear,
|
||||||
size: 22,
|
size: 22,
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => Get.toNamed('/followSearch?mid=$mid'),
|
onPressed: () => Get.toNamed('/followSearch?mid=$mid'),
|
||||||
icon: const Icon(Icons.search_outlined),
|
icon: const Icon(Icons.search_outlined),
|
||||||
|
tooltip: '搜索'
|
||||||
),
|
),
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
icon: const Icon(Icons.more_vert),
|
icon: const Icon(Icons.more_vert),
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ class _FollowSearchPageState extends State<FollowSearchPage> {
|
|||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '搜索',
|
||||||
onPressed: reRequest,
|
onPressed: reRequest,
|
||||||
icon: const Icon(CupertinoIcons.search, size: 22),
|
icon: const Icon(CupertinoIcons.search, size: 22),
|
||||||
),
|
),
|
||||||
@@ -65,6 +66,7 @@ class _FollowSearchPageState extends State<FollowSearchPage> {
|
|||||||
hintText: _followSearchController.hintText,
|
hintText: _followSearchController.hintText,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
|
tooltip: '清空',
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.clear,
|
Icons.clear,
|
||||||
size: 22,
|
size: 22,
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ class _HistoryPageState extends State<HistoryPage> {
|
|||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '搜索',
|
||||||
onPressed: () => Get.toNamed('/historySearch'),
|
onPressed: () => Get.toNamed('/historySearch'),
|
||||||
icon: const Icon(Icons.search_outlined),
|
icon: const Icon(Icons.search_outlined),
|
||||||
),
|
),
|
||||||
@@ -129,6 +130,7 @@ class _HistoryPageState extends State<HistoryPage> {
|
|||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
|
tooltip: '取消',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_historyController.enableMultiple.value = false;
|
_historyController.enableMultiple.value = false;
|
||||||
for (var item in _historyController.historyList) {
|
for (var item in _historyController.historyList) {
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ class HistoryItem extends StatelessWidget {
|
|||||||
const Duration(milliseconds: 250),
|
const Duration(milliseconds: 250),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '取消选择',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(
|
padding: MaterialStateProperty.all(
|
||||||
EdgeInsets.zero),
|
EdgeInsets.zero),
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ class _HistorySearchPageState extends State<HistorySearchPage> {
|
|||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '搜索',
|
||||||
onPressed: () => _historySearchCtr.submit(),
|
onPressed: () => _historySearchCtr.submit(),
|
||||||
icon: const Icon(Icons.search_outlined, size: 22)),
|
icon: const Icon(Icons.search_outlined, size: 22)),
|
||||||
const SizedBox(width: 10)
|
const SizedBox(width: 10)
|
||||||
@@ -65,6 +66,7 @@ class _HistorySearchPageState extends State<HistorySearchPage> {
|
|||||||
hintText: _historySearchCtr.hintText,
|
hintText: _historySearchCtr.hintText,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
|
tooltip: '清空',
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.clear,
|
Icons.clear,
|
||||||
size: 22,
|
size: 22,
|
||||||
|
|||||||
@@ -220,41 +220,46 @@ class UserInfoWidget extends StatelessWidget {
|
|||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
ClipRect(
|
ClipRect(
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '消息',
|
||||||
onPressed: () => Get.toNamed('/whisper'),
|
onPressed: () => Get.toNamed('/whisper'),
|
||||||
icon: const Icon(Icons.notifications_none),
|
icon: const Icon(
|
||||||
|
Icons.notifications_none,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Obx(
|
Semantics(
|
||||||
() => userLogin.value
|
label: "我的",
|
||||||
? Stack(
|
child: Obx(
|
||||||
children: [
|
() => userLogin.value
|
||||||
NetworkImgLayer(
|
? Stack(
|
||||||
type: 'avatar',
|
children: [
|
||||||
width: 34,
|
NetworkImgLayer(
|
||||||
height: 34,
|
type: 'avatar',
|
||||||
src: userFace,
|
width: 34,
|
||||||
),
|
height: 34,
|
||||||
Positioned.fill(
|
src: userFace,
|
||||||
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),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
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,
|
width: 38,
|
||||||
height: 38,
|
height: 38,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '默认用户头像',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
backgroundColor: MaterialStateProperty.resolveWith((states) {
|
backgroundColor: MaterialStateProperty.resolveWith((states) {
|
||||||
@@ -409,6 +415,7 @@ class SearchBar extends StatelessWidget {
|
|||||||
Icon(
|
Icon(
|
||||||
Icons.search_outlined,
|
Icons.search_outlined,
|
||||||
color: colorScheme.onSecondaryContainer,
|
color: colorScheme.onSecondaryContainer,
|
||||||
|
semanticLabel: '搜索',
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ class HomeAppBar extends StatelessWidget {
|
|||||||
Hero(
|
Hero(
|
||||||
tag: 'searchTag',
|
tag: 'searchTag',
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '搜索',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Get.toNamed('/search');
|
Get.toNamed('/search');
|
||||||
},
|
},
|
||||||
@@ -72,11 +73,13 @@ class HomeAppBar extends StatelessWidget {
|
|||||||
width: 32,
|
width: 32,
|
||||||
height: 32,
|
height: 32,
|
||||||
src: userInfo.face,
|
src: userInfo.face,
|
||||||
|
semanticsLabel: '我的',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
] else ...[
|
] else ...[
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '登录',
|
||||||
onPressed: () => showModalBottomSheet(
|
onPressed: () => showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => const SizedBox(
|
builder: (_) => const SizedBox(
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
|||||||
actions: [
|
actions: [
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '用内置浏览器打开',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Get.toNamed('/webview', parameters: {
|
Get.toNamed('/webview', parameters: {
|
||||||
'url': url.startsWith('http') ? url : 'https:$url',
|
'url': url.startsWith('http') ? url : 'https:$url',
|
||||||
@@ -148,6 +149,36 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
|||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
icon: const Icon(Icons.more_vert),
|
icon: const Icon(Icons.more_vert),
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
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(
|
PopupMenuItem(
|
||||||
onTap: () => {
|
onTap: () => {
|
||||||
Clipboard.setData(ClipboardData(text: url)),
|
Clipboard.setData(ClipboardData(text: url)),
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ class _BottomControlState extends State<BottomControl> {
|
|||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '画中画',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
@@ -114,6 +115,7 @@ class _BottomControlState extends State<BottomControl> {
|
|||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
],
|
],
|
||||||
ComBtn(
|
ComBtn(
|
||||||
|
tooltip: '全屏切换',
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.fullscreen,
|
Icons.fullscreen,
|
||||||
size: 20,
|
size: 20,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
leading: Obx(
|
leading: Obx(
|
||||||
() => _loginPageCtr.currentIndex.value == 0
|
() => _loginPageCtr.currentIndex.value == 0
|
||||||
? IconButton(
|
? IconButton(
|
||||||
|
tooltip: '关闭',
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
_loginPageCtr.mobTextFieldNode.unfocus();
|
_loginPageCtr.mobTextFieldNode.unfocus();
|
||||||
await Future.delayed(const Duration(milliseconds: 200));
|
await Future.delayed(const Duration(milliseconds: 200));
|
||||||
@@ -33,6 +34,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
icon: const Icon(Icons.close_outlined),
|
icon: const Icon(Icons.close_outlined),
|
||||||
)
|
)
|
||||||
: IconButton(
|
: IconButton(
|
||||||
|
tooltip: '返回',
|
||||||
onPressed: () => _loginPageCtr.previousPage(),
|
onPressed: () => _loginPageCtr.previousPage(),
|
||||||
icon: const Icon(Icons.arrow_back),
|
icon: const Icon(Icons.arrow_back),
|
||||||
),
|
),
|
||||||
@@ -174,6 +176,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '切换至验证码登录',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
MaterialStateProperty.resolveWith(
|
MaterialStateProperty.resolveWith(
|
||||||
@@ -265,6 +268,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '切换至密码登录',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
MaterialStateProperty.resolveWith(
|
MaterialStateProperty.resolveWith(
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ class _MediaPageState extends State<MediaPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
|
tooltip: '刷新',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_futureBuilderFuture = mediaController.queryFavFolder();
|
_futureBuilderFuture = mediaController.queryFavFolder();
|
||||||
@@ -189,6 +190,7 @@ class _MediaPageState extends State<MediaPage>
|
|||||||
right: 14, bottom: 35),
|
right: 14, bottom: 35),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '查看更多',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(
|
padding: MaterialStateProperty.all(
|
||||||
EdgeInsets.zero),
|
EdgeInsets.zero),
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '搜索',
|
||||||
onPressed: () => Get.toNamed(
|
onPressed: () => Get.toNamed(
|
||||||
'/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'),
|
'/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'),
|
||||||
icon: const Icon(Icons.search_outlined),
|
icon: const Icon(Icons.search_outlined),
|
||||||
@@ -310,17 +311,20 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
FontAwesomeIcons.venus,
|
FontAwesomeIcons.venus,
|
||||||
size: 14,
|
size: 14,
|
||||||
color: Colors.pink,
|
color: Colors.pink,
|
||||||
|
semanticLabel: _memberController.memberInfo.value.sex,
|
||||||
),
|
),
|
||||||
if (_memberController.memberInfo.value.sex == '男')
|
if (_memberController.memberInfo.value.sex == '男')
|
||||||
const Icon(
|
const Icon(
|
||||||
FontAwesomeIcons.mars,
|
FontAwesomeIcons.mars,
|
||||||
size: 14,
|
size: 14,
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
|
semanticLabel: _memberController.memberInfo.value.sex,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Image.asset(
|
Image.asset(
|
||||||
'assets/images/lv/lv${_memberController.memberInfo.value.level}.png',
|
'assets/images/lv/lv${_memberController.memberInfo.value.level}.png',
|
||||||
height: 11,
|
height: 11,
|
||||||
|
semanticLabel: '等级${_memberController.memberInfo.value.level}',
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
if (_memberController
|
if (_memberController
|
||||||
@@ -333,6 +337,7 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
_memberController.memberInfo.value.vip!
|
_memberController.memberInfo.value.vip!
|
||||||
.label!['img_label_uri_hans'],
|
.label!['img_label_uri_hans'],
|
||||||
height: 20,
|
height: 20,
|
||||||
|
semanticLabel: _memberController.memberInfo.value.vip!.label!['text'],
|
||||||
),
|
),
|
||||||
] else if (_memberController
|
] else if (_memberController
|
||||||
.memberInfo.value.vip!.status ==
|
.memberInfo.value.vip!.status ==
|
||||||
@@ -344,6 +349,7 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
_memberController.memberInfo.value.vip!
|
_memberController.memberInfo.value.vip!
|
||||||
.label!['img_label_uri_hans_static'],
|
.label!['img_label_uri_hans_static'],
|
||||||
height: 20,
|
height: 20,
|
||||||
|
semanticLabel: _memberController.memberInfo.value.vip!.label!['text'],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -147,28 +147,30 @@ class ProfilePanel extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Column(
|
InkWell(
|
||||||
children: [
|
onTap: null,
|
||||||
Text(
|
child: Column(
|
||||||
!loadingStatus
|
children: [
|
||||||
? ctr.userStat!['likes'] != null
|
Text(
|
||||||
? Utils.numFormat(
|
!loadingStatus
|
||||||
ctr.userStat!['likes'],
|
? ctr.userStat!['likes'] != null
|
||||||
)
|
? Utils.numFormat(
|
||||||
: '-'
|
ctr.userStat!['likes'],
|
||||||
: '-',
|
)
|
||||||
style: const TextStyle(
|
: '-'
|
||||||
fontWeight: FontWeight.bold)),
|
: '-',
|
||||||
Text(
|
style: const TextStyle(
|
||||||
'获赞',
|
fontWeight: FontWeight.bold)),
|
||||||
style: TextStyle(
|
Text(
|
||||||
fontSize: Theme.of(context)
|
'获赞',
|
||||||
.textTheme
|
style: TextStyle(
|
||||||
.labelMedium!
|
fontSize: Theme.of(context)
|
||||||
.fontSize),
|
.textTheme
|
||||||
)
|
.labelMedium!
|
||||||
],
|
.fontSize),
|
||||||
),
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -221,8 +223,7 @@ class ProfilePanel extends StatelessWidget {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Get.toNamed('/webview', parameters: {
|
Get.toNamed('/webview', parameters: {
|
||||||
'url':
|
'url': 'https://account.bilibili.com/account/home',
|
||||||
'https://account.bilibili.com/account/home',
|
|
||||||
'pageTitle': '编辑资料(建议浏览器打开)',
|
'pageTitle': '编辑资料(建议浏览器打开)',
|
||||||
'type': 'url'
|
'type': 'url'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ class MemberSeasonsPanel extends StatelessWidget {
|
|||||||
width: 35,
|
width: 35,
|
||||||
height: 35,
|
height: 35,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '前往',
|
||||||
onPressed: () => Get.toNamed(
|
onPressed: () => Get.toNamed(
|
||||||
'/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'),
|
'/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'),
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ class _MemberSearchPageState extends State<MemberSearchPage>
|
|||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '搜索',
|
||||||
onPressed: () => _memberSearchCtr.submit(),
|
onPressed: () => _memberSearchCtr.submit(),
|
||||||
icon: const Icon(CupertinoIcons.search, size: 22)),
|
icon: const Icon(CupertinoIcons.search, size: 22)),
|
||||||
const SizedBox(width: 10)
|
const SizedBox(width: 10)
|
||||||
@@ -67,6 +68,7 @@ class _MemberSearchPageState extends State<MemberSearchPage>
|
|||||||
hintText: _memberSearchCtr.hintText,
|
hintText: _memberSearchCtr.hintText,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
|
tooltip: '清空',
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.clear,
|
Icons.clear,
|
||||||
size: 22,
|
size: 22,
|
||||||
|
|||||||
@@ -44,46 +44,54 @@ class _MinePageState extends State<MinePage> {
|
|||||||
toolbarHeight: kTextTabBarHeight + 20,
|
toolbarHeight: kTextTabBarHeight + 20,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
title: //logo
|
title: ExcludeSemantics(
|
||||||
Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Image.asset(
|
Image.asset(
|
||||||
'assets/images/logo/logo_android_2.png',
|
'assets/images/logo/logo_android_2.png',
|
||||||
width: 40,
|
width: 40,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
Text(
|
Text(
|
||||||
'PiliPalaX',
|
'PiliPalaX',
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: "${MineController.anonymity ? '退出' : '进入'}无痕模式",
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
MineController.onChangeAnonymity(context);
|
MineController.onChangeAnonymity(context);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
MineController.anonymity
|
MineController.anonymity
|
||||||
? Icons.visibility_off
|
? CupertinoIcons.checkmark_shield
|
||||||
: Icons.visibility,
|
: CupertinoIcons.shield_slash,
|
||||||
size: 22,
|
size: 22,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => mineController.onChangeTheme(),
|
tooltip:
|
||||||
|
'切换至${mineController.themeType.value == ThemeType.dark ? '浅色' : '深色'}主题',
|
||||||
|
onPressed: () {
|
||||||
|
mineController.onChangeTheme();
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
mineController.themeType.value == ThemeType.dark
|
mineController.themeType.value == ThemeType.dark
|
||||||
? Icons.light_mode
|
? CupertinoIcons.moon
|
||||||
: Icons.mode_night,
|
: CupertinoIcons.sun_min,
|
||||||
size: 22,
|
size: 22,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '设置',
|
||||||
onPressed: () => Get.toNamed('/setting', preventDuplicates: false),
|
onPressed: () => Get.toNamed('/setting', preventDuplicates: false),
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.settings,
|
CupertinoIcons.gear,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
@@ -140,6 +148,7 @@ class _MinePageState extends State<MinePage> {
|
|||||||
child: _mineController.userInfo.value.face != null
|
child: _mineController.userInfo.value.face != null
|
||||||
? NetworkImgLayer(
|
? NetworkImgLayer(
|
||||||
src: _mineController.userInfo.value.face,
|
src: _mineController.userInfo.value.face,
|
||||||
|
semanticsLabel: '头像',
|
||||||
width: 85,
|
width: 85,
|
||||||
height: 85)
|
height: 85)
|
||||||
: Image.asset('assets/images/noface.jpeg'),
|
: Image.asset('assets/images/noface.jpeg'),
|
||||||
@@ -159,6 +168,8 @@ class _MinePageState extends State<MinePage> {
|
|||||||
Image.asset(
|
Image.asset(
|
||||||
'assets/images/lv/lv${_mineController.userInfo.value.levelInfo != null ? _mineController.userInfo.value.levelInfo!.currentLevel : '0'}.png',
|
'assets/images/lv/lv${_mineController.userInfo.value.levelInfo != null ? _mineController.userInfo.value.levelInfo!.currentLevel : '0'}.png',
|
||||||
height: 10,
|
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,
|
color: Theme.of(context).colorScheme.onPrimary,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
),
|
),
|
||||||
|
semanticsLabel:
|
||||||
|
'当前经验${levelInfo.currentExp!},升级需要${levelInfo.nextExp!}',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -64,7 +64,10 @@ class SSearchController extends GetxController {
|
|||||||
void submit() {
|
void submit() {
|
||||||
// ignore: unrelated_type_equality_checks
|
// ignore: unrelated_type_equality_checks
|
||||||
if (searchKeyWord == '') {
|
if (searchKeyWord == '') {
|
||||||
return;
|
if (hintText == ''){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
searchKeyWord.value = hintText;
|
||||||
}
|
}
|
||||||
List arr = historyCacheList.where((e) => e != searchKeyWord.value).toList();
|
List arr = historyCacheList.where((e) => e != searchKeyWord.value).toList();
|
||||||
arr.insert(0, searchKeyWord.value);
|
arr.insert(0, searchKeyWord.value);
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
|
|||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '搜索',
|
||||||
onPressed: () => _searchController.submit(),
|
onPressed: () => _searchController.submit(),
|
||||||
icon: const Icon(CupertinoIcons.search, size: 22),
|
icon: const Icon(CupertinoIcons.search, size: 22),
|
||||||
),
|
),
|
||||||
@@ -69,6 +70,7 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
|
|||||||
hintText: _searchController.hintText,
|
hintText: _searchController.hintText,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
|
tooltip: '清空',
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.clear,
|
Icons.clear,
|
||||||
size: 22,
|
size: 22,
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ class SearchVideoPanel extends StatelessWidget {
|
|||||||
width: 32,
|
width: 32,
|
||||||
height: 32,
|
height: 32,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '筛选',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
|||||||
errMsg: snapshot.data['msg'],
|
errMsg: snapshot.data['msg'],
|
||||||
btnText: snapshot.data['code'] == -404 ||
|
btnText: snapshot.data['code'] == -404 ||
|
||||||
snapshot.data['code'] == 62002
|
snapshot.data['code'] == 62002
|
||||||
? '返回上一页'
|
? '上一页'
|
||||||
: null,
|
: null,
|
||||||
fn: () => Get.back(),
|
fn: () => Get.back(),
|
||||||
);
|
);
|
||||||
@@ -285,8 +285,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
StatDanMu(
|
StatDanMu(
|
||||||
theme: 'gray',
|
theme: 'gray',
|
||||||
danmu: !loadingStatus
|
danmu: !loadingStatus
|
||||||
? widget.videoDetail!.stat!.danmaku
|
? widget.videoDetail!.stat!.danmu
|
||||||
: videoItem['stat'].danmaku,
|
: videoItem['stat'].danmu,
|
||||||
size: 'medium',
|
size: 'medium',
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
@@ -335,17 +335,19 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
Positioned(
|
Positioned(
|
||||||
right: 10,
|
right: 10,
|
||||||
top: 6,
|
top: 6,
|
||||||
child: GestureDetector(
|
child: Semantics(
|
||||||
onTap: () async {
|
label: 'AI总结',
|
||||||
final res =
|
child: GestureDetector(
|
||||||
await videoIntroController.aiConclusion();
|
onTap: () async {
|
||||||
if (res['status']) {
|
final res =
|
||||||
showAiBottomSheet();
|
await videoIntroController.aiConclusion();
|
||||||
}
|
if (res['status']) {
|
||||||
},
|
showAiBottomSheet();
|
||||||
child:
|
}
|
||||||
Image.asset('assets/images/ai.png', height: 22),
|
},
|
||||||
),
|
child: Image.asset('assets/images/ai.png',
|
||||||
|
height: 22),
|
||||||
|
)),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -406,11 +408,15 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
fadeOutDuration: Duration.zero,
|
fadeOutDuration: Duration.zero,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Text(owner.name,
|
Text(
|
||||||
style: const TextStyle(fontSize: 13)),
|
owner.name,
|
||||||
|
style: const TextStyle(fontSize: 13),
|
||||||
|
// semanticsLabel: "Up主:${owner.name}",
|
||||||
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
Text(
|
Text(
|
||||||
follower,
|
follower,
|
||||||
|
semanticsLabel: "粉丝数:$follower",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: t.textTheme.labelSmall!.fontSize,
|
fontSize: t.textTheme.labelSmall!.fontSize,
|
||||||
color: outline,
|
color: outline,
|
||||||
@@ -498,8 +504,9 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
onTap: handleState(videoIntroController.actionLikeVideo),
|
onTap: handleState(videoIntroController.actionLikeVideo),
|
||||||
selectStatus: videoIntroController.hasLike.value,
|
selectStatus: videoIntroController.hasLike.value,
|
||||||
loadingStatus: loadingStatus,
|
loadingStatus: loadingStatus,
|
||||||
|
semanticsLabel: '点赞',
|
||||||
text: !loadingStatus
|
text: !loadingStatus
|
||||||
? widget.videoDetail!.stat!.like!.toString()
|
? Utils.numFormat(widget.videoDetail!.stat!.like!)
|
||||||
: '-'),
|
: '-'),
|
||||||
),
|
),
|
||||||
// ActionItem(
|
// ActionItem(
|
||||||
@@ -515,8 +522,9 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
onTap: handleState(videoIntroController.actionCoinVideo),
|
onTap: handleState(videoIntroController.actionCoinVideo),
|
||||||
selectStatus: videoIntroController.hasCoin.value,
|
selectStatus: videoIntroController.hasCoin.value,
|
||||||
loadingStatus: loadingStatus,
|
loadingStatus: loadingStatus,
|
||||||
|
semanticsLabel: '投币',
|
||||||
text: !loadingStatus
|
text: !loadingStatus
|
||||||
? widget.videoDetail!.stat!.coin!.toString()
|
? Utils.numFormat(widget.videoDetail!.stat!.coin!)
|
||||||
: '-'),
|
: '-'),
|
||||||
),
|
),
|
||||||
Obx(
|
Obx(
|
||||||
@@ -527,8 +535,9 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
onLongPress: () => showFavBottomSheet(type: 'longPress'),
|
onLongPress: () => showFavBottomSheet(type: 'longPress'),
|
||||||
selectStatus: videoIntroController.hasFav.value,
|
selectStatus: videoIntroController.hasFav.value,
|
||||||
loadingStatus: loadingStatus,
|
loadingStatus: loadingStatus,
|
||||||
|
semanticsLabel: '收藏',
|
||||||
text: !loadingStatus
|
text: !loadingStatus
|
||||||
? widget.videoDetail!.stat!.favorite!.toString()
|
? Utils.numFormat(widget.videoDetail!.stat!.favorite!)
|
||||||
: '-'),
|
: '-'),
|
||||||
),
|
),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
@@ -536,15 +545,19 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
onTap: () => videoDetailCtr.tabCtr.animateTo(1),
|
onTap: () => videoDetailCtr.tabCtr.animateTo(1),
|
||||||
selectStatus: false,
|
selectStatus: false,
|
||||||
loadingStatus: loadingStatus,
|
loadingStatus: loadingStatus,
|
||||||
|
semanticsLabel: '评论',
|
||||||
text: !loadingStatus
|
text: !loadingStatus
|
||||||
? widget.videoDetail!.stat!.reply!.toString()
|
? Utils.numFormat(widget.videoDetail!.stat!.reply!)
|
||||||
: '评论'),
|
: '评论'),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
||||||
onTap: () => videoIntroController.actionShareVideo(),
|
onTap: () => videoIntroController.actionShareVideo(),
|
||||||
selectStatus: false,
|
selectStatus: false,
|
||||||
loadingStatus: loadingStatus,
|
loadingStatus: loadingStatus,
|
||||||
text: '分享'),
|
semanticsLabel: '分享',
|
||||||
|
text: !loadingStatus
|
||||||
|
? Utils.numFormat(widget.videoDetail!.stat!.share!)
|
||||||
|
: '分享'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class ActionItem extends StatelessWidget {
|
|||||||
final bool? loadingStatus;
|
final bool? loadingStatus;
|
||||||
final String? text;
|
final String? text;
|
||||||
final bool selectStatus;
|
final bool selectStatus;
|
||||||
|
final String semanticsLabel;
|
||||||
|
|
||||||
const ActionItem({
|
const ActionItem({
|
||||||
Key? key,
|
Key? key,
|
||||||
@@ -20,11 +21,15 @@ class ActionItem extends StatelessWidget {
|
|||||||
this.loadingStatus,
|
this.loadingStatus,
|
||||||
this.text,
|
this.text,
|
||||||
this.selectStatus = false,
|
this.selectStatus = false,
|
||||||
|
required this.semanticsLabel,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return InkWell(
|
return Semantics(
|
||||||
|
label: (text ?? "") + (selectStatus ? "已" :"") + semanticsLabel,
|
||||||
|
child:
|
||||||
|
InkWell(
|
||||||
onTap: () => {
|
onTap: () => {
|
||||||
feedBack(),
|
feedBack(),
|
||||||
onTap!(),
|
onTap!(),
|
||||||
@@ -37,11 +42,15 @@ class ActionItem extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
selectStatus
|
Icon(
|
||||||
? Icon(selectIcon!.icon!,
|
selectStatus
|
||||||
size: 18, color: Theme.of(context).colorScheme.primary)
|
? selectIcon!.icon!
|
||||||
: Icon(icon!.icon!,
|
: icon!.icon!,
|
||||||
size: 18, color: Theme.of(context).colorScheme.outline),
|
size: 18,
|
||||||
|
color: selectStatus
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
AnimatedOpacity(
|
AnimatedOpacity(
|
||||||
opacity: loadingStatus! ? 0 : 1,
|
opacity: loadingStatus! ? 0 : 1,
|
||||||
@@ -59,11 +68,12 @@ class ActionItem extends StatelessWidget {
|
|||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
: Theme.of(context).colorScheme.outline,
|
: Theme.of(context).colorScheme.outline,
|
||||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize),
|
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize),
|
||||||
|
semanticsLabel: "",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class _FavPanelState extends State<FavPanel> {
|
|||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
|
tooltip: '关闭',
|
||||||
onPressed: () => Get.back(),
|
onPressed: () => Get.back(),
|
||||||
icon: const Icon(Icons.close_outlined)),
|
icon: const Icon(Icons.close_outlined)),
|
||||||
title:
|
title:
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ class _GroupPanelState extends State<GroupPanel> {
|
|||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
|
tooltip: '关闭',
|
||||||
onPressed: () => Get.back(),
|
onPressed: () => Get.back(),
|
||||||
icon: const Icon(Icons.close_outlined)),
|
icon: const Icon(Icons.close_outlined)),
|
||||||
title:
|
title:
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class IntroDetail extends StatelessWidget {
|
|||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
StatDanMu(
|
StatDanMu(
|
||||||
theme: 'gray',
|
theme: 'gray',
|
||||||
danmu: videoDetail!.stat!.danmaku,
|
danmu: videoDetail!.stat!.danmu,
|
||||||
size: 'medium',
|
size: 'medium',
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ class _PagesPanelState extends State<PagesPanel> {
|
|||||||
.titleMedium,
|
.titleMedium,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '关闭',
|
||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -210,28 +210,30 @@ class ReplyItem extends StatelessWidget {
|
|||||||
// title
|
// title
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
|
margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
|
||||||
child: Text.rich(
|
child: Semantics(
|
||||||
style: const TextStyle(height: 1.75),
|
label: replyItem?.content?.message ?? "",
|
||||||
maxLines:
|
child: Text.rich(
|
||||||
replyItem!.content!.isText! && replyLevel == '1' ? 3 : 999,
|
style: const TextStyle(height: 1.75),
|
||||||
overflow: TextOverflow.ellipsis,
|
maxLines:
|
||||||
TextSpan(
|
replyItem!.content!.isText! && replyLevel == '1' ? 3 : 999,
|
||||||
children: [
|
overflow: TextOverflow.ellipsis,
|
||||||
if (replyItem!.isTop!)
|
TextSpan(
|
||||||
const WidgetSpan(
|
children: [
|
||||||
alignment: PlaceholderAlignment.top,
|
if (replyItem!.isTop!)
|
||||||
child: PBadge(
|
const WidgetSpan(
|
||||||
text: 'TOP',
|
alignment: PlaceholderAlignment.top,
|
||||||
size: 'small',
|
child: PBadge(
|
||||||
stack: 'normal',
|
text: 'TOP',
|
||||||
type: 'line',
|
size: 'small',
|
||||||
fs: 9,
|
stack: 'normal',
|
||||||
),
|
type: 'line',
|
||||||
),
|
fs: 9,
|
||||||
buildContent(context, replyItem!, replyReply, null),
|
),
|
||||||
],
|
),
|
||||||
),
|
buildContent(context, replyItem!, replyReply, null),
|
||||||
),
|
],
|
||||||
|
),
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
// 操作区域
|
// 操作区域
|
||||||
bottonAction(context, replyItem!.replyControl),
|
bottonAction(context, replyItem!.replyControl),
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ class _ZanButtonState extends State<ZanButton> {
|
|||||||
: FontAwesomeIcons.thumbsUp,
|
: FontAwesomeIcons.thumbsUp,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: widget.replyItem!.action == 1 ? primary : color,
|
color: widget.replyItem!.action == 1 ? primary : color,
|
||||||
|
semanticLabel: widget.replyItem!.action == 1 ? '已赞' : '点赞',
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
AnimatedSwitcher(
|
AnimatedSwitcher(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ class ToolbarIconButton extends StatelessWidget {
|
|||||||
final Icon icon;
|
final Icon icon;
|
||||||
final String toolbarType;
|
final String toolbarType;
|
||||||
final bool selected;
|
final bool selected;
|
||||||
|
final String tooltip;
|
||||||
|
|
||||||
const ToolbarIconButton({
|
const ToolbarIconButton({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -12,6 +13,7 @@ class ToolbarIconButton extends StatelessWidget {
|
|||||||
required this.icon,
|
required this.icon,
|
||||||
required this.toolbarType,
|
required this.toolbarType,
|
||||||
required this.selected,
|
required this.selected,
|
||||||
|
required this.tooltip,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -20,6 +22,7 @@ class ToolbarIconButton extends StatelessWidget {
|
|||||||
width: 36,
|
width: 36,
|
||||||
height: 36,
|
height: 36,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: tooltip,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
icon: icon,
|
icon: icon,
|
||||||
highlightColor: Theme.of(context).colorScheme.secondaryContainer,
|
highlightColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
|||||||
@@ -192,6 +192,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
ToolbarIconButton(
|
ToolbarIconButton(
|
||||||
|
tooltip: '输入',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (toolbarType == 'emote') {
|
if (toolbarType == 'emote') {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -206,6 +207,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 20),
|
const SizedBox(width: 20),
|
||||||
ToolbarIconButton(
|
ToolbarIconButton(
|
||||||
|
tooltip: '表情',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (toolbarType == 'input') {
|
if (toolbarType == 'input') {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Text('评论详情'),
|
const Text('评论详情'),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '关闭',
|
||||||
icon: const Icon(Icons.close, size: 20),
|
icon: const Icon(Icons.close, size: 20),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_videoReplyReplyController.currentPage = 0;
|
_videoReplyReplyController.currentPage = 0;
|
||||||
|
|||||||
@@ -1027,6 +1027,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
children: [
|
children: [
|
||||||
// SizedBox(width: MediaQuery.of(context).padding.left,),
|
// SizedBox(width: MediaQuery.of(context).padding.left,),
|
||||||
ComBtn(
|
ComBtn(
|
||||||
|
tooltip: '上一页',
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
FontAwesomeIcons.arrowLeft,
|
FontAwesomeIcons.arrowLeft,
|
||||||
size: 15,
|
size: 15,
|
||||||
@@ -1048,8 +1049,9 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(width: buttonSpace),
|
SizedBox(width: buttonSpace),
|
||||||
if ((videoIntroController.videoDetail.value.title != null) && (isFullScreen ||
|
if ((videoIntroController.videoDetail.value.title != null) &&
|
||||||
(!isFullScreen && isLandscape && !horizontalScreen))) ...[
|
(isFullScreen ||
|
||||||
|
(!isFullScreen && isLandscape && !horizontalScreen))) ...[
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -1090,6 +1092,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
)
|
)
|
||||||
] else ...[
|
] else ...[
|
||||||
ComBtn(
|
ComBtn(
|
||||||
|
tooltip: '返回主页',
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
FontAwesomeIcons.house,
|
FontAwesomeIcons.house,
|
||||||
size: 15,
|
size: 15,
|
||||||
@@ -1118,12 +1121,13 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '发弹幕',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
onPressed: () => showShootDanmakuSheet(),
|
onPressed: () => showShootDanmakuSheet(),
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.add_card_outlined,
|
Icons.add_comment_outlined,
|
||||||
size: 19,
|
size: 19,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
@@ -1135,6 +1139,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
height: 34,
|
height: 34,
|
||||||
child: Obx(
|
child: Obx(
|
||||||
() => IconButton(
|
() => IconButton(
|
||||||
|
tooltip: "${_.isOpenDanmu.value ? '关闭' : '开启'}弹幕",
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
@@ -1143,8 +1148,8 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
},
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
_.isOpenDanmu.value
|
_.isOpenDanmu.value
|
||||||
? Icons.subtitles_outlined
|
? Icons.comment_outlined
|
||||||
: Icons.subtitles_off_outlined,
|
: Icons.comments_disabled_outlined,
|
||||||
size: 19,
|
size: 19,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
@@ -1157,6 +1162,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '画中画',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
@@ -1182,6 +1188,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
SizedBox(width: buttonSpace),
|
SizedBox(width: buttonSpace),
|
||||||
],
|
],
|
||||||
ComBtn(
|
ComBtn(
|
||||||
|
tooltip: '更多设置',
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.more_vert_outlined,
|
Icons.more_vert_outlined,
|
||||||
size: 18,
|
size: 18,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class _WebviewPageState extends State<WebviewPage> {
|
|||||||
actions: [
|
actions: [
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '刷新',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_webviewController.controller.reload();
|
_webviewController.controller.reload();
|
||||||
},
|
},
|
||||||
@@ -34,6 +35,7 @@ class _WebviewPageState extends State<WebviewPage> {
|
|||||||
color: Theme.of(context).colorScheme.primary),
|
color: Theme.of(context).colorScheme.primary),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '用外部浏览器打开',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
launchUrl(Uri.parse(_webviewController.url));
|
launchUrl(Uri.parse(_webviewController.url));
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
|
|||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '返回',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
backgroundColor: MaterialStateProperty.resolveWith(
|
backgroundColor: MaterialStateProperty.resolveWith(
|
||||||
@@ -160,7 +161,8 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
|
|||||||
reverse: true,
|
reverse: true,
|
||||||
itemBuilder: (_, int i) {
|
itemBuilder: (_, int i) {
|
||||||
return ChatItem(
|
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(
|
IconButton(
|
||||||
|
tooltip: '表情',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// if (toolbarType == 'input') {
|
// if (toolbarType == 'input') {
|
||||||
// setState(() {
|
// setState(() {
|
||||||
@@ -220,22 +223,25 @@ class _WhisperDetailPageState extends State<WhisperDetailPage>
|
|||||||
.withOpacity(0.08),
|
.withOpacity(0.08),
|
||||||
borderRadius: BorderRadius.circular(40.0),
|
borderRadius: BorderRadius.circular(40.0),
|
||||||
),
|
),
|
||||||
child: TextField(
|
child: Semantics(
|
||||||
readOnly: true,
|
label: '私信输入框(开发中)',
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
child: TextField(
|
||||||
controller: _replyContentController,
|
readOnly: true,
|
||||||
autofocus: false,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
focusNode: replyContentFocusNode,
|
controller: _replyContentController,
|
||||||
decoration: const InputDecoration(
|
autofocus: false,
|
||||||
border: InputBorder.none, // 移除默认边框
|
focusNode: replyContentFocusNode,
|
||||||
hintText: '开发中 ...', // 提示文本
|
decoration: const InputDecoration(
|
||||||
contentPadding: EdgeInsets.symmetric(
|
border: InputBorder.none, // 移除默认边框
|
||||||
horizontal: 16.0, vertical: 12.0), // 内边距
|
hintText: '开发中 ...', // 提示文本
|
||||||
),
|
contentPadding: EdgeInsets.symmetric(
|
||||||
),
|
horizontal: 16.0, vertical: 12.0), // 内边距
|
||||||
|
),
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: '发送',
|
||||||
// onPressed: _whisperDetailController.sendMsg,
|
// onPressed: _whisperDetailController.sendMsg,
|
||||||
onPressed: null,
|
onPressed: null,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:nil/nil.dart';
|
import 'package:nil/nil.dart';
|
||||||
import 'package:PiliPalaX/plugin/pl_player/index.dart';
|
import 'package:PiliPalaX/plugin/pl_player/index.dart';
|
||||||
import 'package:PiliPalaX/plugin/pl_player/widgets/play_pause_btn.dart';
|
import 'package:PiliPalaX/plugin/pl_player/widgets/play_pause_btn.dart';
|
||||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||||
|
|
||||||
|
import '../../../common/widgets/audio_video_progress_bar.dart';
|
||||||
|
import '../../../utils/utils.dart';
|
||||||
|
|
||||||
class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
||||||
final PlPlayerController? controller;
|
final PlPlayerController? controller;
|
||||||
final Function? triggerFullScreen;
|
final Function? triggerFullScreen;
|
||||||
@@ -23,7 +28,9 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
);
|
);
|
||||||
|
//阅读器限制
|
||||||
|
Timer? _accessibilityDebounce;
|
||||||
|
double _lastAnnouncedValue = -1;
|
||||||
return Container(
|
return Container(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
height: 90,
|
height: 90,
|
||||||
@@ -41,31 +48,49 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
}
|
}
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 7, right: 7, bottom: 6),
|
padding: const EdgeInsets.only(left: 7, right: 7, bottom: 6),
|
||||||
child: ProgressBar(
|
child: Semantics(
|
||||||
progress: Duration(seconds: value),
|
// label: '${(value / max * 100).round()}%',
|
||||||
buffered: Duration(seconds: buffer),
|
value: '${(value / max * 100).round()}%',
|
||||||
total: Duration(seconds: max),
|
// enabled: false,
|
||||||
progressBarColor: colorTheme,
|
child: ProgressBar(
|
||||||
baseBarColor: Colors.white.withOpacity(0.2),
|
progress: Duration(seconds: value),
|
||||||
bufferedBarColor: colorTheme.withOpacity(0.4),
|
buffered: Duration(seconds: buffer),
|
||||||
timeLabelLocation: TimeLabelLocation.none,
|
total: Duration(seconds: max),
|
||||||
thumbColor: colorTheme,
|
progressBarColor: colorTheme,
|
||||||
barHeight: 3.5,
|
baseBarColor: Colors.white.withOpacity(0.2),
|
||||||
thumbRadius: 7,
|
bufferedBarColor: colorTheme.withOpacity(0.4),
|
||||||
onDragStart: (duration) {
|
timeLabelLocation: TimeLabelLocation.none,
|
||||||
feedBack();
|
thumbColor: colorTheme,
|
||||||
_.onChangedSliderStart();
|
barHeight: 3.5,
|
||||||
},
|
thumbRadius: 7,
|
||||||
onDragUpdate: (duration) {
|
onDragStart: (duration) {
|
||||||
_.onUpdatedSliderProgress(duration.timeStamp);
|
feedBack();
|
||||||
},
|
_.onChangedSliderStart();
|
||||||
onSeek: (duration) {
|
},
|
||||||
_.onChangedSliderEnd();
|
onDragUpdate: (duration) {
|
||||||
_.onChangedSlider(duration.inSeconds.toDouble());
|
double newProgress = duration.timeStamp.inSeconds / max;
|
||||||
_.seekTo(Duration(seconds: duration.inSeconds),
|
if ((newProgress - _lastAnnouncedValue).abs() > 0.02) {
|
||||||
type: 'slider');
|
_accessibilityDebounce?.cancel();
|
||||||
},
|
_accessibilityDebounce =
|
||||||
),
|
Timer(const Duration(milliseconds: 200), () {
|
||||||
|
SemanticsService.announce(
|
||||||
|
"${(newProgress * 100).round()}%",
|
||||||
|
TextDirection.ltr);
|
||||||
|
_lastAnnouncedValue = newProgress;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_.onUpdatedSliderProgress(duration.timeStamp);
|
||||||
|
},
|
||||||
|
onSeek: (duration) {
|
||||||
|
_.onChangedSliderEnd();
|
||||||
|
_.onChangedSlider(duration.inSeconds.toDouble());
|
||||||
|
_.seekTo(Duration(seconds: duration.inSeconds),
|
||||||
|
type: 'slider');
|
||||||
|
SemanticsService.announce(
|
||||||
|
"${(duration.inSeconds / max * 100).round()}%",
|
||||||
|
TextDirection.ltr);
|
||||||
|
},
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -80,25 +105,26 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
// 播放时间
|
// 播放时间
|
||||||
Obx(() {
|
Obx(() {
|
||||||
return Text(
|
return Text(
|
||||||
_.durationSeconds.value >= 3600
|
Utils.timeFormat(_.positionSeconds.value),
|
||||||
? printDurationWithHours(
|
|
||||||
Duration(seconds: _.positionSeconds.value))
|
|
||||||
: printDuration(
|
|
||||||
Duration(seconds: _.positionSeconds.value)),
|
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
|
semanticsLabel:
|
||||||
|
'已播放${Utils.durationReadFormat(Utils.timeFormat(_.positionSeconds.value))}',
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
const Text('/', style: textStyle),
|
const ExcludeSemantics(
|
||||||
|
child: Text(
|
||||||
|
'/',
|
||||||
|
style: textStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
Obx(
|
Obx(
|
||||||
() => Text(
|
() => Text(
|
||||||
_.durationSeconds.value >= 3600
|
Utils.timeFormat(_.durationSeconds.value),
|
||||||
? printDurationWithHours(
|
|
||||||
Duration(seconds: _.durationSeconds.value))
|
|
||||||
: printDuration(
|
|
||||||
Duration(seconds: _.durationSeconds.value)),
|
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
|
semanticsLabel:
|
||||||
|
'共${Utils.durationReadFormat(Utils.timeFormat(_.durationSeconds.value))}',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
@@ -127,6 +153,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
width: 45,
|
width: 45,
|
||||||
height: 30,
|
height: 30,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: '字幕',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
@@ -151,6 +178,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
() => Text(
|
() => Text(
|
||||||
'${_.playbackSpeed}X',
|
'${_.playbackSpeed}X',
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 13),
|
style: const TextStyle(color: Colors.white, fontSize: 13),
|
||||||
|
semanticsLabel: '${_.playbackSpeed}倍速',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -159,16 +187,18 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: 45,
|
width: 45,
|
||||||
height: 30,
|
height: 30,
|
||||||
child: ComBtn(
|
child: Obx(() => ComBtn(
|
||||||
icon: Obx(() => Icon(
|
tooltip: _.isFullScreen.value ? '退出全屏' : '全屏',
|
||||||
|
icon: Icon(
|
||||||
_.isFullScreen.value
|
_.isFullScreen.value
|
||||||
? Icons.fullscreen_exit
|
? Icons.fullscreen_exit
|
||||||
: Icons.fullscreen,
|
: Icons.fullscreen,
|
||||||
size: 19,
|
size: 19,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
)),
|
),
|
||||||
fuc: () => triggerFullScreen!(status: !_.isFullScreen.value),
|
fuc: () =>
|
||||||
),
|
triggerFullScreen!(status: !_.isFullScreen.value),
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ import 'package:flutter/material.dart';
|
|||||||
class ComBtn extends StatelessWidget {
|
class ComBtn extends StatelessWidget {
|
||||||
final Widget? icon;
|
final Widget? icon;
|
||||||
final Function? fuc;
|
final Function? fuc;
|
||||||
|
final String tooltip;
|
||||||
|
|
||||||
const ComBtn({
|
const ComBtn({
|
||||||
this.icon,
|
this.icon,
|
||||||
this.fuc,
|
this.fuc,
|
||||||
|
required this.tooltip,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -16,6 +18,7 @@ class ComBtn extends StatelessWidget {
|
|||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: tooltip,
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ class PlayOrPauseButtonState extends State<PlayOrPauseButton>
|
|||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: widget.controller!.videoPlayerController!.state.playing
|
||||||
|
? '暂停'
|
||||||
|
: '播放',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -45,6 +45,76 @@ class Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String durationReadFormat(String duration) {
|
||||||
|
List<String> durationParts = duration.split(':');
|
||||||
|
|
||||||
|
if (durationParts.length == 3) {
|
||||||
|
if (durationParts[0] != '00') {
|
||||||
|
return '${int.parse(durationParts[0])}小时${durationParts[1]}分钟${durationParts[2]}秒';
|
||||||
|
}
|
||||||
|
durationParts.removeAt(0);
|
||||||
|
}
|
||||||
|
if (durationParts.length == 2) {
|
||||||
|
if (durationParts[0] != '00') {
|
||||||
|
return '${int.parse(durationParts[0])}分钟${durationParts[1]}秒';
|
||||||
|
}
|
||||||
|
durationParts.removeAt(0);
|
||||||
|
}
|
||||||
|
return '${int.parse(durationParts[0])}秒';
|
||||||
|
}
|
||||||
|
|
||||||
|
static String videoItemSemantics(dynamic videoItem) {
|
||||||
|
String semanticsLabel = "";
|
||||||
|
bool emptyStatCheck(dynamic stat) {
|
||||||
|
return stat == null ||
|
||||||
|
stat == '' ||
|
||||||
|
stat == 0 ||
|
||||||
|
stat == '0' ||
|
||||||
|
stat == '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoItem.runtimeType.toString() == "RecVideoItemAppModel") {
|
||||||
|
if (videoItem.goto == 'picture') {
|
||||||
|
semanticsLabel += '动态,';
|
||||||
|
} else if (videoItem.goto == 'bangumi') {
|
||||||
|
semanticsLabel += '番剧,';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
semanticsLabel += '${videoItem.title}';
|
||||||
|
if (!emptyStatCheck(videoItem.stat.view)) {
|
||||||
|
semanticsLabel += ',${Utils.numFormat(videoItem.stat.view)}';
|
||||||
|
semanticsLabel +=
|
||||||
|
(videoItem.runtimeType.toString() == "RecVideoItemAppModel" &&
|
||||||
|
videoItem.goto == 'picture')
|
||||||
|
? '浏览'
|
||||||
|
: '播放';
|
||||||
|
}
|
||||||
|
if (!emptyStatCheck(videoItem.stat.danmu)) {
|
||||||
|
semanticsLabel += ',${Utils.numFormat(videoItem.stat.danmu)}弹幕';
|
||||||
|
}
|
||||||
|
if (videoItem.rcmdReason != null && videoItem.rcmdReason.content != '') {
|
||||||
|
semanticsLabel += ',${videoItem.rcmdReason.content}';
|
||||||
|
}
|
||||||
|
if (!emptyStatCheck(videoItem.duration)) {
|
||||||
|
semanticsLabel +=
|
||||||
|
',时长${Utils.durationReadFormat(Utils.timeFormat(videoItem.duration))}';
|
||||||
|
}
|
||||||
|
if (videoItem.runtimeType.toString() != "RecVideoItemAppModel" &&
|
||||||
|
videoItem.pubdate != null) {
|
||||||
|
semanticsLabel +=
|
||||||
|
',${Utils.dateFormat(videoItem.pubdate!, formatType: 'day')}';
|
||||||
|
}
|
||||||
|
if (videoItem.owner.name != '') {
|
||||||
|
semanticsLabel += ',Up主:${videoItem.owner.name}';
|
||||||
|
}
|
||||||
|
if (videoItem.runtimeType.toString() == "RecVideoItemAppModel" ||
|
||||||
|
videoItem.runtimeType.toString() == "RecVideoItemModel" &&
|
||||||
|
videoItem.isFollowed == 1) {
|
||||||
|
semanticsLabel += ',已关注';
|
||||||
|
}
|
||||||
|
return semanticsLabel;
|
||||||
|
}
|
||||||
|
|
||||||
static String timeFormat(dynamic time) {
|
static String timeFormat(dynamic time) {
|
||||||
// 1小时内
|
// 1小时内
|
||||||
if (time is String && time.contains(':')) {
|
if (time is String && time.contains(':')) {
|
||||||
@@ -214,7 +284,8 @@ class Utils {
|
|||||||
closestNumber = number;
|
closestNumber = number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (_) {} finally {
|
} catch (_) {
|
||||||
|
} finally {
|
||||||
closestNumber ??= numbers.last;
|
closestNumber ??= numbers.last;
|
||||||
}
|
}
|
||||||
return closestNumber;
|
return closestNumber;
|
||||||
@@ -347,9 +418,8 @@ class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static List<int> generateRandomBytes(int minLength, int maxLength) {
|
static List<int> generateRandomBytes(int minLength, int maxLength) {
|
||||||
return List<int>.generate(
|
return List<int>.generate(random.nextInt(maxLength - minLength + 1),
|
||||||
random.nextInt(maxLength-minLength+1), (_) => random.nextInt(0x60) + 0x20
|
(_) => random.nextInt(0x60) + 0x20);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static String base64EncodeRandomString(int minLength, int maxLength) {
|
static String base64EncodeRandomString(int minLength, int maxLength) {
|
||||||
|
|||||||
@@ -97,14 +97,6 @@ packages:
|
|||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.18"
|
version: "0.1.18"
|
||||||
audio_video_progress_bar:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: audio_video_progress_bar
|
|
||||||
sha256: "3384875247cdbea748bd9ae8330631cd06a6cabfcda4945d45c9b406da92bc66"
|
|
||||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.1"
|
|
||||||
auto_orientation:
|
auto_orientation:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -98,8 +98,6 @@ dependencies:
|
|||||||
screen_brightness: ^0.2.2+1
|
screen_brightness: ^0.2.2+1
|
||||||
wakelock_plus: ^1.1.1
|
wakelock_plus: ^1.1.1
|
||||||
universal_platform: ^1.0.0+1
|
universal_platform: ^1.0.0+1
|
||||||
# 进度条
|
|
||||||
audio_video_progress_bar: ^2.0.1
|
|
||||||
auto_orientation: ^2.3.1
|
auto_orientation: ^2.3.1
|
||||||
protobuf: ^3.0.0
|
protobuf: ^3.0.0
|
||||||
animations: ^2.0.8
|
animations: ^2.0.8
|
||||||
|
|||||||
Reference in New Issue
Block a user