Merge branch 'main' into fix
This commit is contained in:
@ -154,7 +154,7 @@ SPEC CHECKSUMS:
|
|||||||
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
|
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
|
||||||
auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d
|
auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d
|
||||||
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
|
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
|
||||||
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
|
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
|
||||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529
|
flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529
|
||||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||||
@ -163,17 +163,17 @@ SPEC CHECKSUMS:
|
|||||||
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
||||||
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
|
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
|
||||||
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
|
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
|
||||||
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
|
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
|
||||||
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||||
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
|
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
|
||||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||||
saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78
|
saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78
|
||||||
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
|
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
|
||||||
share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028
|
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
|
||||||
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
||||||
status_bar_control: 7c84146799e6a076315cc1550f78ef53aae3e446
|
status_bar_control: 7c84146799e6a076315cc1550f78ef53aae3e446
|
||||||
system_proxy: bec1a5c5af67dd3e3ebf43979400a8756c04cc44
|
system_proxy: bec1a5c5af67dd3e3ebf43979400a8756c04cc44
|
||||||
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b
|
||||||
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
|
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
|
||||||
wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
|
wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
|
||||||
webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7
|
webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7
|
||||||
@ -181,4 +181,4 @@ SPEC CHECKSUMS:
|
|||||||
|
|
||||||
PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b
|
PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b
|
||||||
|
|
||||||
COCOAPODS: 1.12.1
|
COCOAPODS: 1.14.3
|
||||||
|
@ -312,6 +312,10 @@ class Api {
|
|||||||
|
|
||||||
static const String webDanmaku = '/x/v2/dm/web/seg.so';
|
static const String webDanmaku = '/x/v2/dm/web/seg.so';
|
||||||
|
|
||||||
|
//发送视频弹幕
|
||||||
|
//https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/danmaku/action.md
|
||||||
|
static const String shootDanmaku = '/x/v2/dm/post';
|
||||||
|
|
||||||
// up主分组
|
// up主分组
|
||||||
static const String followUpTag = '/x/relation/tags';
|
static const String followUpTag = '/x/relation/tags';
|
||||||
|
|
||||||
|
@ -24,4 +24,72 @@ class DanmakaHttp {
|
|||||||
);
|
);
|
||||||
return DmSegMobileReply.fromBuffer(response.data);
|
return DmSegMobileReply.fromBuffer(response.data);
|
||||||
}
|
}
|
||||||
|
static Future shootDanmaku({
|
||||||
|
int type = 1,//弹幕类选择(1:视频弹幕 2:漫画弹幕)
|
||||||
|
required int oid,// 视频cid
|
||||||
|
required String msg,//弹幕文本(长度小于 100 字符)
|
||||||
|
int mode = 1,// 弹幕类型(1:滚动弹幕 4:底端弹幕 5:顶端弹幕 6:逆向弹幕(不能使用) 7:高级弹幕 8:代码弹幕(不能使用) 9:BAS弹幕(pool必须为2))
|
||||||
|
// String? aid,// 稿件avid
|
||||||
|
// String? bvid,// bvid与aid必须有一个
|
||||||
|
required String bvid,
|
||||||
|
int? progress,// 弹幕出现在视频内的时间(单位为毫秒,默认为0)
|
||||||
|
int? color,// 弹幕颜色(默认白色,16777215)
|
||||||
|
int? fontsize,// 弹幕字号(默认25)
|
||||||
|
int? pool,// 弹幕池选择(0:普通池 1:字幕池 2:特殊池(代码/BAS弹幕)默认普通池,0)
|
||||||
|
//int? rnd,// 当前时间戳*1000000(若无此项,则发送弹幕冷却时间限制为90s;若有此项,则发送弹幕冷却时间限制为5s)
|
||||||
|
int? colorful,//60001:专属渐变彩色(需要会员)
|
||||||
|
int? checkbox_type,//是否带 UP 身份标识(0:普通;4:带有标识)
|
||||||
|
// String? csrf,//CSRF Token(位于 Cookie) Cookie 方式必要
|
||||||
|
// String? access_key,// APP 登录 Token APP 方式必要
|
||||||
|
}) async {
|
||||||
|
// 构建参数对象
|
||||||
|
// assert(aid != null || bvid != null);
|
||||||
|
// assert(csrf != null || access_key != null);
|
||||||
|
assert(msg.length < 100);
|
||||||
|
// 构建参数对象
|
||||||
|
var params = <String, dynamic>{
|
||||||
|
'type': type,
|
||||||
|
'oid': oid,
|
||||||
|
'msg': msg,
|
||||||
|
'mode': mode,
|
||||||
|
//'aid': aid,
|
||||||
|
'bvid': bvid,
|
||||||
|
'progress': progress,
|
||||||
|
'color': color,
|
||||||
|
'fontsize': fontsize,
|
||||||
|
'pool': pool,
|
||||||
|
'rnd': DateTime.now().microsecondsSinceEpoch,
|
||||||
|
'colorful': colorful,
|
||||||
|
'checkbox_type': checkbox_type,
|
||||||
|
'csrf': await Request.getCsrf(),
|
||||||
|
// 'access_key': access_key,
|
||||||
|
}..removeWhere((key, value) => value == null);
|
||||||
|
|
||||||
|
var response = await Request().post(
|
||||||
|
Api.shootDanmaku,
|
||||||
|
data: params,
|
||||||
|
options: Options(
|
||||||
|
contentType: Headers.formUrlEncodedContentType,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': '弹幕发送失败,状态码:${response.statusCode}',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (response.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': response.data['data'],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': "${response.data['code']}: ${response.data['message']}",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,11 +34,6 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Divider(
|
|
||||||
thickness: 8,
|
|
||||||
height: 10,
|
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
|
||||||
),
|
|
||||||
Image.asset(
|
Image.asset(
|
||||||
'assets/images/logo/logo_android_2.png',
|
'assets/images/logo/logo_android_2.png',
|
||||||
width: 150,
|
width: 150,
|
||||||
@ -83,9 +78,9 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
Divider(
|
Divider(
|
||||||
thickness: 8,
|
thickness: 1,
|
||||||
height: 30,
|
height: 30,
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
color: Theme.of(context).colorScheme.outlineVariant,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => _aboutController.githubUrl(),
|
onTap: () => _aboutController.githubUrl(),
|
||||||
@ -134,11 +129,6 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
title: const Text('赞助'),
|
title: const Text('赞助'),
|
||||||
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
|
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
|
||||||
),
|
),
|
||||||
Divider(
|
|
||||||
thickness: 8,
|
|
||||||
height: 30,
|
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -6,6 +6,7 @@ import 'package:flutter/rendering.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
|
import 'package:pilipala/pages/home/index.dart';
|
||||||
import 'package:pilipala/pages/main/index.dart';
|
import 'package:pilipala/pages/main/index.dart';
|
||||||
import 'package:pilipala/pages/rcmd/view.dart';
|
import 'package:pilipala/pages/rcmd/view.dart';
|
||||||
|
|
||||||
@ -35,6 +36,8 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
scrollController = _bangumidController.scrollController;
|
scrollController = _bangumidController.scrollController;
|
||||||
StreamController<bool> mainStream =
|
StreamController<bool> mainStream =
|
||||||
Get.find<MainController>().bottomBarStream;
|
Get.find<MainController>().bottomBarStream;
|
||||||
|
StreamController<bool> searchBarStream =
|
||||||
|
Get.find<HomeController>().searchBarStream;
|
||||||
_futureBuilderFuture = _bangumidController.queryBangumiListFeed();
|
_futureBuilderFuture = _bangumidController.queryBangumiListFeed();
|
||||||
_futureBuilderFutureFollow = _bangumidController.queryBangumiFollow();
|
_futureBuilderFutureFollow = _bangumidController.queryBangumiFollow();
|
||||||
scrollController.addListener(
|
scrollController.addListener(
|
||||||
@ -51,8 +54,10 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
scrollController.position.userScrollDirection;
|
scrollController.position.userScrollDirection;
|
||||||
if (direction == ScrollDirection.forward) {
|
if (direction == ScrollDirection.forward) {
|
||||||
mainStream.add(true);
|
mainStream.add(true);
|
||||||
|
searchBarStream.add(true);
|
||||||
} else if (direction == ScrollDirection.reverse) {
|
} else if (direction == ScrollDirection.reverse) {
|
||||||
mainStream.add(false);
|
mainStream.add(false);
|
||||||
|
searchBarStream.add(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@ -15,6 +17,10 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
|
|||||||
RxBool userLogin = false.obs;
|
RxBool userLogin = false.obs;
|
||||||
RxString userFace = ''.obs;
|
RxString userFace = ''.obs;
|
||||||
var userInfo;
|
var userInfo;
|
||||||
|
Box setting = GStrorage.setting;
|
||||||
|
late final StreamController<bool> searchBarStream =
|
||||||
|
StreamController<bool>.broadcast();
|
||||||
|
late bool hideSearchBar;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -33,6 +39,8 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
|
|||||||
length: tabs.length,
|
length: tabs.length,
|
||||||
vsync: this,
|
vsync: this,
|
||||||
);
|
);
|
||||||
|
hideSearchBar =
|
||||||
|
setting.get(SettingBoxKey.hideSearchBar, defaultValue: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onRefresh() {
|
void onRefresh() {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
@ -18,11 +20,17 @@ class _HomePageState extends State<HomePage>
|
|||||||
with AutomaticKeepAliveClientMixin, TickerProviderStateMixin {
|
with AutomaticKeepAliveClientMixin, TickerProviderStateMixin {
|
||||||
final HomeController _homeController = Get.put(HomeController());
|
final HomeController _homeController = Get.put(HomeController());
|
||||||
List videoList = [];
|
List videoList = [];
|
||||||
Stream<bool> stream = Get.find<MainController>().bottomBarStream.stream;
|
late Stream<bool> stream;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get wantKeepAlive => true;
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
stream = _homeController.searchBarStream.stream;
|
||||||
|
}
|
||||||
|
|
||||||
showUserBottonSheet() {
|
showUserBottonSheet() {
|
||||||
feedBack();
|
feedBack();
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
@ -46,7 +54,9 @@ class _HomePageState extends State<HomePage>
|
|||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
CustomAppBar(
|
CustomAppBar(
|
||||||
stream: stream,
|
stream: _homeController.hideSearchBar
|
||||||
|
? stream
|
||||||
|
: StreamController<bool>.broadcast().stream,
|
||||||
ctr: _homeController,
|
ctr: _homeController,
|
||||||
callback: showUserBottonSheet,
|
callback: showUserBottonSheet,
|
||||||
),
|
),
|
||||||
@ -119,7 +129,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
height: snapshot.data
|
height: snapshot.data
|
||||||
? MediaQuery.of(context).padding.top + 52
|
? MediaQuery.of(context).padding.top + 52
|
||||||
: MediaQuery.of(context).padding.top,
|
: MediaQuery.of(context).padding.top - 10,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
left: 20,
|
left: 20,
|
||||||
|
@ -9,6 +9,7 @@ import 'package:pilipala/common/widgets/overlay_pop.dart';
|
|||||||
import 'package:pilipala/common/skeleton/video_card_h.dart';
|
import 'package:pilipala/common/skeleton/video_card_h.dart';
|
||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/common/widgets/video_card_h.dart';
|
import 'package:pilipala/common/widgets/video_card_h.dart';
|
||||||
|
import 'package:pilipala/pages/home/index.dart';
|
||||||
import 'package:pilipala/pages/hot/controller.dart';
|
import 'package:pilipala/pages/hot/controller.dart';
|
||||||
import 'package:pilipala/pages/main/index.dart';
|
import 'package:pilipala/pages/main/index.dart';
|
||||||
|
|
||||||
@ -35,6 +36,8 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
|||||||
scrollController = _hotController.scrollController;
|
scrollController = _hotController.scrollController;
|
||||||
StreamController<bool> mainStream =
|
StreamController<bool> mainStream =
|
||||||
Get.find<MainController>().bottomBarStream;
|
Get.find<MainController>().bottomBarStream;
|
||||||
|
StreamController<bool> searchBarStream =
|
||||||
|
Get.find<HomeController>().searchBarStream;
|
||||||
scrollController.addListener(
|
scrollController.addListener(
|
||||||
() {
|
() {
|
||||||
if (scrollController.position.pixels >=
|
if (scrollController.position.pixels >=
|
||||||
@ -49,8 +52,10 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
|||||||
scrollController.position.userScrollDirection;
|
scrollController.position.userScrollDirection;
|
||||||
if (direction == ScrollDirection.forward) {
|
if (direction == ScrollDirection.forward) {
|
||||||
mainStream.add(true);
|
mainStream.add(true);
|
||||||
|
searchBarStream.add(true);
|
||||||
} else if (direction == ScrollDirection.reverse) {
|
} else if (direction == ScrollDirection.reverse) {
|
||||||
mainStream.add(false);
|
mainStream.add(false);
|
||||||
|
searchBarStream.add(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -9,6 +9,7 @@ import 'package:pilipala/common/skeleton/video_card_v.dart';
|
|||||||
import 'package:pilipala/common/widgets/animated_dialog.dart';
|
import 'package:pilipala/common/widgets/animated_dialog.dart';
|
||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/common/widgets/overlay_pop.dart';
|
import 'package:pilipala/common/widgets/overlay_pop.dart';
|
||||||
|
import 'package:pilipala/pages/home/index.dart';
|
||||||
import 'package:pilipala/pages/main/index.dart';
|
import 'package:pilipala/pages/main/index.dart';
|
||||||
import 'package:pilipala/pages/rcmd/index.dart';
|
import 'package:pilipala/pages/rcmd/index.dart';
|
||||||
|
|
||||||
@ -38,6 +39,8 @@ class _LivePageState extends State<LivePage>
|
|||||||
scrollController = _liveController.scrollController;
|
scrollController = _liveController.scrollController;
|
||||||
StreamController<bool> mainStream =
|
StreamController<bool> mainStream =
|
||||||
Get.find<MainController>().bottomBarStream;
|
Get.find<MainController>().bottomBarStream;
|
||||||
|
StreamController<bool> searchBarStream =
|
||||||
|
Get.find<HomeController>().searchBarStream;
|
||||||
scrollController.addListener(
|
scrollController.addListener(
|
||||||
() {
|
() {
|
||||||
if (scrollController.position.pixels >=
|
if (scrollController.position.pixels >=
|
||||||
@ -52,8 +55,10 @@ class _LivePageState extends State<LivePage>
|
|||||||
scrollController.position.userScrollDirection;
|
scrollController.position.userScrollDirection;
|
||||||
if (direction == ScrollDirection.forward) {
|
if (direction == ScrollDirection.forward) {
|
||||||
mainStream.add(true);
|
mainStream.add(true);
|
||||||
|
searchBarStream.add(true);
|
||||||
} else if (direction == ScrollDirection.reverse) {
|
} else if (direction == ScrollDirection.reverse) {
|
||||||
mainStream.add(false);
|
mainStream.add(false);
|
||||||
|
searchBarStream.add(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -55,6 +55,7 @@ class MainController extends GetxController {
|
|||||||
StreamController<bool>.broadcast();
|
StreamController<bool>.broadcast();
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
DateTime? _lastPressedAt;
|
DateTime? _lastPressedAt;
|
||||||
|
late bool hideTabBar;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -62,6 +63,7 @@ class MainController extends GetxController {
|
|||||||
if (setting.get(SettingBoxKey.autoUpdate, defaultValue: false)) {
|
if (setting.get(SettingBoxKey.autoUpdate, defaultValue: false)) {
|
||||||
Utils.checkUpdata();
|
Utils.checkUpdata();
|
||||||
}
|
}
|
||||||
|
hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> onBackPressed(BuildContext context) {
|
Future<bool> onBackPressed(BuildContext context) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@ -142,7 +144,9 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
bottomNavigationBar: StreamBuilder(
|
bottomNavigationBar: StreamBuilder(
|
||||||
stream: _mainController.bottomBarStream.stream,
|
stream: _mainController.hideTabBar
|
||||||
|
? _mainController.bottomBarStream.stream
|
||||||
|
: StreamController<bool>.broadcast().stream,
|
||||||
initialData: true,
|
initialData: true,
|
||||||
builder: (context, AsyncSnapshot snapshot) {
|
builder: (context, AsyncSnapshot snapshot) {
|
||||||
return AnimatedSlide(
|
return AnimatedSlide(
|
||||||
|
@ -11,6 +11,7 @@ import 'package:pilipala/common/widgets/animated_dialog.dart';
|
|||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/common/widgets/overlay_pop.dart';
|
import 'package:pilipala/common/widgets/overlay_pop.dart';
|
||||||
import 'package:pilipala/common/widgets/video_card_v.dart';
|
import 'package:pilipala/common/widgets/video_card_v.dart';
|
||||||
|
import 'package:pilipala/pages/home/index.dart';
|
||||||
import 'package:pilipala/pages/main/index.dart';
|
import 'package:pilipala/pages/main/index.dart';
|
||||||
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
@ -37,6 +38,8 @@ class _RcmdPageState extends State<RcmdPage>
|
|||||||
ScrollController scrollController = _rcmdController.scrollController;
|
ScrollController scrollController = _rcmdController.scrollController;
|
||||||
StreamController<bool> mainStream =
|
StreamController<bool> mainStream =
|
||||||
Get.find<MainController>().bottomBarStream;
|
Get.find<MainController>().bottomBarStream;
|
||||||
|
StreamController<bool> searchBarStream =
|
||||||
|
Get.find<HomeController>().searchBarStream;
|
||||||
scrollController.addListener(
|
scrollController.addListener(
|
||||||
() {
|
() {
|
||||||
if (scrollController.position.pixels >=
|
if (scrollController.position.pixels >=
|
||||||
@ -52,8 +55,10 @@ class _RcmdPageState extends State<RcmdPage>
|
|||||||
scrollController.position.userScrollDirection;
|
scrollController.position.userScrollDirection;
|
||||||
if (direction == ScrollDirection.forward) {
|
if (direction == ScrollDirection.forward) {
|
||||||
mainStream.add(true);
|
mainStream.add(true);
|
||||||
|
searchBarStream.add(true);
|
||||||
} else if (direction == ScrollDirection.reverse) {
|
} else if (direction == ScrollDirection.reverse) {
|
||||||
mainStream.add(false);
|
mainStream.add(false);
|
||||||
|
searchBarStream.add(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -3,6 +3,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/models/common/dynamics_type.dart';
|
import 'package:pilipala/models/common/dynamics_type.dart';
|
||||||
import 'package:pilipala/models/common/reply_sort_type.dart';
|
import 'package:pilipala/models/common/reply_sort_type.dart';
|
||||||
|
import 'package:pilipala/pages/setting/widgets/select_dialog.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
import 'widgets/switch_item.dart';
|
import 'widgets/switch_item.dart';
|
||||||
@ -182,23 +183,21 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
'当前优先展示「${ReplySortType.values[defaultReplySort].titles}」',
|
'当前优先展示「${ReplySortType.values[defaultReplySort].titles}」',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
trailing: PopupMenuButton(
|
onTap: () async {
|
||||||
initialValue: defaultReplySort,
|
int? result = await showDialog(
|
||||||
icon: const Icon(Icons.more_vert_outlined, size: 22),
|
context: context,
|
||||||
onSelected: (item) {
|
builder: (context) {
|
||||||
defaultReplySort = item;
|
return SelectDialog<int>(title: '评论展示', value: defaultReplySort, values: ReplySortType.values.map((e) {
|
||||||
setting.put(SettingBoxKey.replySortType, item);
|
return {'title': e.titles, 'value': e.index};
|
||||||
|
}).toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
defaultReplySort = result;
|
||||||
|
setting.put(SettingBoxKey.replySortType, result);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
}
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
},
|
||||||
for (var i in ReplySortType.values) ...[
|
|
||||||
PopupMenuItem(
|
|
||||||
value: i.index,
|
|
||||||
child: Text(i.titles),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
@ -207,23 +206,21 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
'当前优先展示「${DynamicsType.values[defaultDynamicType].labels}」',
|
'当前优先展示「${DynamicsType.values[defaultDynamicType].labels}」',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
trailing: PopupMenuButton(
|
onTap: () async {
|
||||||
initialValue: defaultDynamicType,
|
int? result = await showDialog(
|
||||||
icon: const Icon(Icons.more_vert_outlined, size: 22),
|
context: context,
|
||||||
onSelected: (item) {
|
builder: (context) {
|
||||||
defaultDynamicType = item;
|
return SelectDialog<int>(title: '动态展示', value: defaultDynamicType, values: DynamicsType.values.map((e) {
|
||||||
setting.put(SettingBoxKey.defaultDynamicType, item);
|
return {'title': e.labels, 'value': e.index};
|
||||||
|
}).toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
defaultDynamicType = result;
|
||||||
|
setting.put(SettingBoxKey.defaultDynamicType, result);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
}
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
},
|
||||||
for (var i in DynamicsType.values) ...[
|
|
||||||
PopupMenuItem(
|
|
||||||
value: i.index,
|
|
||||||
child: Text(i.labels),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
enableFeedback: true,
|
enableFeedback: true,
|
||||||
@ -231,6 +228,7 @@ class _ExtraSettingState extends State<ExtraSetting> {
|
|||||||
title: Text('设置代理', style: titleStyle),
|
title: Text('设置代理', style: titleStyle),
|
||||||
subtitle: Text('设置代理 host:port', style: subTitleStyle),
|
subtitle: Text('设置代理 host:port', style: subTitleStyle),
|
||||||
trailing: Transform.scale(
|
trailing: Transform.scale(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
scale: 0.8,
|
scale: 0.8,
|
||||||
child: Switch(
|
child: Switch(
|
||||||
thumbIcon: MaterialStateProperty.resolveWith<Icon?>(
|
thumbIcon: MaterialStateProperty.resolveWith<Icon?>(
|
||||||
|
@ -44,12 +44,10 @@ class _FontSizeSelectPageState extends State<FontSizeSelectPage> {
|
|||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: Center(
|
||||||
child: Center(
|
child: Text(
|
||||||
child: Text(
|
'当前字体大小:${currentSize == 1.0 ? '默认' : currentSize}',
|
||||||
'当前字体大小:${currentSize == 1.0 ? '默认' : currentSize}',
|
style: TextStyle(fontSize: 14 * currentSize),
|
||||||
style: TextStyle(fontSize: 14 * currentSize),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/models/video/play/quality.dart';
|
import 'package:pilipala/models/video/play/quality.dart';
|
||||||
|
import 'package:pilipala/pages/setting/widgets/select_dialog.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
import 'package:pilipala/services/service_locator.dart';
|
import 'package:pilipala/services/service_locator.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
@ -68,7 +69,7 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => Get.toNamed('/playSpeedSet'),
|
onTap: () => Get.toNamed('/playSpeedSet'),
|
||||||
title: Text('倍速设置', style: titleStyle),
|
title: Text('倍速设置', style: titleStyle),
|
||||||
trailing: const Icon(Icons.arrow_forward_ios, size: 17),
|
subtitle: Text('设置视频播放速度', style: subTitleStyle),
|
||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '开启1080P',
|
title: '开启1080P',
|
||||||
@ -96,7 +97,7 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
),
|
),
|
||||||
const SetSwitchItem(
|
const SetSwitchItem(
|
||||||
title: '自动PiP播放',
|
title: '自动PiP播放',
|
||||||
subTitle: 'app切换至后台时画中画播放',
|
subTitle: '进入后台时画中画播放',
|
||||||
setKey: SettingBoxKey.autoPiP,
|
setKey: SettingBoxKey.autoPiP,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
@ -149,23 +150,21 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
'当前画质${VideoQualityCode.fromCode(defaultVideoQa)!.description!}',
|
'当前画质${VideoQualityCode.fromCode(defaultVideoQa)!.description!}',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
trailing: PopupMenuButton(
|
onTap: () async {
|
||||||
initialValue: defaultVideoQa,
|
int? result = await showDialog(
|
||||||
icon: const Icon(Icons.more_vert_outlined, size: 22),
|
context: context,
|
||||||
onSelected: (item) {
|
builder: (context) {
|
||||||
defaultVideoQa = item;
|
return SelectDialog<int>(title: '默认画质', value: defaultVideoQa, values: VideoQuality.values.reversed.map((e) {
|
||||||
setting.put(SettingBoxKey.defaultVideoQa, item);
|
return {'title': e.description, 'value': e.code};
|
||||||
|
}).toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
defaultVideoQa = result;
|
||||||
|
setting.put(SettingBoxKey.defaultVideoQa, result);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
}
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
},
|
||||||
for (var i in VideoQuality.values.reversed) ...[
|
|
||||||
PopupMenuItem(
|
|
||||||
value: i.code,
|
|
||||||
child: Text(i.description),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
@ -174,23 +173,21 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
'当前音质${AudioQualityCode.fromCode(defaultAudioQa)!.description!}',
|
'当前音质${AudioQualityCode.fromCode(defaultAudioQa)!.description!}',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
trailing: PopupMenuButton(
|
onTap: () async {
|
||||||
initialValue: defaultAudioQa,
|
int? result = await showDialog(
|
||||||
icon: const Icon(Icons.more_vert_outlined, size: 22),
|
context: context,
|
||||||
onSelected: (item) {
|
builder: (context) {
|
||||||
defaultAudioQa = item;
|
return SelectDialog<int>(title: '默认音质', value: defaultAudioQa, values: AudioQuality.values.reversed.map((e) {
|
||||||
setting.put(SettingBoxKey.defaultAudioQa, item);
|
return {'title': e.description, 'value': e.code};
|
||||||
|
}).toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
defaultAudioQa = result;
|
||||||
|
setting.put(SettingBoxKey.defaultAudioQa, result);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
}
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
},
|
||||||
for (var i in AudioQuality.values.reversed) ...[
|
|
||||||
PopupMenuItem(
|
|
||||||
value: i.code,
|
|
||||||
child: Text(i.description),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
@ -199,23 +196,21 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
'当前解码格式${VideoDecodeFormatsCode.fromCode(defaultDecode)!.description!}',
|
'当前解码格式${VideoDecodeFormatsCode.fromCode(defaultDecode)!.description!}',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
trailing: PopupMenuButton(
|
onTap: () async {
|
||||||
initialValue: defaultDecode,
|
String? result = await showDialog(
|
||||||
icon: const Icon(Icons.more_vert_outlined, size: 22),
|
context: context,
|
||||||
onSelected: (item) {
|
builder: (context) {
|
||||||
defaultDecode = item;
|
return SelectDialog<String>(title: '默认解码格式', value: defaultDecode, values: VideoDecodeFormats.values.map((e) {
|
||||||
setting.put(SettingBoxKey.defaultDecode, item);
|
return {'title': e.description, 'value': e.code};
|
||||||
|
}).toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
defaultDecode = result;
|
||||||
|
setting.put(SettingBoxKey.defaultDecode, result);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
}
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
},
|
||||||
for (var i in VideoDecodeFormats.values) ...[
|
|
||||||
PopupMenuItem(
|
|
||||||
value: i.code,
|
|
||||||
child: Text(i.description),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
@ -224,23 +219,21 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
'当前全屏方式:${FullScreenModeCode.fromCode(defaultFullScreenMode)!.description}',
|
'当前全屏方式:${FullScreenModeCode.fromCode(defaultFullScreenMode)!.description}',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
trailing: PopupMenuButton(
|
onTap: () async {
|
||||||
initialValue: defaultFullScreenMode,
|
int? result = await showDialog(
|
||||||
icon: const Icon(Icons.more_vert_outlined, size: 22),
|
context: context,
|
||||||
onSelected: (item) {
|
builder: (context) {
|
||||||
defaultFullScreenMode = item;
|
return SelectDialog<int>(title: '默认全屏方式', value: defaultFullScreenMode, values: FullScreenMode.values.map((e) {
|
||||||
setting.put(SettingBoxKey.fullScreenMode, item);
|
return {'title': e.description, 'value': e.code};
|
||||||
|
}).toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
defaultFullScreenMode = result;
|
||||||
|
setting.put(SettingBoxKey.fullScreenMode, result);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
}
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
},
|
||||||
for (var i in FullScreenMode.values) ...[
|
|
||||||
PopupMenuItem(
|
|
||||||
value: i.code,
|
|
||||||
child: Text(i.description),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
@ -249,23 +242,21 @@ class _PlaySettingState extends State<PlaySetting> {
|
|||||||
'当前展示方式:${BtmProgresBehaviorCode.fromCode(defaultBtmProgressBehavior)!.description}',
|
'当前展示方式:${BtmProgresBehaviorCode.fromCode(defaultBtmProgressBehavior)!.description}',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
trailing: PopupMenuButton(
|
onTap: () async {
|
||||||
initialValue: defaultBtmProgressBehavior,
|
int? result = await showDialog(
|
||||||
icon: const Icon(Icons.more_vert_outlined, size: 22),
|
context: context,
|
||||||
onSelected: (item) {
|
builder: (context) {
|
||||||
defaultBtmProgressBehavior = item;
|
return SelectDialog<int>(title: '底部进度条展示', value: defaultBtmProgressBehavior, values: BtmProgresBehavior.values.map((e) {
|
||||||
setting.put(SettingBoxKey.btmProgressBehavior, item);
|
return {'title': e.description, 'value': e.code};
|
||||||
|
}).toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
defaultBtmProgressBehavior = result;
|
||||||
|
setting.put(SettingBoxKey.btmProgressBehavior, result);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
}
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
},
|
||||||
for (var i in BtmProgresBehavior.values) ...[
|
|
||||||
PopupMenuItem(
|
|
||||||
value: i.code,
|
|
||||||
child: Text(i.description),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/models/common/theme_type.dart';
|
import 'package:pilipala/models/common/theme_type.dart';
|
||||||
|
import 'package:pilipala/pages/setting/pages/color_select.dart';
|
||||||
|
import 'package:pilipala/pages/setting/widgets/select_dialog.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
@ -18,6 +20,8 @@ class StyleSetting extends StatefulWidget {
|
|||||||
|
|
||||||
class _StyleSettingState extends State<StyleSetting> {
|
class _StyleSettingState extends State<StyleSetting> {
|
||||||
final SettingController settingController = Get.put(SettingController());
|
final SettingController settingController = Get.put(SettingController());
|
||||||
|
final ColorSelectController colorSelectController = Get.put(ColorSelectController());
|
||||||
|
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
late int picQuality;
|
late int picQuality;
|
||||||
late ThemeType _tempThemeValue;
|
late ThemeType _tempThemeValue;
|
||||||
@ -56,6 +60,7 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
title: const Text('震动反馈'),
|
title: const Text('震动反馈'),
|
||||||
subtitle: Text('请确定手机设置中已开启震动反馈', style: subTitleStyle),
|
subtitle: Text('请确定手机设置中已开启震动反馈', style: subTitleStyle),
|
||||||
trailing: Transform.scale(
|
trailing: Transform.scale(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
scale: 0.8,
|
scale: 0.8,
|
||||||
child: Switch(
|
child: Switch(
|
||||||
thumbIcon: MaterialStateProperty.resolveWith<Icon?>(
|
thumbIcon: MaterialStateProperty.resolveWith<Icon?>(
|
||||||
@ -83,37 +88,42 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
setKey: SettingBoxKey.enableMYBar,
|
setKey: SettingBoxKey.enableMYBar,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
// SetSwitchItem(
|
const SetSwitchItem(
|
||||||
// title: '首页单列',
|
title: '首页顶栏收起',
|
||||||
// subTitle: '每行展示一个内容卡片',
|
subTitle: '首页列表滑动时,收起顶栏',
|
||||||
// setKey: SettingBoxKey.enableSingleRow,
|
setKey: SettingBoxKey.hideSearchBar,
|
||||||
// defaultVal: false,
|
defaultVal: true,
|
||||||
// callFn: (val) => {SmartDialog.showToast('下次启动时生效')},
|
needReboot: true,
|
||||||
// ),
|
),
|
||||||
|
const SetSwitchItem(
|
||||||
|
title: '首页底栏收起',
|
||||||
|
subTitle: '首页列表滑动时,收起底栏',
|
||||||
|
setKey: SettingBoxKey.hideTabBar,
|
||||||
|
defaultVal: true,
|
||||||
|
needReboot: true,
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
|
onTap: () async {
|
||||||
|
int? result = await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return SelectDialog<int>(title: '自定义列数', value: defaultCustomRows, values: [1, 2, 3, 4, 5].map((e) {
|
||||||
|
return {'title': '$e 列', 'value': e};
|
||||||
|
}).toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
defaultCustomRows = result;
|
||||||
|
setting.put(SettingBoxKey.customRows, result);
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
},
|
||||||
dense: false,
|
dense: false,
|
||||||
title: Text('自定义列数', style: titleStyle),
|
title: Text('自定义列数', style: titleStyle),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'当前列数',
|
'当前列数 $defaultCustomRows 列',
|
||||||
style: subTitleStyle,
|
style: subTitleStyle,
|
||||||
),
|
),
|
||||||
trailing: PopupMenuButton(
|
|
||||||
initialValue: defaultCustomRows,
|
|
||||||
icon: const Icon(Icons.more_vert_outlined, size: 22),
|
|
||||||
onSelected: (item) {
|
|
||||||
defaultCustomRows = item;
|
|
||||||
setting.put(SettingBoxKey.customRows, item);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
|
||||||
for (var i in [1, 2, 3, 4, 5]) ...[
|
|
||||||
PopupMenuItem(
|
|
||||||
value: i,
|
|
||||||
child: Text(i.toString()),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
@ -169,88 +179,58 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
},
|
},
|
||||||
title: Text('图片质量', style: titleStyle),
|
title: Text('图片质量', style: titleStyle),
|
||||||
subtitle: Text('选择合适的图片清晰度,上限100%', style: subTitleStyle),
|
subtitle: Text('选择合适的图片清晰度,上限100%', style: subTitleStyle),
|
||||||
trailing: Obx(
|
trailing: Padding(
|
||||||
() => Text(
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
'${settingController.picQuality.value}%',
|
child: Obx(
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
() => Text(
|
||||||
|
'${settingController.picQuality.value}%',
|
||||||
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
showDialog(
|
ThemeType? result = await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return SelectDialog<ThemeType>(title: '主题模式', value: _tempThemeValue, values: ThemeType.values.map((e) {
|
||||||
title: const Text('主题模式'),
|
return {'title': e.description, 'value': e};
|
||||||
contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12),
|
}).toList());
|
||||||
content: StatefulBuilder(
|
|
||||||
builder: (context, StateSetter setState) {
|
|
||||||
return Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
for (var i in ThemeType.values) ...[
|
|
||||||
RadioListTile(
|
|
||||||
value: i,
|
|
||||||
title: Text(i.description, style: titleStyle),
|
|
||||||
groupValue: _tempThemeValue,
|
|
||||||
onChanged: (ThemeType? value) {
|
|
||||||
setState(() {
|
|
||||||
_tempThemeValue = i;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
child: Text(
|
|
||||||
'取消',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.outline),
|
|
||||||
)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
settingController.themeType.value = _tempThemeValue;
|
|
||||||
setting.put(
|
|
||||||
SettingBoxKey.themeMode, _tempThemeValue.code);
|
|
||||||
Get.forceAppUpdate();
|
|
||||||
Get.back();
|
|
||||||
},
|
|
||||||
child: const Text('确定'))
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
if (result != null) {
|
||||||
|
_tempThemeValue = result;
|
||||||
|
settingController.themeType.value = result;
|
||||||
|
setting.put(
|
||||||
|
SettingBoxKey.themeMode, result.code);
|
||||||
|
Get.forceAppUpdate();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
title: Text('主题模式', style: titleStyle),
|
title: Text('主题模式', style: titleStyle),
|
||||||
subtitle: Obx(() => Text(
|
subtitle: Obx(() => Text(
|
||||||
'当前模式:${settingController.themeType.value.description}',
|
'当前模式:${settingController.themeType.value.description}',
|
||||||
style: subTitleStyle)),
|
style: subTitleStyle)),
|
||||||
trailing: const Icon(Icons.arrow_right_alt_outlined),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => Get.toNamed('/colorSetting'),
|
onTap: () => Get.toNamed('/colorSetting'),
|
||||||
title: Text('应用主题', style: titleStyle),
|
title: Text('应用主题', style: titleStyle),
|
||||||
trailing: const Icon(Icons.arrow_forward_ios, size: 17),
|
subtitle: Obx(() => Text(
|
||||||
|
'当前主题:${colorSelectController.type.value == 0 ? '动态取色': '指定颜色'}',
|
||||||
|
style: subTitleStyle)),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => Get.toNamed('/fontSizeSetting'),
|
onTap: () => Get.toNamed('/fontSizeSetting'),
|
||||||
title: Text('字体大小', style: titleStyle),
|
title: Text('字体大小', style: titleStyle),
|
||||||
trailing: const Icon(Icons.arrow_forward_ios, size: 17),
|
|
||||||
),
|
),
|
||||||
if (Platform.isAndroid)
|
if (Platform.isAndroid)
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => Get.toNamed('/displayModeSetting'),
|
onTap: () => Get.toNamed('/displayModeSetting'),
|
||||||
title: Text('屏幕帧率', style: titleStyle),
|
title: Text('屏幕帧率', style: titleStyle),
|
||||||
trailing: const Icon(Icons.arrow_forward_ios, size: 17),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
68
lib/pages/setting/widgets/select_dialog.dart
Normal file
68
lib/pages/setting/widgets/select_dialog.dart
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pilipala/models/common/theme_type.dart';
|
||||||
|
|
||||||
|
class SelectDialog<T> extends StatefulWidget {
|
||||||
|
final T value;
|
||||||
|
final String title;
|
||||||
|
final List<dynamic> values;
|
||||||
|
const SelectDialog({super.key, required this.value, required this.values, required this.title});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SelectDialogState<T> createState() => _SelectDialogState<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SelectDialogState<T> extends State<SelectDialog<T>> {
|
||||||
|
|
||||||
|
late T _tempValue;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_tempValue = widget.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
TextStyle titleStyle = Theme.of(context).textTheme.titleMedium!;
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(widget.title),
|
||||||
|
contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12),
|
||||||
|
content: StatefulBuilder(
|
||||||
|
builder: (context, StateSetter setState) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
for (var i in widget.values) ...[
|
||||||
|
RadioListTile(
|
||||||
|
value: i['value'],
|
||||||
|
title: Text(i['title'], style: titleStyle),
|
||||||
|
groupValue: _tempValue,
|
||||||
|
onChanged: (value) {
|
||||||
|
print(value);
|
||||||
|
setState(() {
|
||||||
|
_tempValue = value as T;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: Text(
|
||||||
|
'取消',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.outline),
|
||||||
|
)),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context, _tempValue),
|
||||||
|
child: const Text('确定'))
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
@ -9,6 +10,7 @@ class SetSwitchItem extends StatefulWidget {
|
|||||||
final String? setKey;
|
final String? setKey;
|
||||||
final bool? defaultVal;
|
final bool? defaultVal;
|
||||||
final Function? callFn;
|
final Function? callFn;
|
||||||
|
final bool? needReboot;
|
||||||
|
|
||||||
const SetSwitchItem({
|
const SetSwitchItem({
|
||||||
this.title,
|
this.title,
|
||||||
@ -16,6 +18,7 @@ class SetSwitchItem extends StatefulWidget {
|
|||||||
this.setKey,
|
this.setKey,
|
||||||
this.defaultVal,
|
this.defaultVal,
|
||||||
this.callFn,
|
this.callFn,
|
||||||
|
this.needReboot,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -43,6 +46,9 @@ class _SetSwitchItemState extends State<SetSwitchItem> {
|
|||||||
if (widget.callFn != null) {
|
if (widget.callFn != null) {
|
||||||
widget.callFn!.call(val);
|
widget.callFn!.call(val);
|
||||||
}
|
}
|
||||||
|
if (widget.needReboot != null && widget.needReboot!) {
|
||||||
|
SmartDialog.showToast('重启生效');
|
||||||
|
}
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +67,7 @@ class _SetSwitchItemState extends State<SetSwitchItem> {
|
|||||||
? Text(widget.subTitle!, style: subTitleStyle)
|
? Text(widget.subTitle!, style: subTitleStyle)
|
||||||
: null,
|
: null,
|
||||||
trailing: Transform.scale(
|
trailing: Transform.scale(
|
||||||
|
alignment: Alignment.centerRight, // 缩放Switch的大小后保持右侧对齐, 避免右侧空隙过大
|
||||||
scale: 0.8,
|
scale: 0.8,
|
||||||
child: Switch(
|
child: Switch(
|
||||||
thumbIcon: MaterialStateProperty.resolveWith<Icon?>(
|
thumbIcon: MaterialStateProperty.resolveWith<Icon?>(
|
||||||
|
@ -15,6 +15,7 @@ import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart';
|
|||||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||||
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
|
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
import 'package:pilipala/http/danmaku.dart';
|
||||||
|
|
||||||
class HeaderControl extends StatefulWidget implements PreferredSizeWidget {
|
class HeaderControl extends StatefulWidget implements PreferredSizeWidget {
|
||||||
final PlPlayerController? controller;
|
final PlPlayerController? controller;
|
||||||
@ -179,6 +180,84 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 发送弹幕
|
||||||
|
void showShootDanmakuSheet() {
|
||||||
|
final TextEditingController textController = TextEditingController();
|
||||||
|
bool isSending = false; // 追踪是否正在发送
|
||||||
|
showDialog(
|
||||||
|
context: Get.context!,
|
||||||
|
builder: (context) {
|
||||||
|
// TODO: 支持更多类型和颜色的弹幕
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('发送弹幕(测试)'),
|
||||||
|
content: StatefulBuilder(builder: (context, StateSetter setState) {
|
||||||
|
return TextField(
|
||||||
|
controller: textController,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
child: Text(
|
||||||
|
'取消',
|
||||||
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
StatefulBuilder(builder: (context, StateSetter setState) {
|
||||||
|
return TextButton(
|
||||||
|
onPressed: isSending
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
String msg = textController.text;
|
||||||
|
if (msg.isEmpty) {
|
||||||
|
SmartDialog.showToast('弹幕内容不能为空');
|
||||||
|
return;
|
||||||
|
} else if (msg.length > 100) {
|
||||||
|
SmartDialog.showToast('弹幕内容不能超过100个字符');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
isSending = true; // 开始发送,更新状态
|
||||||
|
});
|
||||||
|
//修改按钮文字
|
||||||
|
// SmartDialog.showToast('弹幕发送中,\n$msg');
|
||||||
|
dynamic res = await DanmakaHttp.shootDanmaku(
|
||||||
|
oid: widget.videoDetailCtr!.cid!.value,
|
||||||
|
msg: textController.text,
|
||||||
|
bvid: widget.videoDetailCtr!.bvid!,
|
||||||
|
progress:
|
||||||
|
widget.controller!.position.value.inMilliseconds,
|
||||||
|
type: 1,
|
||||||
|
);
|
||||||
|
setState(() {
|
||||||
|
isSending = false; // 发送结束,更新状态
|
||||||
|
});
|
||||||
|
if (res['status']) {
|
||||||
|
SmartDialog.showToast('发送成功');
|
||||||
|
// 发送成功,自动预览该弹幕,避免重新请求
|
||||||
|
// TODO: 暂停状态下预览弹幕仍会移动与计时,可考虑添加到dmSegList或其他方式实现
|
||||||
|
widget.controller!.danmakuController!.addItems([
|
||||||
|
DanmakuItem(
|
||||||
|
msg,
|
||||||
|
color: Colors.white,
|
||||||
|
time: widget.controller!.position.value.inMilliseconds,
|
||||||
|
type: DanmakuItemType.scroll,
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
Get.back();
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast('发送失败,错误信息为${res['msg']}');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(isSending ? '发送中...' : '发送'),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// 选择倍速
|
/// 选择倍速
|
||||||
void showSetSpeedSheet() {
|
void showSetSpeedSheet() {
|
||||||
double currentSpeed = widget.controller!.playbackSpeed;
|
double currentSpeed = widget.controller!.playbackSpeed;
|
||||||
@ -825,6 +904,20 @@ class _HeaderControlState extends State<HeaderControl> {
|
|||||||
// ),
|
// ),
|
||||||
// fuc: () => _.screenshot(),
|
// fuc: () => _.screenshot(),
|
||||||
// ),
|
// ),
|
||||||
|
SizedBox(
|
||||||
|
width: 56,
|
||||||
|
height: 34,
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
|
),
|
||||||
|
onPressed: () => showShootDanmakuSheet(),
|
||||||
|
child: const Text(
|
||||||
|
'发弹幕',
|
||||||
|
style: textStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 34,
|
width: 34,
|
||||||
height: 34,
|
height: 34,
|
||||||
|
@ -139,6 +139,8 @@ class SettingBoxKey {
|
|||||||
static const String displayMode = 'displayMode';
|
static const String displayMode = 'displayMode';
|
||||||
static const String customRows = 'customRows'; // 自定义列
|
static const String customRows = 'customRows'; // 自定义列
|
||||||
static const String enableMYBar = 'enableMYBar';
|
static const String enableMYBar = 'enableMYBar';
|
||||||
|
static const String hideSearchBar = 'hideSearchBar'; // 收起顶栏
|
||||||
|
static const String hideTabBar = 'hideTabBar'; // 收起底栏
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalCacheKey {
|
class LocalCacheKey {
|
||||||
|
@ -30,7 +30,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
FlutterVolumeControllerPlugin.register(with: registry.registrar(forPlugin: "FlutterVolumeControllerPlugin"))
|
FlutterVolumeControllerPlugin.register(with: registry.registrar(forPlugin: "FlutterVolumeControllerPlugin"))
|
||||||
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
|
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
|
||||||
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
|
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
|
||||||
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin"))
|
ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin"))
|
||||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||||
|
692
pubspec.lock
692
pubspec.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user