diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 94db6539..55667534 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -20,7 +20,22 @@
"android.support.customtabs.action.CustomTabsService" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -56,7 +229,6 @@
android:value="2" />
-
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 5100bf76..183d026e 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1,4 +1,6 @@
PODS:
+ - appscheme (1.0.4):
+ - Flutter
- connectivity_plus (0.0.1):
- Flutter
- ReachabilitySwift
@@ -45,6 +47,7 @@ PODS:
- Flutter
DEPENDENCIES:
+ - appscheme (from `.symlinks/plugins/appscheme/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`)
@@ -71,6 +74,8 @@ SPEC REPOS:
- ReachabilitySwift
EXTERNAL SOURCES:
+ appscheme:
+ :path: ".symlinks/plugins/appscheme/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
device_info_plus:
@@ -111,6 +116,7 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
SPEC CHECKSUMS:
+ appscheme: b1c3f8862331cb20430cf9e0e4af85dbc1572ad8
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 863a8ef8..57e29a51 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -1,62 +1,107 @@
-
- CFBundleDevelopmentRegion
- $(DEVELOPMENT_LANGUAGE)
- CFBundleDisplayName
- PiliPala
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- pilipala
- CFBundlePackageType
- APPL
- CFBundleShortVersionString
- $(FLUTTER_BUILD_NAME)
- CFBundleSignature
- ????
- CFBundleVersion
- $(FLUTTER_BUILD_NUMBER)
- LSRequiresIPhoneOS
-
- UILaunchStoryboardName
- LaunchScreen
- UIMainStoryboardFile
- Main
- UISupportedInterfaceOrientations
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UISupportedInterfaceOrientations~ipad
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationPortraitUpsideDown
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UIViewControllerBasedStatusBarAppearance
-
- CADisableMinimumFrameDurationOnPhone
-
- UIApplicationSupportsIndirectInputEvents
-
- NSPhotoLibraryAddUsageDescription
- 请允许APP保存图片到相册
- NSCameraUsageDescription
- App需要您的同意,才能访问相册
- NSAppleMusicUsageDescription
- App需要您的同意,才能访问媒体资料库
- LSApplicationQueriesSchemes
-
- https
- http
-
-
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ PiliPala
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ PiliPala
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+ CADisableMinimumFrameDurationOnPhone
+
+ UIApplicationSupportsIndirectInputEvents
+
+ NSPhotoLibraryAddUsageDescription
+ 请允许APP保存图片到相册
+ NSCameraUsageDescription
+ App需要您的同意,才能访问相册
+ NSAppleMusicUsageDescription
+ App需要您的同意,才能访问媒体资料库
+ LSApplicationQueriesSchemes
+
+ https
+ http
+
+
+ CFBundleURLTypes
+
+
+ CFBundleURLName
+
+ CFBundleURLSchemes
+
+ http
+ https
+
+ CFBundleURLTypes
+
+
+ CFBundleURLName
+
+ CFBundleURLSchemes
+
+ m.bilibili.com
+ bilibili.com
+ www.bilibili.com
+ bangumi.bilibili.com
+ bilibili.cn
+ www.bilibili.cn
+ bangumi.bilibili.cn
+ bilibili.tv
+ www.bilibili.tv
+ bangumi.bilibili.tv
+ miniapp.bilibili.com
+ live.bilibili.com
+
+
+
+
+
+
+
+ CFBundleURLName
+ bilibili
+ CFBundleURLSchemes
+
+ bilibili
+
+
+
+
diff --git a/lib/common/skeleton/video_card_v.dart b/lib/common/skeleton/video_card_v.dart
index fc247e56..d13eaee3 100644
--- a/lib/common/skeleton/video_card_v.dart
+++ b/lib/common/skeleton/video_card_v.dart
@@ -45,11 +45,6 @@ class VideoCardVSkeleton extends StatelessWidget {
margin: const EdgeInsets.only(bottom: 12),
color: Theme.of(context).colorScheme.onInverseSurface,
),
- Container(
- width: 80,
- height: 12,
- color: Theme.of(context).colorScheme.onInverseSurface,
- ),
],
),
),
diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart
index eb37d0e1..c81c878b 100644
--- a/lib/common/widgets/video_card_v.dart
+++ b/lib/common/widgets/video_card_v.dart
@@ -15,12 +15,14 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
// 视频卡片 - 垂直布局
class VideoCardV extends StatelessWidget {
final dynamic videoItem;
+ final int crossAxisCount;
final Function()? longPress;
final Function()? longPressEnd;
const VideoCardV({
Key? key,
required this.videoItem,
+ required this.crossAxisCount,
this.longPress,
this.longPressEnd,
}) : super(key: key);
@@ -77,7 +79,7 @@ class VideoCardV extends StatelessWidget {
Widget build(BuildContext context) {
String heroTag = Utils.makeHeroTag(videoItem.id);
return Card(
- elevation: 1,
+ elevation: crossAxisCount == 1 ? 0 : 1,
clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
child: GestureDetector(
@@ -100,17 +102,27 @@ class VideoCardV extends StatelessWidget {
child: LayoutBuilder(builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
- return Hero(
- tag: heroTag,
- child: NetworkImgLayer(
- src: videoItem.pic,
- width: maxWidth,
- height: maxHeight,
- ),
+ return Stack(
+ children: [
+ Hero(
+ tag: heroTag,
+ child: NetworkImgLayer(
+ src: videoItem.pic,
+ width: maxWidth,
+ height: maxHeight,
+ ),
+ ),
+ if (crossAxisCount == 1)
+ PBadge(
+ bottom: 10,
+ right: 10,
+ text: videoItem.duration,
+ )
+ ],
);
}),
),
- VideoContent(videoItem: videoItem)
+ VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount)
],
),
),
@@ -121,22 +133,47 @@ class VideoCardV extends StatelessWidget {
class VideoContent extends StatelessWidget {
final dynamic videoItem;
- const VideoContent({Key? key, required this.videoItem}) : super(key: key);
+ final int crossAxisCount;
+ const VideoContent(
+ {Key? key, required this.videoItem, required this.crossAxisCount})
+ : super(key: key);
@override
Widget build(BuildContext context) {
return Expanded(
+ flex: crossAxisCount == 1 ? 0 : 1,
child: Padding(
- padding: const EdgeInsets.fromLTRB(9, 8, 9, 4),
+ padding: crossAxisCount == 1
+ ? const EdgeInsets.fromLTRB(9, 9, 9, 4)
+ : const EdgeInsets.fromLTRB(9, 8, 9, 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Text(
- videoItem.title,
- maxLines: 2,
- overflow: TextOverflow.ellipsis,
+ Row(
+ children: [
+ Expanded(
+ child: Text(
+ videoItem.title,
+ maxLines: 2,
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ if (videoItem.goto == 'av' && crossAxisCount == 1) ...[
+ const SizedBox(width: 10),
+ WatchLater(
+ size: 32,
+ iconSize: 18,
+ callFn: () async {
+ int aid = videoItem.param;
+ var res =
+ await UserHttp.toViewLater(bvid: IdUtils.av2bv(aid));
+ SmartDialog.showToast(res['msg']);
+ },
+ ),
+ ],
+ ],
),
-
+ if (crossAxisCount == 1) const SizedBox(height: 6),
Row(
children: [
if (videoItem.goto == 'bangumi') ...[
@@ -167,6 +204,7 @@ class VideoContent extends StatelessWidget {
)
],
Expanded(
+ flex: crossAxisCount == 1 ? 0 : 1,
child: Text(
videoItem.owner.name,
maxLines: 1,
@@ -177,95 +215,33 @@ class VideoContent extends StatelessWidget {
),
),
),
- if (videoItem.goto == 'av')
- SizedBox(
- width: 24,
- height: 24,
- child: PopupMenuButton(
- padding: EdgeInsets.zero,
- tooltip: '稍后再看',
- icon: Icon(
- Icons.more_vert_outlined,
- color: Theme.of(context).colorScheme.outline,
- size: 14,
- ),
- position: PopupMenuPosition.under,
- // constraints: const BoxConstraints(maxHeight: 35),
- onSelected: (String type) {},
- itemBuilder: (BuildContext context) =>
- >[
- PopupMenuItem(
- onTap: () async {
- int aid = videoItem.param;
- var res = await UserHttp.toViewLater(
- bvid: IdUtils.av2bv(aid));
- SmartDialog.showToast(res['msg']);
- },
- value: 'pause',
- height: 35,
- child: const Row(
- children: [
- Icon(Icons.watch_later_outlined, size: 16),
- SizedBox(width: 6),
- Text('稍后再看', style: TextStyle(fontSize: 13))
- ],
- ),
- ),
- ],
+ if (crossAxisCount == 1) ...[
+ Text(
+ ' • ',
+ style: TextStyle(
+ fontSize:
+ Theme.of(context).textTheme.labelMedium!.fontSize,
+ color: Theme.of(context).colorScheme.outline,
),
),
+ VideoStat(
+ videoItem: videoItem,
+ )
+ ],
+ const Spacer(),
+ if (videoItem.goto == 'av' && crossAxisCount != 1)
+ WatchLater(
+ size: 24,
+ iconSize: 14,
+ callFn: () async {
+ int aid = videoItem.param;
+ var res =
+ await UserHttp.toViewLater(bvid: IdUtils.av2bv(aid));
+ SmartDialog.showToast(res['msg']);
+ },
+ ),
],
),
- // Row(
- // children: [
- // const SizedBox(width: 1),
- // StatView(
- // theme: 'gray',
- // view: videoItem.stat.view,
- // ),
- // const SizedBox(width: 10),
- // StatDanMu(
- // theme: 'gray',
- // danmu: videoItem.stat.danmaku,
- // ),
- // const Spacer(),
- // SizedBox(
- // width: 24,
- // height: 24,
- // child: PopupMenuButton(
- // padding: EdgeInsets.zero,
- // tooltip: '稍后再看',
- // icon: Icon(
- // Icons.more_vert_outlined,
- // color: Theme.of(context).colorScheme.outline,
- // size: 14,
- // ),
- // position: PopupMenuPosition.under,
- // // constraints: const BoxConstraints(maxHeight: 35),
- // onSelected: (String type) {},
- // itemBuilder: (BuildContext context) =>
- // >[
- // PopupMenuItem(
- // onTap: () async {
- // var res =
- // await UserHttp.toViewLater(bvid: videoItem.bvid);
- // SmartDialog.showToast(res['msg']);
- // },
- // value: 'pause',
- // height: 35,
- // child: const Row(
- // children: [
- // Icon(Icons.watch_later_outlined, size: 16),
- // SizedBox(width: 6),
- // Text('稍后再看', style: TextStyle(fontSize: 13))
- // ],
- // ),
- // ),
- // ],
- // ),
- // ),
- // ],
- // ),
],
),
),
@@ -274,53 +250,77 @@ class VideoContent extends StatelessWidget {
}
class VideoStat extends StatelessWidget {
- final int? view;
- final int? danmaku;
- final int? duration;
+ final dynamic videoItem;
- const VideoStat(
- {Key? key,
- required this.view,
- required this.danmaku,
- required this.duration})
- : super(key: key);
+ const VideoStat({
+ Key? key,
+ required this.videoItem,
+ }) : super(key: key);
@override
Widget build(BuildContext context) {
- return Container(
- height: 48,
- padding: const EdgeInsets.only(top: 22, left: 6, right: 6),
- decoration: const BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: [
- Colors.transparent,
- Colors.black54,
- ],
- tileMode: TileMode.mirror,
- ),
- ),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Row(
- children: [
- StatView(
- theme: 'white',
- view: view,
- ),
- const SizedBox(width: 6),
- StatDanMu(
- theme: 'white',
- danmu: danmaku,
- ),
- ],
+ return Row(
+ children: [
+ Text(
+ '${videoItem.stat.view}次观看',
+ style: TextStyle(
+ fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
+ color: Theme.of(context).colorScheme.outline,
+ ),
+ ),
+ Text(
+ ' • ${videoItem.stat.danmu}条弹幕',
+ style: TextStyle(
+ fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
+ color: Theme.of(context).colorScheme.outline,
+ ),
+ ),
+ ],
+ );
+ }
+}
+
+class WatchLater extends StatelessWidget {
+ final double? size;
+ final double? iconSize;
+ final Function? callFn;
+
+ const WatchLater({
+ Key? key,
+ required this.size,
+ required this.iconSize,
+ this.callFn,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ width: size,
+ height: size,
+ child: PopupMenuButton(
+ padding: EdgeInsets.zero,
+ tooltip: '稍后再看',
+ icon: Icon(
+ Icons.more_vert_outlined,
+ color: Theme.of(context).colorScheme.outline,
+ size: iconSize,
+ ),
+ position: PopupMenuPosition.under,
+ // constraints: const BoxConstraints(maxHeight: 35),
+ onSelected: (String type) {},
+ itemBuilder: (BuildContext context) => >[
+ PopupMenuItem(
+ onTap: () => callFn!(),
+ value: 'pause',
+ height: 35,
+ child: const Row(
+ children: [
+ Icon(Icons.watch_later_outlined, size: 16),
+ SizedBox(width: 6),
+ Text('稍后再看', style: TextStyle(fontSize: 13))
+ ],
+ ),
),
- Text(
- Utils.timeFormat(duration!),
- style: const TextStyle(fontSize: 11, color: Colors.white),
- )
],
),
);
diff --git a/lib/main.dart b/lib/main.dart
index 0bb42d79..5040edca 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -13,6 +13,7 @@ import 'package:pilipala/pages/search/index.dart';
import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/router/app_pages.dart';
import 'package:pilipala/pages/main/view.dart';
+import 'package:pilipala/utils/app_scheme.dart';
import 'package:pilipala/utils/data.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc.
@@ -25,9 +26,6 @@ void main() async {
.then((_) async {
await GStrorage.init();
runApp(const MyApp());
- await Request.setCookie();
- await Data.init();
- await GStrorage.lazyInit();
// 小白条、导航栏沉浸
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
@@ -35,6 +33,10 @@ void main() async {
systemNavigationBarDividerColor: Colors.transparent,
statusBarColor: Colors.transparent,
));
+ await Request.setCookie();
+ Data.init();
+ GStrorage.lazyInit();
+ PiliSchame.init();
});
}
diff --git a/lib/pages/live/controller.dart b/lib/pages/live/controller.dart
index 2779659a..8cdf53a7 100644
--- a/lib/pages/live/controller.dart
+++ b/lib/pages/live/controller.dart
@@ -1,17 +1,27 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
+import 'package:hive/hive.dart';
import 'package:pilipala/http/live.dart';
import 'package:pilipala/models/live/item.dart';
+import 'package:pilipala/utils/storage.dart';
class LiveController extends GetxController {
final ScrollController scrollController = ScrollController();
int count = 12;
int _currentPage = 1;
- int crossAxisCount = 2;
+ RxInt crossAxisCount = 2.obs;
RxList liveList = [LiveItemModel()].obs;
bool isLoadingMore = false;
bool flag = false;
OverlayEntry? popupDialog;
+ Box setting = GStrorage.setting;
+
+ @override
+ void onInit() {
+ super.onInit();
+ crossAxisCount.value =
+ setting.get(SettingBoxKey.enableSingleRow, defaultValue: false) ? 1 : 2;
+ }
// 获取推荐
Future queryLiveList(type) async {
diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart
index 1acf0dc7..5e3e68a1 100644
--- a/lib/pages/live/view.dart
+++ b/lib/pages/live/view.dart
@@ -129,14 +129,15 @@ class _LivePageState extends State {
}
Widget contentGrid(ctr, liveList) {
- double maxWidth = Get.size.width;
- int baseWidth = 500;
- int step = 300;
- int crossAxisCount =
- maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2;
- if (maxWidth < 300) {
- crossAxisCount = 1;
- }
+ // double maxWidth = Get.size.width;
+ // int baseWidth = 500;
+ // int step = 300;
+ // int crossAxisCount =
+ // maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2;
+ // if (maxWidth < 300) {
+ // crossAxisCount = 1;
+ // }
+ int crossAxisCount = ctr.crossAxisCount.value;
return SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// 行间距
@@ -147,13 +148,15 @@ class _LivePageState extends State {
crossAxisCount: crossAxisCount,
mainAxisExtent:
Get.size.width / crossAxisCount / StyleString.aspectRatio +
- 68 * MediaQuery.of(context).textScaleFactor,
+ (crossAxisCount == 1 ? 48 : 68) *
+ MediaQuery.of(context).textScaleFactor,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return liveList!.isNotEmpty
? LiveCardV(
liveItem: liveList[index],
+ crossAxisCount: crossAxisCount,
longPress: () {
_liveController.popupDialog =
_createPopupDialog(liveList[index]);
diff --git a/lib/pages/live/widgets/live_item.dart b/lib/pages/live/widgets/live_item.dart
index a8185be7..48a4356e 100644
--- a/lib/pages/live/widgets/live_item.dart
+++ b/lib/pages/live/widgets/live_item.dart
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
-import 'package:pilipala/common/widgets/badge.dart';
import 'package:pilipala/models/live/item.dart';
import 'package:pilipala/utils/utils.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
@@ -9,12 +8,14 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
// 视频卡片 - 垂直布局
class LiveCardV extends StatelessWidget {
final LiveItemModel liveItem;
+ final int crossAxisCount;
final Function()? longPress;
final Function()? longPressEnd;
const LiveCardV({
Key? key,
required this.liveItem,
+ required this.crossAxisCount,
this.longPress,
this.longPressEnd,
}) : super(key: key);
@@ -23,7 +24,7 @@ class LiveCardV extends StatelessWidget {
Widget build(BuildContext context) {
String heroTag = Utils.makeHeroTag(liveItem.roomId);
return Card(
- elevation: 1,
+ elevation: crossAxisCount == 1 ? 0 : 1,
clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
child: GestureDetector(
@@ -45,12 +46,7 @@ class LiveCardV extends StatelessWidget {
child: Column(
children: [
ClipRRect(
- borderRadius: const BorderRadius.only(
- topLeft: StyleString.imgRadius,
- topRight: StyleString.imgRadius,
- bottomLeft: StyleString.imgRadius,
- bottomRight: StyleString.imgRadius,
- ),
+ borderRadius: const BorderRadius.all(StyleString.imgRadius),
child: AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(builder: (context, boxConstraints) {
@@ -66,24 +62,25 @@ class LiveCardV extends StatelessWidget {
height: maxHeight,
),
),
- Positioned(
- left: 0,
- right: 0,
- bottom: 0,
- child: AnimatedOpacity(
- opacity: 1,
- duration: const Duration(milliseconds: 200),
- child: VideoStat(
- liveItem: liveItem,
+ if (crossAxisCount != 1)
+ Positioned(
+ left: 0,
+ right: 0,
+ bottom: 0,
+ child: AnimatedOpacity(
+ opacity: 1,
+ duration: const Duration(milliseconds: 200),
+ child: VideoStat(
+ liveItem: liveItem,
+ ),
),
),
- ),
],
);
}),
),
),
- LiveContent(liveItem: liveItem)
+ LiveContent(liveItem: liveItem, crossAxisCount: crossAxisCount)
],
),
),
@@ -94,13 +91,18 @@ class LiveCardV extends StatelessWidget {
class LiveContent extends StatelessWidget {
final dynamic liveItem;
- const LiveContent({Key? key, required this.liveItem}) : super(key: key);
+ final int crossAxisCount;
+ const LiveContent(
+ {Key? key, required this.liveItem, required this.crossAxisCount})
+ : super(key: key);
@override
Widget build(BuildContext context) {
return Expanded(
+ flex: crossAxisCount == 1 ? 0 : 1,
child: Padding(
- // 多列
- padding: const EdgeInsets.fromLTRB(9, 9, 9, 8),
+ padding: crossAxisCount == 1
+ ? const EdgeInsets.fromLTRB(9, 9, 9, 4)
+ : const EdgeInsets.fromLTRB(9, 8, 9, 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -112,29 +114,40 @@ class LiveContent extends StatelessWidget {
fontWeight: FontWeight.w500,
letterSpacing: 0.3,
),
- maxLines: 2,
+ maxLines: crossAxisCount == 1 ? 1 : 2,
overflow: TextOverflow.ellipsis,
),
+ if (crossAxisCount == 1) const SizedBox(height: 4),
Row(
children: [
- const PBadge(
- text: 'UP',
- size: 'small',
- stack: 'normal',
- fs: 9,
+ Text(
+ liveItem.uname,
+ textAlign: TextAlign.start,
+ style: TextStyle(
+ fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
+ color: Theme.of(context).colorScheme.outline,
+ ),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
),
- Expanded(
- child: Text(
- liveItem.uname,
- textAlign: TextAlign.start,
+ if (crossAxisCount == 1) ...[
+ Text(
+ ' • ${liveItem!.areaName!}',
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
+ color: Theme.of(context).colorScheme.outline,
),
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
),
- )
+ Text(
+ ' • ${liveItem!.watchedShow!['text_small']}人观看',
+ style: TextStyle(
+ fontSize:
+ Theme.of(context).textTheme.labelMedium!.fontSize,
+ color: Theme.of(context).colorScheme.outline,
+ ),
+ ),
+ ]
],
),
],
diff --git a/lib/pages/liveRoom/controller.dart b/lib/pages/liveRoom/controller.dart
index 18b39756..2f489fec 100644
--- a/lib/pages/liveRoom/controller.dart
+++ b/lib/pages/liveRoom/controller.dart
@@ -29,10 +29,10 @@ class LiveRoomController extends GetxController {
if (Get.arguments != null) {
liveItem = Get.arguments['liveItem'];
heroTag = Get.arguments['heroTag'] ?? '';
- if (liveItem.pic != null && liveItem.pic != '') {
+ if (liveItem != null && liveItem.pic != null && liveItem.pic != '') {
cover = liveItem.pic;
}
- if (liveItem.cover != null && liveItem.cover != '') {
+ if (liveItem != null && liveItem.cover != null && liveItem.cover != '') {
cover = liveItem.cover;
}
}
diff --git a/lib/pages/liveRoom/view.dart b/lib/pages/liveRoom/view.dart
index 0c650126..fa881cb8 100644
--- a/lib/pages/liveRoom/view.dart
+++ b/lib/pages/liveRoom/view.dart
@@ -48,32 +48,35 @@ class _LiveRoomPageState extends State {
appBar: AppBar(
centerTitle: false,
titleSpacing: 0,
- title: Row(
- children: [
- NetworkImgLayer(
- width: 34,
- height: 34,
- type: 'avatar',
- src: _liveRoomController.liveItem.face,
- ),
- const SizedBox(width: 10),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- _liveRoomController.liveItem.uname,
- style: const TextStyle(fontSize: 14),
- ),
- const SizedBox(height: 1),
- if (_liveRoomController.liveItem.watchedShow != null)
- Text(
- _liveRoomController.liveItem.watchedShow['text_large'] ??
- '',
- style: const TextStyle(fontSize: 12)),
- ],
- ),
- ],
- ),
+ title: _liveRoomController.liveItem != null
+ ? Row(
+ children: [
+ NetworkImgLayer(
+ width: 34,
+ height: 34,
+ type: 'avatar',
+ src: _liveRoomController.liveItem.face,
+ ),
+ const SizedBox(width: 10),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ _liveRoomController.liveItem.uname,
+ style: const TextStyle(fontSize: 14),
+ ),
+ const SizedBox(height: 1),
+ if (_liveRoomController.liveItem.watchedShow != null)
+ Text(
+ _liveRoomController
+ .liveItem.watchedShow['text_large'] ??
+ '',
+ style: const TextStyle(fontSize: 12)),
+ ],
+ ),
+ ],
+ )
+ : const SizedBox(),
// actions: [
// SizedBox(
// height: 34,
@@ -94,21 +97,22 @@ class _LiveRoomPageState extends State {
? PLVideoPlayer(controller: plPlayerController!)
: const SizedBox(),
),
- if (_liveRoomController.liveItem.cover != null)
- Visibility(
- visible: isShowCover,
- child: Positioned(
- top: 0,
- left: 0,
- right: 0,
- child: NetworkImgLayer(
- type: 'emote',
- src: _liveRoomController.liveItem.cover,
- width: Get.size.width,
- height: videoHeight,
- ),
- ),
- ),
+ // if (_liveRoomController.liveItem != null &&
+ // _liveRoomController.liveItem.cover != null)
+ // Visibility(
+ // visible: isShowCover,
+ // child: Positioned(
+ // top: 0,
+ // left: 0,
+ // right: 0,
+ // child: NetworkImgLayer(
+ // type: 'emote',
+ // src: _liveRoomController.liveItem.cover,
+ // width: Get.size.width,
+ // height: videoHeight,
+ // ),
+ // ),
+ // ),
],
),
),
diff --git a/lib/pages/rcmd/controller.dart b/lib/pages/rcmd/controller.dart
index df7aa2e7..183b79bf 100644
--- a/lib/pages/rcmd/controller.dart
+++ b/lib/pages/rcmd/controller.dart
@@ -12,10 +12,14 @@ class RcmdController extends GetxController {
bool isLoadingMore = true;
OverlayEntry? popupDialog;
Box recVideo = GStrorage.recVideo;
+ Box setting = GStrorage.setting;
+ RxInt crossAxisCount = 2.obs;
@override
void onInit() {
super.onInit();
+ crossAxisCount.value =
+ setting.get(SettingBoxKey.enableSingleRow, defaultValue: false) ? 1 : 2;
if (recVideo.get('cacheList') != null &&
recVideo.get('cacheList').isNotEmpty) {
List list = [];
diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart
index 0ed66ac9..ab4d7f55 100644
--- a/lib/pages/rcmd/view.dart
+++ b/lib/pages/rcmd/view.dart
@@ -142,31 +142,34 @@ class _RcmdPageState extends State
}
Widget contentGrid(ctr, videoList) {
- double maxWidth = Get.size.width;
- int baseWidth = 500;
- int step = 300;
- int crossAxisCount =
- maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2;
- if (maxWidth < 300) {
- crossAxisCount = 1;
- }
+ // double maxWidth = Get.size.width;
+ // int baseWidth = 500;
+ // int step = 300;
+ // int crossAxisCount =
+ // maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2;
+ // if (maxWidth < 300) {
+ // crossAxisCount = 1;
+ // }
+ int crossAxisCount = ctr.crossAxisCount.value;
+ double mainAxisExtent =
+ (Get.size.width / crossAxisCount / StyleString.aspectRatio) +
+ 68 * MediaQuery.of(context).textScaleFactor;
return SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// 行间距
- mainAxisSpacing: StyleString.cardSpace + 4,
+ mainAxisSpacing: StyleString.safeSpace,
// 列间距
- crossAxisSpacing: StyleString.cardSpace + 4,
+ crossAxisSpacing: StyleString.safeSpace,
// 列数
crossAxisCount: crossAxisCount,
- mainAxisExtent:
- (Get.size.width / crossAxisCount / StyleString.aspectRatio) +
- 68 * MediaQuery.of(context).textScaleFactor,
+ mainAxisExtent: mainAxisExtent,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return videoList!.isNotEmpty
? VideoCardV(
videoItem: videoList[index],
+ crossAxisCount: crossAxisCount,
longPress: () {
_rcmdController.popupDialog =
_createPopupDialog(videoList[index]);
diff --git a/lib/pages/search/controller.dart b/lib/pages/search/controller.dart
index 20277b89..64f3cb65 100644
--- a/lib/pages/search/controller.dart
+++ b/lib/pages/search/controller.dart
@@ -21,6 +21,8 @@ class SSearchController extends GetxController {
Debouncer(delay: const Duration(milliseconds: 200)); // 设置延迟时间
String hintText = '搜索';
RxString defaultSearch = '输入关键词搜索'.obs;
+ Box setting = GStrorage.setting;
+ bool enableHotKey = true;
@override
void onInit() {
@@ -38,6 +40,7 @@ class SSearchController extends GetxController {
}
historyCacheList = histiryWord.get('cacheList') ?? [];
historyList.value = historyCacheList;
+ enableHotKey = setting.get(SettingBoxKey.enableHotKey, defaultValue: true);
}
void onChange(value) {
diff --git a/lib/pages/search/view.dart b/lib/pages/search/view.dart
index 04af9ed4..ea902365 100644
--- a/lib/pages/search/view.dart
+++ b/lib/pages/search/view.dart
@@ -146,7 +146,9 @@ class _SearchPageState extends State with RouteAware {
// 搜索建议
_searchSuggest(),
// 热搜
- hotSearch(_searchController),
+ Visibility(
+ visible: _searchController.enableHotKey,
+ child: hotSearch(_searchController)),
// 搜索历史
_history()
],
diff --git a/lib/pages/searchPanel/controller.dart b/lib/pages/searchPanel/controller.dart
index 826fdacc..399df1e1 100644
--- a/lib/pages/searchPanel/controller.dart
+++ b/lib/pages/searchPanel/controller.dart
@@ -56,14 +56,20 @@ class SearchPanelController extends GetxController {
// 匹配输入内容,如果是AV、BV号且有结果 直接跳转详情页
Map matchRes = IdUtils.matchAvorBv(input: keyword);
List matchKeys = matchRes.keys.toList();
- if (matchKeys.isNotEmpty && searchType == SearchType.video) {
- String bvid = resultList.first.bvid;
- int aid = resultList.first.aid;
+ String bvid = resultList.first.bvid;
+ // keyword 可能输入纯数字
+ int aid = resultList.first.aid;
+ if (matchKeys.isNotEmpty && searchType == SearchType.video ||
+ aid.toString() == keyword) {
String heroTag = Utils.makeHeroTag(bvid);
-
int cid = await SearchHttp.ab2c(aid: aid, bvid: bvid);
- if (matchKeys.first == 'BV' && matchRes[matchKeys.first] == bvid ||
- matchKeys.first == 'AV' && matchRes[matchKeys.first] == aid) {
+ if (matchKeys.isNotEmpty &&
+ matchKeys.first == 'BV' &&
+ matchRes[matchKeys.first] == bvid ||
+ matchKeys.isNotEmpty &&
+ matchKeys.first == 'AV' &&
+ matchRes[matchKeys.first] == aid ||
+ aid.toString() == keyword) {
Get.toNamed(
'/video?bvid=$bvid&cid=$cid',
arguments: {'videoItem': resultList.first, 'heroTag': heroTag},
diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart
index 29ad5aac..4ed55f96 100644
--- a/lib/pages/setting/extra_setting.dart
+++ b/lib/pages/setting/extra_setting.dart
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/models/common/dynamics_type.dart';
import 'package:pilipala/models/common/reply_sort_type.dart';
@@ -47,6 +48,13 @@ class _ExtraSettingState extends State {
),
body: ListView(
children: [
+ SetSwitchItem(
+ title: '大家都在搜',
+ subTitle: '是否展示「大家都在搜」',
+ setKey: SettingBoxKey.enableHotKey,
+ defaultVal: true,
+ callFn: (val) => {SmartDialog.showToast('下次启动时生效')},
+ ),
ListTile(
dense: false,
title: Text('评论展示', style: titleStyle),
diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart
index 73cad841..c502beeb 100644
--- a/lib/pages/setting/style_setting.dart
+++ b/lib/pages/setting/style_setting.dart
@@ -1,6 +1,7 @@
import 'dart:io';
import 'package:flutter/material.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/models/common/theme_type.dart';
@@ -75,6 +76,13 @@ class _StyleSettingState extends State {
setKey: SettingBoxKey.iosTransition,
defaultVal: false,
),
+ SetSwitchItem(
+ title: '首页单列',
+ subTitle: '每行展示一个内容卡片',
+ setKey: SettingBoxKey.enableSingleRow,
+ defaultVal: false,
+ callFn: (val) => {SmartDialog.showToast('下次启动时生效')},
+ ),
ListTile(
dense: false,
onTap: () {
diff --git a/lib/pages/setting/widgets/switch_item.dart b/lib/pages/setting/widgets/switch_item.dart
index 1b2cc620..0e091b9a 100644
--- a/lib/pages/setting/widgets/switch_item.dart
+++ b/lib/pages/setting/widgets/switch_item.dart
@@ -8,12 +8,14 @@ class SetSwitchItem extends StatefulWidget {
final String? subTitle;
final String? setKey;
final bool? defaultVal;
+ final Function? callFn;
const SetSwitchItem({
this.title,
this.subTitle,
this.setKey,
this.defaultVal,
+ this.callFn,
Key? key,
}) : super(key: key);
@@ -32,12 +34,15 @@ class _SetSwitchItemState extends State {
val = Setting.get(widget.setKey, defaultValue: widget.defaultVal ?? false);
}
- void switchChange(value) {
+ void switchChange(value) async {
val = value ?? !val;
- Setting.put(widget.setKey, val);
+ await Setting.put(widget.setKey, val);
if (widget.setKey == SettingBoxKey.autoUpdate && value == true) {
Utils.checkUpdata();
}
+ if (widget.callFn != null) {
+ widget.callFn!.call(val);
+ }
setState(() {});
}
diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart
index e4fd89dc..8fd71b32 100644
--- a/lib/pages/video/detail/controller.dart
+++ b/lib/pages/video/detail/controller.dart
@@ -209,27 +209,17 @@ class VideoDetailController extends GetxController
if (result['status']) {
data = result['data'];
- /// 优先顺序 省流模式 -> 设置中指定质量 -> 当前可选的最高质量
- // firstVideo = data.dash!.video!.first;
- // videoUrl = firstVideo.baseUrl!;
- // //
- // currentVideoQa = VideoQualityCode.fromCode(firstVideo.id!)!;
-
- // /// 优先顺序 设置中指定质量 -> 当前可选的最高质量
- // AudioItem firstAudio =
- // data.dash!.audio!.isNotEmpty ? data.dash!.audio!.first : AudioItem();
- // audioUrl = firstAudio.baseUrl ?? '';
-
List allVideosList = data.dash!.video!;
try {
// 当前可播放的最高质量视频
int currentHighVideoQa = allVideosList.first.quality!.code;
- //
+ // 使用预设的画质 | 当前可用的最高质量
int cacheVideoQa = setting.get(SettingBoxKey.defaultVideoQa,
defaultValue: currentHighVideoQa);
int resVideoQa = currentHighVideoQa;
if (cacheVideoQa <= currentHighVideoQa) {
+ // 如果预设的画质低于当前最高
List numbers = data.acceptQuality!
.where((e) => e <= currentHighVideoQa)
.toList();
@@ -250,10 +240,16 @@ class VideoDetailController extends GetxController
currentDecodeFormats = VideoDecodeFormatsCode.fromString(setting.get(
SettingBoxKey.defaultDecode,
defaultValue: VideoDecodeFormats.values.last.code))!;
+ print(currentDecodeFormats.description);
try {
// 当前视频没有对应格式返回第一个
- currentDecodeFormats = supportDecodeFormats
- .contains(currentDecodeFormats)
+ bool flag = false;
+ for (var i in supportDecodeFormats) {
+ if (i.startsWith(currentDecodeFormats.code)) {
+ flag = true;
+ }
+ }
+ currentDecodeFormats = flag
? currentDecodeFormats
: VideoDecodeFormatsCode.fromString(supportDecodeFormats.first)!;
} catch (e) {
@@ -297,13 +293,6 @@ class VideoDetailController extends GetxController
}
defaultST = Duration(milliseconds: data.lastPlayTime!);
await playerInit();
-
- // await playerInit(
- // firstVideo,
- // audioUrl,
- // defaultST: Duration(milliseconds: data.lastPlayTime!),
- // duration: data.timeLength ?? 0,
- // );
} else {
if (result['code'] == -404) {
isShowCover.value = false;
diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart
index c45d56dd..8b47dde6 100644
--- a/lib/pages/video/detail/reply/widgets/reply_item.dart
+++ b/lib/pages/video/detail/reply/widgets/reply_item.dart
@@ -602,9 +602,26 @@ InlineSpan buildContent(
color: Theme.of(context).colorScheme.primary,
),
recognizer: TapGestureRecognizer()
- ..onTap = () => Get.toNamed('/searchResult', parameters: {
- 'keyword': content.jumpUrl[matchStr]['title']
- }),
+ ..onTap = () {
+ String appUrlSchema =
+ content.jumpUrl[matchStr]['app_url_schema'];
+ if (appUrlSchema == '') {
+ Get.toNamed(
+ '/webview',
+ parameters: {
+ 'url': matchStr,
+ 'type': 'url',
+ 'pageTitle': ''
+ },
+ );
+ } else {
+ if (appUrlSchema.startsWith('bilibili://search')) {
+ Get.toNamed('/searchResult', parameters: {
+ 'keyword': content.jumpUrl[matchStr]['title']
+ });
+ }
+ }
+ },
),
);
spanChilds.add(
diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart
index d45bcfb9..2bf53cf0 100644
--- a/lib/pages/video/detail/view.dart
+++ b/lib/pages/video/detail/view.dart
@@ -149,7 +149,7 @@ class _VideoDetailPageState extends State
key: videoDetailController.scaffoldKey,
// fix 1px black line
// backgroundColor: Colors.transparent,
- backgroundColor: Theme.of(context).colorScheme.background,
+ backgroundColor: Colors.black,
body: ExtendedNestedScrollView(
controller: _extendNestCtr,
headerSliverBuilder:
@@ -162,11 +162,7 @@ class _VideoDetailPageState extends State
scrolledUnderElevation: 0,
forceElevated: innerBoxIsScrolled,
expandedHeight: videoHeight,
- backgroundColor:
- MediaQuery.of(Get.context!).platformBrightness ==
- Brightness.dark
- ? Colors.black
- : Theme.of(context).colorScheme.background,
+ backgroundColor: Colors.black,
flexibleSpace: FlexibleSpaceBar(
background: Padding(
padding: EdgeInsets.only(top: statusBarHeight),
diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart
index 77619043..0a84a193 100644
--- a/lib/pages/video/detail/widgets/header_control.dart
+++ b/lib/pages/video/detail/widgets/header_control.dart
@@ -167,50 +167,46 @@ class _HeaderControlState extends State {
/// 选择倍速
void showSetSpeedSheet() {
- showModalBottomSheet(
- context: context,
- elevation: 0,
- backgroundColor: Colors.transparent,
- builder: (BuildContext context) {
- return Container(
- width: double.infinity,
- height: 450,
- clipBehavior: Clip.hardEdge,
- decoration: BoxDecoration(
- color: Theme.of(context).colorScheme.background,
- borderRadius: const BorderRadius.all(Radius.circular(12)),
- ),
- margin: const EdgeInsets.all(12),
- child: Material(
- child: ListView(
- physics: const NeverScrollableScrollPhysics(),
+ double currentSpeed = widget.controller!.playbackSpeed;
+ SmartDialog.show(
+ animationType: SmartAnimationType.centerFade_otherSlide,
+ builder: (context) {
+ return AlertDialog(
+ title: const Text('播放速度'),
+ contentPadding: const EdgeInsets.fromLTRB(0, 20, 0, 20),
+ content: StatefulBuilder(builder: (context, StateSetter setState) {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
children: [
- const SizedBox(
- height: 45,
- child: Center(
- child: Text('播放速度'),
- ),
- ),
- for (var i in playSpeed) ...[
- ListTile(
- onTap: () {
- widget.controller!.setPlaybackSpeed(i.value);
- Get.back(result: {'playbackSpeed': i.value});
- },
- dense: true,
- contentPadding: const EdgeInsets.only(left: 20, right: 20),
- title: Text(i.description),
- trailing: i.value == widget.controller!.playbackSpeed
- ? Icon(
- Icons.done,
- color: Theme.of(context).colorScheme.primary,
- )
- : null,
- ),
- ]
+ Text('$currentSpeed倍'),
+ Slider(
+ min: PlaySpeed.values.first.value,
+ max: PlaySpeed.values.last.value,
+ value: currentSpeed,
+ divisions: PlaySpeed.values.length - 1,
+ label: '${currentSpeed}x',
+ onChanged: (double val) =>
+ {setState(() => currentSpeed = val)},
+ )
],
+ );
+ }),
+ actions: [
+ TextButton(
+ onPressed: () => SmartDialog.dismiss(),
+ child: Text(
+ '取消',
+ style: TextStyle(color: Theme.of(context).colorScheme.outline),
+ ),
),
- ),
+ TextButton(
+ onPressed: () async {
+ await SmartDialog.dismiss();
+ widget.controller!.setPlaybackSpeed(currentSpeed);
+ },
+ child: const Text('确定'),
+ ),
+ ],
);
},
);
diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart
index 03b99ee8..67d60d6a 100644
--- a/lib/pages/webview/controller.dart
+++ b/lib/pages/webview/controller.dart
@@ -63,6 +63,13 @@ class WebviewController extends GetxController {
onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('bilibili://')) {
+ if (request.url.startsWith('bilibili://video/')) {
+ String str = Uri.parse(request.url).pathSegments[0];
+ Get.offAndToNamed(
+ '/searchResult',
+ parameters: {'keyword': str},
+ );
+ }
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
diff --git a/lib/plugin/pl_player/models/play_speed.dart b/lib/plugin/pl_player/models/play_speed.dart
index 01226ed5..5ccb91a4 100644
--- a/lib/plugin/pl_player/models/play_speed.dart
+++ b/lib/plugin/pl_player/models/play_speed.dart
@@ -2,11 +2,23 @@ enum PlaySpeed {
pointTwoFive,
pointFive,
pointSevenFive,
+
one,
onePointTwoFive,
onePointFive,
onePointSevenFive,
- two
+
+ two,
+ twoPointTwoFive,
+ twoPointFive,
+ twoPointSevenFive,
+
+ twhree,
+ threePointTwoFive,
+ threePointFive,
+ threePointSevenFive,
+
+ four,
}
extension PlaySpeedExtension on PlaySpeed {
@@ -17,8 +29,15 @@ extension PlaySpeedExtension on PlaySpeed {
'正常速度',
'1.25倍',
'1.5倍',
- '1.75倍',
'2.0倍',
+ '2.25倍',
+ '2.5倍',
+ '2.75倍',
+ '3.0倍',
+ '3.25倍',
+ '3.5倍',
+ '3.75倍',
+ '4.0倍'
];
get description => _descList[index];
@@ -30,7 +49,15 @@ extension PlaySpeedExtension on PlaySpeed {
1.25,
1.5,
1.75,
- 2.0
+ 2.0,
+ 2.25,
+ 2.5,
+ 2.75,
+ 3.0,
+ 3.25,
+ 3.5,
+ 3.75,
+ 4.0,
];
get value => _valueList[index];
get defaultValue => _valueList[3];
diff --git a/lib/plugin/pl_player/utils/fullscreen.dart b/lib/plugin/pl_player/utils/fullscreen.dart
index 46540bbd..6d36c409 100644
--- a/lib/plugin/pl_player/utils/fullscreen.dart
+++ b/lib/plugin/pl_player/utils/fullscreen.dart
@@ -1,5 +1,6 @@
import 'dart:io';
+import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
@@ -48,12 +49,17 @@ Future enterFullScreen() async {
//退出全屏显示
Future exitFullScreen() async {
dynamic document;
+ late SystemUiMode mode = SystemUiMode.edgeToEdge;
try {
if (kIsWeb) {
document.exitFullscreen();
} else if (Platform.isAndroid || Platform.isIOS) {
+ if (Platform.isAndroid &&
+ (await DeviceInfoPlugin().androidInfo).version.sdkInt < 29) {
+ mode = SystemUiMode.manual;
+ }
await SystemChrome.setEnabledSystemUIMode(
- SystemUiMode.manual,
+ mode,
overlays: SystemUiOverlay.values,
);
await SystemChrome.setPreferredOrientations([]);
diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart
index 48c016c9..077b5a1b 100644
--- a/lib/plugin/pl_player/view.dart
+++ b/lib/plugin/pl_player/view.dart
@@ -199,22 +199,11 @@ class _PLVideoPlayerState extends State
context: Get.context!,
useSafeArea: false,
builder: (context) => Dialog.fullscreen(
- child: Scaffold(
- backgroundColor: Colors.black,
- appBar: AppBar(
- primary: false,
- toolbarHeight: 0,
- backgroundColor: Colors.black,
- systemOverlayStyle: SystemUiOverlayStyle.light,
+ backgroundColor: Colors.black,
+ child: PLVideoPlayer(
+ controller: _,
+ headerControl: _.headerControl,
),
- body: SafeArea(
- bottom: false,
- child: PLVideoPlayer(
- controller: _,
- headerControl: _.headerControl,
- ),
- ),
- ),
),
);
if (result == null) {
diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart
index 995aef59..f49e9138 100644
--- a/lib/plugin/pl_player/widgets/bottom_control.dart
+++ b/lib/plugin/pl_player/widgets/bottom_control.dart
@@ -30,12 +30,12 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
elevation: 0,
scrolledUnderElevation: 0,
primary: false,
- toolbarHeight: 73,
+ toolbarHeight: 85,
automaticallyImplyLeading: false,
titleSpacing: 14,
title: Column(
children: [
- const SizedBox(height: 23),
+ const SizedBox(height: 17),
Obx(
() {
final int value = _.sliderPosition.value.inSeconds;
@@ -45,7 +45,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget {
return Container();
}
return Padding(
- padding: const EdgeInsets.only(left: 5, right: 5, bottom: 5),
+ padding: const EdgeInsets.only(left: 7, right: 5, bottom: 6),
child: ProgressBar(
progress: Duration(seconds: value),
buffered: Duration(seconds: buffer),
diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart
new file mode 100644
index 00000000..3736b5b5
--- /dev/null
+++ b/lib/utils/app_scheme.dart
@@ -0,0 +1,106 @@
+import 'package:appscheme/appscheme.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
+import 'package:pilipala/http/search.dart';
+import 'package:pilipala/models/common/search_type.dart';
+
+import 'id_utils.dart';
+import 'utils.dart';
+
+class PiliSchame {
+ static AppScheme appScheme = AppSchemeImpl.getInstance() as AppScheme;
+ static void init() async {
+ ///
+ SchemeEntity? value = await appScheme.getInitScheme();
+ if (value != null) {
+ _routePush(value);
+ }
+
+ ///
+ appScheme.getLatestScheme().then((value) {
+ if (value != null) {}
+ });
+
+ /// 注册从外部打开的Scheme监听信息 #
+ appScheme.registerSchemeListener().listen((event) {
+ if (event != null) {
+ _routePush(event);
+ }
+ });
+ }
+
+ /// 路由跳转
+ static void _routePush(value) async {
+ String scheme = value.scheme;
+ String host = value.host;
+ String path = value.path;
+
+ if (scheme == 'bilibili') {
+ // bilibili://root
+ if (host == 'root') {
+ Navigator.popUntil(Get.context!, (route) => route.isFirst);
+ }
+
+ // bilibili://space/{uid}
+ else if (host == 'space') {
+ var mid = path.split('/').last;
+ Get.toNamed(
+ '/member?mid=$mid',
+ arguments: {'face': null},
+ );
+ }
+
+ // bilibili://video/{aid}
+ else if (host == 'video') {
+ var pathQuery = path.split('/').last;
+ int aid = int.parse(pathQuery);
+ String bvid = IdUtils.av2bv(aid);
+ int cid = await SearchHttp.ab2c(bvid: bvid);
+ String heroTag = Utils.makeHeroTag(aid);
+ Get.toNamed('/video?bvid=$bvid&cid=$cid', arguments: {
+ 'pic': null,
+ 'heroTag': heroTag,
+ });
+ }
+
+ // bilibili://live/{roomid}
+ else if (host == 'live') {
+ var roomId = path.split('/').last;
+ Get.toNamed('/liveRoom?roomid=$roomId',
+ arguments: {'liveItem': null, 'heroTag': roomId.toString()});
+ }
+
+ // bilibili://bangumi/season/${ssid}
+ else if (host == 'bangumi') {
+ if (path.startsWith('/season')) {
+ SmartDialog.showLoading(msg: '获取中...');
+ try {
+ var seasonId = path.split('/').last;
+ var result = await SearchHttp.bangumiInfo(
+ seasonId: int.parse(seasonId), epId: null);
+ if (result['status']) {
+ var bangumiDetail = result['data'];
+ int cid = bangumiDetail.episodes!.first.cid;
+ String bvid = IdUtils.av2bv(bangumiDetail.episodes!.first.aid);
+ String heroTag = Utils.makeHeroTag(cid);
+ var epId = bangumiDetail.episodes!.first.id;
+ SmartDialog.dismiss().then(
+ (e) => Get.toNamed(
+ '/video?bvid=$bvid&cid=$cid&epId=$epId',
+ arguments: {
+ 'pic': bangumiDetail.cover,
+ 'heroTag': heroTag,
+ 'videoType': SearchType.media_bangumi,
+ },
+ ),
+ );
+ }
+ } catch (e) {
+ SmartDialog.showToast('失败:${e.toString()}');
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart
index a117760e..21a8060b 100644
--- a/lib/utils/storage.dart
+++ b/lib/utils/storage.dart
@@ -105,6 +105,7 @@ class SettingBoxKey {
static const String autoUpdate = 'autoUpdate';
static const String replySortType = 'replySortType';
static const String defaultDynamicType = 'defaultDynamicType';
+ static const String enableHotKey = 'enableHotKey';
/// 外观
static const String themeMode = 'themeMode';
@@ -112,6 +113,7 @@ class SettingBoxKey {
static const String dynamicColor = 'dynamicColor'; // bool
static const String customColor = 'customColor'; // 自定义主题色
static const String iosTransition = 'iosTransition'; // ios路由
+ static const String enableSingleRow = 'enableSingleRow'; // 首页单列
}
class LocalCacheKey {
diff --git a/pubspec.lock b/pubspec.lock
index eddd41e7..633e51c6 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -25,6 +25,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.7"
+ appscheme:
+ dependency: "direct main"
+ description:
+ name: appscheme
+ sha256: b885b65219f3839ebafc937024a1bc5ce5a75b0e458fd249ef15e80e81235b6f
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.8"
archive:
dependency: transitive
description:
@@ -713,20 +721,20 @@ packages:
dependency: "direct main"
description:
name: media_kit
- sha256: "0a89e7037002a62701ec319c375586849f9ef8e681820e1dd4a4ff7b843f7542"
+ sha256: "66f04934bcadf592f24d829127471e4dc304de8e9bba5795ade2f3e95552ebfc"
url: "https://pub.dev"
source: hosted
- version: "1.1.4+1"
+ version: "1.1.6"
media_kit_libs_android_video:
- dependency: "direct main"
+ dependency: transitive
description:
name: media_kit_libs_android_video
- sha256: "142d389bf3efcf8469594a9c7a06a92fc25843fc6c0c3247f76cdcf70b3b29de"
+ sha256: "498a5062bc5f000bd23ada3be788ea886ab32c52f7a8252dde1264ca019b819b"
url: "https://pub.dev"
source: hosted
- version: "1.3.2"
+ version: "1.3.3"
media_kit_libs_ios_video:
- dependency: "direct main"
+ dependency: transitive
description:
name: media_kit_libs_ios_video
sha256: fed403dc9d54462e51ee80e0cb23c12a53fadea9a8fa18aca2de9054176d1159
@@ -734,31 +742,39 @@ packages:
source: hosted
version: "1.1.3"
media_kit_libs_linux:
- dependency: "direct main"
+ dependency: transitive
description:
name: media_kit_libs_linux
- sha256: "570bf18ebbd1221caec082657468be05d180510385d3515ec38e0be44fdcc859"
+ sha256: "3b7c272179639a914dc8a50bf8a3f2df0e9a503bd727c88fab499dbdf6cb1eb8"
url: "https://pub.dev"
source: hosted
- version: "1.1.1"
+ version: "1.1.2"
media_kit_libs_macos_video:
- dependency: "direct main"
+ dependency: transitive
description:
name: media_kit_libs_macos_video
sha256: c06e831f3c22a45296d375788d9bc07871b448f8e9ec98d77b11e5e118a83fb2
url: "https://pub.dev"
source: hosted
version: "1.1.3"
- media_kit_libs_windows_video:
+ media_kit_libs_video:
dependency: "direct main"
description:
- name: media_kit_libs_windows_video
- sha256: f33aabd8414470d99e2c91dd98d605e6a5f1c4b8082dd933c10951bc961b9124
+ name: media_kit_libs_video
+ sha256: "48c8ace458f340e6b930c89c48141ea727b80aa0878f7a01904d7d439865f162"
url: "https://pub.dev"
source: hosted
- version: "1.0.7"
+ version: "1.0.0"
+ media_kit_libs_windows_video:
+ dependency: transitive
+ description:
+ name: media_kit_libs_windows_video
+ sha256: "923f068344d7d200184e0aaa2597f3de6c05982a3b1f18035d842ab53f2a1350"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.8"
media_kit_native_event_loop:
- dependency: "direct main"
+ dependency: transitive
description:
name: media_kit_native_event_loop
sha256: e37ce6fb5fa71b8cf513c6a6cd591367743a342a385e7da621a047dd8ef6f4a4
@@ -769,10 +785,10 @@ packages:
dependency: "direct main"
description:
name: media_kit_video
- sha256: e7fcbe426d42a78ad6696f8f557adb9cbdc012177829026d04992cc106a1c815
+ sha256: "809a3797da7d49fad85f139555b352dd615f9d2da6ae9f1745c6978963491bae"
url: "https://pub.dev"
source: hosted
- version: "1.1.5"
+ version: "1.1.7"
meta:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index a49569a2..9ba87b5a 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -79,26 +79,14 @@ dependencies:
flutter_smart_dialog: ^4.9.3+2
# 下滑关闭
dismissible_page: ^1.0.2
- # 媒体播放
- # flutter_meedu_media_kit:
- # path: /Users/rr/Desktop/code/flutter_meedu_media_kit/package
- # git:
- # url: https://github.com/guozhigq/flutter_meedu_media_kit.git
- # ref: feature-custom
- # path: package
custom_sliding_segmented_control: ^1.7.5
# 加密
crypto: ^3.0.3
# 视频播放器
- media_kit: ^1.1.4 # Primary package.
- media_kit_video: ^1.1.5 # For video rendering.
- media_kit_native_event_loop: ^1.0.7 # Support for higher number of concurrent instances & better performance.
- media_kit_libs_android_video: ^1.3.2 # Android package for video native libraries.
- media_kit_libs_ios_video: ^1.1.3 # iOS package for video native libraries.
- media_kit_libs_macos_video: ^1.1.3 # macOS package for video native libraries.
- media_kit_libs_windows_video: ^1.0.7 # Windows package for video native libraries.
- media_kit_libs_linux: ^1.1.1
+ media_kit: ^1.1.6
+ media_kit_video: ^1.1.7
+ media_kit_libs_video: ^1.0.0
# 音量、亮度、屏幕控制
flutter_volume_controller: ^1.2.7
@@ -119,6 +107,8 @@ dependencies:
easy_debounce: ^2.0.3
# 高帧率
flutter_displaymode: ^0.6.0
+ # scheme跳转
+ appscheme: ^1.0.8
dev_dependencies:
flutter_test: