Merge branch 'main' into feature-collections

This commit is contained in:
guozhigq
2024-04-13 23:02:11 +08:00
44 changed files with 642 additions and 379 deletions

View File

@ -4,7 +4,7 @@ on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: branches:
- "main" - "never"
paths-ignore: paths-ignore:
- "**.md" - "**.md"
- "**.txt" - "**.txt"
@ -12,6 +12,7 @@ on:
- ".idea/**" - ".idea/**"
- "!.github/workflows/**" - "!.github/workflows/**"
jobs: jobs:
update_version: update_version:
name: Read and update version name: Read and update version

View File

@ -490,8 +490,11 @@ class Api {
/// 我的订阅 /// 我的订阅
static const userSubFolder = '/x/v3/fav/folder/collected/list'; static const userSubFolder = '/x/v3/fav/folder/collected/list';
/// 我的订阅详情 /// 我的订阅详情 type 21
static const userSubFolderDetail = '/x/space/fav/season/list'; static const userSeasonList = '/x/space/fav/season/list';
/// 我的订阅详情 type 11
static const userResourceList = '/x/v3/fav/resource/list';
/// 表情 /// 表情
static const emojiList = '/x/emote/user/panel/web'; static const emojiList = '/x/emote/user/panel/web';

View File

@ -330,12 +330,12 @@ class UserHttp {
} }
} }
static Future userSubFolderDetail({ static Future userSeasonList({
required int seasonId, required int seasonId,
required int pn, required int pn,
required int ps, required int ps,
}) async { }) async {
var res = await Request().get(Api.userSubFolderDetail, data: { var res = await Request().get(Api.userSeasonList, data: {
'season_id': seasonId, 'season_id': seasonId,
'ps': ps, 'ps': ps,
'pn': pn, 'pn': pn,
@ -350,6 +350,35 @@ class UserHttp {
} }
} }
static Future userResourceList({
required int seasonId,
required int pn,
required int ps,
}) async {
var res = await Request().get(Api.userResourceList, data: {
'media_id': seasonId,
'ps': ps,
'pn': pn,
'keyword': '',
'order': 'mtime',
'type': 0,
'tid': 0,
'platform': 'web',
});
if (res.data['code'] == 0) {
try {
return {
'status': true,
'data': SubDetailModelData.fromJson(res.data['data'])
};
} catch (err) {
return {'status': false, 'msg': err};
}
} else {
return {'status': false, 'msg': res.data['message']};
}
}
// 取消订阅 // 取消订阅
static Future cancelSub({required int seasonId}) async { static Future cancelSub({required int seasonId}) async {
var res = await Request().post( var res = await Request().post(

View File

@ -20,6 +20,7 @@ import 'package:pilipala/services/disable_battery_opt.dart';
import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/services/service_locator.dart';
import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/app_scheme.dart';
import 'package:pilipala/utils/data.dart'; import 'package:pilipala/utils/data.dart';
import 'package:pilipala/utils/global_data.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc. import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc.
import 'package:pilipala/utils/recommend_filter.dart'; import 'package:pilipala/utils/recommend_filter.dart';
@ -64,14 +65,8 @@ void main() async {
}, },
); );
// 小白条、导航栏沉浸
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
systemNavigationBarColor: Colors.transparent,
systemNavigationBarDividerColor: Colors.transparent,
statusBarColor: Colors.transparent,
));
Data.init(); Data.init();
GlobalData();
PiliSchame.init(); PiliSchame.init();
DisableBatteryOpt(); DisableBatteryOpt();
}); });
@ -134,45 +129,51 @@ class MyApp extends StatelessWidget {
brightness: Brightness.dark, brightness: Brightness.dark,
); );
} }
final SnackBarThemeData snackBarThemeData = SnackBarThemeData(
actionTextColor: darkColorScheme.primary,
backgroundColor: darkColorScheme.secondaryContainer,
closeIconColor: darkColorScheme.secondary,
contentTextStyle: TextStyle(color: darkColorScheme.secondary),
elevation: 20,
);
ThemeData themeData = ThemeData(
// fontFamily: 'HarmonyOS',
colorScheme: currentThemeValue == ThemeType.dark
? darkColorScheme
: lightColorScheme,
snackBarTheme: snackBarThemeData,
pageTransitionsTheme: const PageTransitionsTheme(
builders: <TargetPlatform, PageTransitionsBuilder>{
TargetPlatform.android: ZoomPageTransitionsBuilder(
allowEnterRouteSnapshotting: false,
),
},
),
);
// 小白条、导航栏沉浸
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: GlobalData().enableMYBar
? const Color(0x00010000)
: themeData.canvasColor,
systemNavigationBarDividerColor: GlobalData().enableMYBar
? const Color(0x00010000)
: themeData.canvasColor,
systemNavigationBarIconBrightness: currentThemeValue == ThemeType.dark
? Brightness.light
: Brightness.dark,
statusBarColor: Colors.transparent,
));
// 图片缓存 // 图片缓存
// PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20; // PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20;
return GetMaterialApp( return GetMaterialApp(
title: 'PiLiPaLa', title: 'PiLiPaLa',
theme: ThemeData( theme: themeData,
// fontFamily: 'HarmonyOS', darkTheme: themeData,
colorScheme: currentThemeValue == ThemeType.dark
? darkColorScheme
: lightColorScheme,
useMaterial3: true,
snackBarTheme: SnackBarThemeData(
actionTextColor: lightColorScheme.primary,
backgroundColor: lightColorScheme.secondaryContainer,
closeIconColor: lightColorScheme.secondary,
contentTextStyle: TextStyle(color: lightColorScheme.secondary),
elevation: 20,
),
pageTransitionsTheme: const PageTransitionsTheme(
builders: <TargetPlatform, PageTransitionsBuilder>{
TargetPlatform.android: ZoomPageTransitionsBuilder(
allowEnterRouteSnapshotting: false,
),
},
),
),
darkTheme: ThemeData(
// fontFamily: 'HarmonyOS',
colorScheme: currentThemeValue == ThemeType.light
? lightColorScheme
: darkColorScheme,
useMaterial3: true,
snackBarTheme: SnackBarThemeData(
actionTextColor: darkColorScheme.primary,
backgroundColor: darkColorScheme.secondaryContainer,
closeIconColor: darkColorScheme.secondary,
contentTextStyle: TextStyle(color: darkColorScheme.secondary),
elevation: 20,
),
),
localizationsDelegates: const [ localizationsDelegates: const [
GlobalCupertinoLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate, GlobalMaterialLocalizations.delegate,

View File

@ -1,5 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../pages/dynamics/index.dart';
import '../../pages/home/index.dart';
import '../../pages/media/index.dart';
import '../../pages/rank/index.dart';
List defaultNavigationBars = [ List defaultNavigationBars = [
{ {
'id': 0, 'id': 0,
@ -13,6 +18,7 @@ List defaultNavigationBars = [
), ),
'label': "首页", 'label': "首页",
'count': 0, 'count': 0,
'page': const HomePage(),
}, },
{ {
'id': 1, 'id': 1,
@ -26,6 +32,7 @@ List defaultNavigationBars = [
), ),
'label': "排行榜", 'label': "排行榜",
'count': 0, 'count': 0,
'page': const RankPage(),
}, },
{ {
'id': 2, 'id': 2,
@ -39,6 +46,7 @@ List defaultNavigationBars = [
), ),
'label': "动态", 'label': "动态",
'count': 0, 'count': 0,
'page': const DynamicsPage(),
}, },
{ {
'id': 3, 'id': 3,
@ -52,5 +60,6 @@ List defaultNavigationBars = [
), ),
'label': "媒体库", 'label': "媒体库",
'count': 0, 'count': 0,
'page': const MediaPage(),
} }
]; ];

View File

@ -2,13 +2,11 @@ import 'dart:async';
import 'package:easy_debounce/easy_throttle.dart'; import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:nil/nil.dart'; import 'package:nil/nil.dart';
import 'package: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/utils/main_stream.dart';
import 'package:pilipala/pages/main/index.dart';
import 'controller.dart'; import 'controller.dart';
import 'widgets/bangumu_card_v.dart'; import 'widgets/bangumu_card_v.dart';
@ -34,10 +32,6 @@ class _BangumiPageState extends State<BangumiPage>
void initState() { void initState() {
super.initState(); super.initState();
scrollController = _bangumidController.scrollController; scrollController = _bangumidController.scrollController;
StreamController<bool> mainStream =
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(
@ -49,16 +43,7 @@ class _BangumiPageState extends State<BangumiPage>
_bangumidController.onLoad(); _bangumidController.onLoad();
}); });
} }
handleScrollEvent(scrollController);
final ScrollDirection direction =
scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
mainStream.add(true);
searchBarStream.add(true);
} else if (direction == ScrollDirection.reverse) {
mainStream.add(false);
searchBarStream.add(false);
}
}, },
); );
} }

View File

@ -92,11 +92,15 @@ class _BangumiPanelState extends State<BangumiPanel> {
// 在回调函数中获取更新后的状态 // 在回调函数中获取更新后的状态
final double offset = min((currentIndex * 150) - 75, final double offset = min((currentIndex * 150) - 75,
listViewScrollCtr.position.maxScrollExtent); listViewScrollCtr.position.maxScrollExtent);
listViewScrollCtr.animateTo( if (currentIndex.value == 0) {
offset, listViewScrollCtr.jumpTo(0);
duration: const Duration(milliseconds: 300), } else {
curve: Curves.easeInOut, listViewScrollCtr.animateTo(
); offset,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
}); });
} }

111
lib/pages/dlna/index.dart Normal file
View File

@ -0,0 +1,111 @@
import 'dart:async';
import 'package:dlna_dart/dlna.dart';
import 'package:flutter/material.dart';
class LiveDlnaPage extends StatefulWidget {
final String datasource;
const LiveDlnaPage({Key? key, required this.datasource}) : super(key: key);
@override
State<LiveDlnaPage> createState() => _LiveDlnaPageState();
}
class _LiveDlnaPageState extends State<LiveDlnaPage> {
final Map<String, DLNADevice> _deviceList = {};
final DLNAManager searcher = DLNAManager();
late final Timer stopSearchTimer;
String selectDeviceKey = '';
bool isSearching = true;
DLNADevice? get device => _deviceList[selectDeviceKey];
@override
void initState() {
stopSearchTimer = Timer(const Duration(seconds: 20), () {
setState(() => isSearching = false);
searcher.stop();
});
searcher.stop();
startSearch();
super.initState();
}
@override
void dispose() {
super.dispose();
searcher.stop();
stopSearchTimer.cancel();
}
void startSearch() async {
// clear old devices
isSearching = true;
selectDeviceKey = '';
_deviceList.clear();
setState(() {});
// start search server
final m = await searcher.start();
m.devices.stream.listen((deviceList) {
deviceList.forEach((key, value) {
_deviceList[key] = value;
});
setState(() {});
});
// close the server, the closed server can be start by call searcher.start()
}
void selectDevice(String key) {
if (selectDeviceKey.isNotEmpty) device?.pause();
selectDeviceKey = key;
device?.setUrl(widget.datasource);
device?.play();
setState(() {});
}
@override
Widget build(BuildContext context) {
Widget cur;
if (isSearching && _deviceList.isEmpty) {
cur = const Center(child: CircularProgressIndicator());
} else if (_deviceList.isEmpty) {
cur = Center(
child: Text(
'没有找到设备',
style: Theme.of(context).textTheme.bodyLarge,
),
);
} else {
cur = ListView(
children: _deviceList.keys
.map<Widget>((key) => ListTile(
contentPadding: const EdgeInsets.all(2),
title: Text(_deviceList[key]!.info.friendlyName),
subtitle: Text(key),
onTap: () => selectDevice(key),
))
.toList(),
);
}
return AlertDialog(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('查找设备'),
IconButton(
onPressed: startSearch,
icon: const Icon(Icons.refresh_rounded),
),
],
),
content: SizedBox(
height: 200,
width: 200,
child: cur,
),
);
}
}

View File

@ -194,7 +194,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
centerTitle: false, centerTitle: false,
titleSpacing: 0, titleSpacing: 0,
title: StreamBuilder( title: StreamBuilder(
stream: titleStreamC.stream, stream: titleStreamC.stream.distinct(),
initialData: false, initialData: false,
builder: (context, AsyncSnapshot snapshot) { builder: (context, AsyncSnapshot snapshot) {
return AnimatedOpacity( return AnimatedOpacity(

View File

@ -3,15 +3,14 @@ import 'dart:async';
import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart'; import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart';
import 'package:easy_debounce/easy_throttle.dart'; import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/common/skeleton/dynamic_card.dart'; import 'package:pilipala/common/skeleton/dynamic_card.dart';
import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/common/widgets/no_data.dart';
import 'package:pilipala/models/dynamics/result.dart'; import 'package:pilipala/models/dynamics/result.dart';
import 'package:pilipala/pages/main/index.dart';
import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/main_stream.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import '../mine/controller.dart'; import '../mine/controller.dart';
@ -44,8 +43,6 @@ class _DynamicsPageState extends State<DynamicsPage>
_futureBuilderFuture = _dynamicsController.queryFollowDynamic(); _futureBuilderFuture = _dynamicsController.queryFollowDynamic();
_futureBuilderFutureUp = _dynamicsController.queryFollowUp(); _futureBuilderFutureUp = _dynamicsController.queryFollowUp();
scrollController = _dynamicsController.scrollController; scrollController = _dynamicsController.scrollController;
StreamController<bool> mainStream =
Get.find<MainController>().bottomBarStream;
scrollController.addListener( scrollController.addListener(
() async { () async {
if (scrollController.position.pixels >= if (scrollController.position.pixels >=
@ -55,14 +52,7 @@ class _DynamicsPageState extends State<DynamicsPage>
_dynamicsController.queryFollowDynamic(type: 'onLoad'); _dynamicsController.queryFollowDynamic(type: 'onLoad');
}); });
} }
handleScrollEvent(scrollController);
final ScrollDirection direction =
scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
mainStream.add(true);
} else if (direction == ScrollDirection.reverse) {
mainStream.add(false);
}
}, },
); );

View File

@ -67,7 +67,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
pinned: true, pinned: true,
titleSpacing: 0, titleSpacing: 0,
title: StreamBuilder( title: StreamBuilder(
stream: titleStreamC.stream, stream: titleStreamC.stream.distinct(),
initialData: false, initialData: false,
builder: (context, AsyncSnapshot snapshot) { builder: (context, AsyncSnapshot snapshot) {
return AnimatedOpacity( return AnimatedOpacity(

View File

@ -35,7 +35,7 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
userLogin.value = userInfo != null; userLogin.value = userInfo != null;
userFace.value = userInfo != null ? userInfo.face : ''; userFace.value = userInfo != null ? userInfo.face : '';
hideSearchBar = hideSearchBar =
setting.get(SettingBoxKey.hideSearchBar, defaultValue: true); setting.get(SettingBoxKey.hideSearchBar, defaultValue: false);
if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) { if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) {
searchDefault(); searchDefault();
} }

View File

@ -171,7 +171,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StreamBuilder( return StreamBuilder(
stream: stream, stream: stream!.distinct(),
initialData: true, initialData: true,
builder: (BuildContext context, AsyncSnapshot snapshot) { builder: (BuildContext context, AsyncSnapshot snapshot) {
final RxBool isUserLoggedIn = ctr!.userLogin; final RxBool isUserLoggedIn = ctr!.userLogin;

View File

@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/animated_dialog.dart';
@ -9,9 +8,8 @@ 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/utils/main_stream.dart';
class HotPage extends StatefulWidget { class HotPage extends StatefulWidget {
const HotPage({Key? key}) : super(key: key); const HotPage({Key? key}) : super(key: key);
@ -34,10 +32,6 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
super.initState(); super.initState();
_futureBuilderFuture = _hotController.queryHotFeed('init'); _futureBuilderFuture = _hotController.queryHotFeed('init');
scrollController = _hotController.scrollController; scrollController = _hotController.scrollController;
StreamController<bool> mainStream =
Get.find<MainController>().bottomBarStream;
StreamController<bool> searchBarStream =
Get.find<HomeController>().searchBarStream;
scrollController.addListener( scrollController.addListener(
() { () {
if (scrollController.position.pixels >= if (scrollController.position.pixels >=
@ -47,16 +41,7 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
_hotController.onLoad(); _hotController.onLoad();
} }
} }
handleScrollEvent(scrollController);
final ScrollDirection direction =
scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
mainStream.add(true);
searchBarStream.add(true);
} else if (direction == ScrollDirection.reverse) {
mainStream.add(false);
searchBarStream.add(false);
}
}, },
); );
} }

View File

@ -2,15 +2,13 @@ import 'dart:async';
import 'package:easy_debounce/easy_throttle.dart'; import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/skeleton/video_card_v.dart'; 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/utils/main_stream.dart';
import 'package:pilipala/pages/main/index.dart';
import 'controller.dart'; import 'controller.dart';
import 'widgets/live_item.dart'; import 'widgets/live_item.dart';
@ -36,10 +34,6 @@ class _LivePageState extends State<LivePage>
super.initState(); super.initState();
_futureBuilderFuture = _liveController.queryLiveList('init'); _futureBuilderFuture = _liveController.queryLiveList('init');
scrollController = _liveController.scrollController; scrollController = _liveController.scrollController;
StreamController<bool> mainStream =
Get.find<MainController>().bottomBarStream;
StreamController<bool> searchBarStream =
Get.find<HomeController>().searchBarStream;
scrollController.addListener( scrollController.addListener(
() { () {
if (scrollController.position.pixels >= if (scrollController.position.pixels >=
@ -49,16 +43,7 @@ class _LivePageState extends State<LivePage>
_liveController.onLoad(); _liveController.onLoad();
}); });
} }
handleScrollEvent(scrollController);
final ScrollDirection direction =
scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
mainStream.add(true);
searchBarStream.add(true);
} else if (direction == ScrollDirection.reverse) {
mainStream.add(false);
searchBarStream.add(false);
}
}, },
); );
} }

View File

@ -62,6 +62,11 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
controller: plPlayerController, controller: plPlayerController,
liveRoomCtr: _liveRoomController, liveRoomCtr: _liveRoomController,
floating: floating, floating: floating,
onRefresh: () {
setState(() {
_futureBuilderFuture = _liveRoomController.queryLiveInfo();
});
},
), ),
); );
} else { } else {

View File

@ -14,10 +14,12 @@ class BottomControl extends StatefulWidget implements PreferredSizeWidget {
final PlPlayerController? controller; final PlPlayerController? controller;
final LiveRoomController? liveRoomCtr; final LiveRoomController? liveRoomCtr;
final Floating? floating; final Floating? floating;
final Function? onRefresh;
const BottomControl({ const BottomControl({
this.controller, this.controller,
this.liveRoomCtr, this.liveRoomCtr,
this.floating, this.floating,
this.onRefresh,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@ -61,6 +63,14 @@ class _BottomControlState extends State<BottomControl> {
// ), // ),
// fuc: () => Get.back(), // fuc: () => Get.back(),
// ), // ),
ComBtn(
icon: const Icon(
Icons.refresh_outlined,
size: 18,
color: Colors.white,
),
fuc: widget.onRefresh,
),
const Spacer(), const Spacer(),
// ComBtn( // ComBtn(
// icon: const Icon( // icon: const Icon(
@ -150,21 +160,3 @@ class _BottomControlState extends State<BottomControl> {
); );
} }
} }
class MSliderTrackShape extends RoundedRectSliderTrackShape {
@override
Rect getPreferredRect({
required RenderBox parentBox,
Offset offset = Offset.zero,
SliderThemeData? sliderTheme,
bool isEnabled = false,
bool isDiscrete = false,
}) {
const double trackHeight = 3;
final double trackLeft = offset.dx;
final double trackTop =
offset.dy + (parentBox.size.height - trackHeight) / 2 + 4;
final double trackWidth = parentBox.size.width;
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
}
}

View File

@ -6,23 +6,16 @@ import 'package:get/get.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/http/common.dart'; import 'package:pilipala/http/common.dart';
import 'package:pilipala/pages/dynamics/index.dart';
import 'package:pilipala/pages/home/view.dart';
import 'package:pilipala/pages/media/index.dart';
import 'package:pilipala/pages/rank/index.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';
import '../../models/common/dynamic_badge_mode.dart'; import '../../models/common/dynamic_badge_mode.dart';
import '../../models/common/nav_bar_config.dart'; import '../../models/common/nav_bar_config.dart';
class MainController extends GetxController { class MainController extends GetxController {
List<Widget> pages = <Widget>[ List<Widget> pages = <Widget>[];
const HomePage(), RxList navigationBars = [].obs;
const RankPage(), late List defaultNavTabs;
const DynamicsPage(), late List<int> navBarSort;
const MediaPage(),
];
RxList navigationBars = defaultNavigationBars.obs;
final StreamController<bool> bottomBarStream = final StreamController<bool> bottomBarStream =
StreamController<bool>.broadcast(); StreamController<bool>.broadcast();
Box setting = GStrorage.setting; Box setting = GStrorage.setting;
@ -40,11 +33,8 @@ 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); hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: false);
int defaultHomePage =
setting.get(SettingBoxKey.defaultHomePage, defaultValue: 0) as int;
selectedIndex = defaultNavigationBars
.indexWhere((item) => item['id'] == defaultHomePage);
var userInfo = userInfoCache.get('userInfoCache'); var userInfo = userInfoCache.get('userInfoCache');
userLogin.value = userInfo != null; userLogin.value = userInfo != null;
dynamicBadgeType.value = DynamicBadgeMode.values[setting.get( dynamicBadgeType.value = DynamicBadgeMode.values[setting.get(
@ -53,6 +43,7 @@ class MainController extends GetxController {
if (dynamicBadgeType.value != DynamicBadgeMode.hidden) { if (dynamicBadgeType.value != DynamicBadgeMode.hidden) {
getUnreadDynamic(); getUnreadDynamic();
} }
setNavBarConfig();
} }
void onBackPressed(BuildContext context) { void onBackPressed(BuildContext context) {
@ -93,4 +84,21 @@ class MainController extends GetxController {
} }
navigationBars.refresh(); navigationBars.refresh();
} }
void setNavBarConfig() async {
defaultNavTabs = [...defaultNavigationBars];
navBarSort =
setting.get(SettingBoxKey.navBarSort, defaultValue: [0, 1, 2, 3]);
defaultNavTabs.retainWhere((item) => navBarSort.contains(item['id']));
defaultNavTabs.sort((a, b) =>
navBarSort.indexOf(a['id']).compareTo(navBarSort.indexOf(b['id'])));
navigationBars.value = defaultNavTabs;
int defaultHomePage =
setting.get(SettingBoxKey.defaultHomePage, defaultValue: 0) as int;
int defaultIndex =
navigationBars.indexWhere((item) => item['id'] == defaultHomePage);
// 如果找不到匹配项默认索引设置为0或其他合适的值
selectedIndex = defaultIndex != -1 ? defaultIndex : 0;
pages = navigationBars.map<Widget>((e) => e['page']).toList();
}
} }

View File

@ -10,6 +10,7 @@ import 'package:pilipala/pages/media/index.dart';
import 'package:pilipala/pages/rank/index.dart'; import 'package:pilipala/pages/rank/index.dart';
import 'package:pilipala/utils/event_bus.dart'; import 'package:pilipala/utils/event_bus.dart';
import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/global_data.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import './controller.dart'; import './controller.dart';
@ -29,7 +30,6 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
int? _lastSelectTime; //上次点击时间 int? _lastSelectTime; //上次点击时间
Box setting = GStrorage.setting; Box setting = GStrorage.setting;
late bool enableMYBar;
@override @override
void initState() { void initState() {
@ -37,7 +37,6 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
_lastSelectTime = DateTime.now().millisecondsSinceEpoch; _lastSelectTime = DateTime.now().millisecondsSinceEpoch;
_mainController.pageController = _mainController.pageController =
PageController(initialPage: _mainController.selectedIndex); PageController(initialPage: _mainController.selectedIndex);
enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true);
} }
void setIndex(int value) async { void setIndex(int value) async {
@ -127,81 +126,82 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
}, },
children: _mainController.pages, children: _mainController.pages,
), ),
bottomNavigationBar: StreamBuilder( bottomNavigationBar: _mainController.navigationBars.length > 1
stream: _mainController.hideTabBar ? StreamBuilder(
? _mainController.bottomBarStream.stream stream: _mainController.hideTabBar
: StreamController<bool>.broadcast().stream, ? _mainController.bottomBarStream.stream.distinct()
initialData: true, : StreamController<bool>.broadcast().stream,
builder: (context, AsyncSnapshot snapshot) { initialData: true,
return AnimatedSlide( builder: (context, AsyncSnapshot snapshot) {
curve: Curves.easeInOutCubicEmphasized, return AnimatedSlide(
duration: const Duration(milliseconds: 500), curve: Curves.easeInOutCubicEmphasized,
offset: Offset(0, snapshot.data ? 0 : 1), duration: const Duration(milliseconds: 500),
child: Obx( offset: Offset(0, snapshot.data ? 0 : 1),
() => enableMYBar child: GlobalData().enableMYBar
? NavigationBar( ? NavigationBar(
onDestinationSelected: (value) => setIndex(value), onDestinationSelected: (value) => setIndex(value),
selectedIndex: _mainController.selectedIndex, selectedIndex: _mainController.selectedIndex,
destinations: <Widget>[ destinations: <Widget>[
..._mainController.navigationBars.map((e) { ..._mainController.navigationBars.map((e) {
return NavigationDestination( return NavigationDestination(
icon: Obx( icon: Obx(
() => Badge( () => Badge(
label: label: _mainController
_mainController.dynamicBadgeType.value == .dynamicBadgeType.value ==
DynamicBadgeMode.number DynamicBadgeMode.number
? Text(e['count'].toString()) ? Text(e['count'].toString())
: null, : null,
padding: padding:
const EdgeInsets.fromLTRB(6, 0, 6, 0), const EdgeInsets.fromLTRB(6, 0, 6, 0),
isLabelVisible: isLabelVisible: _mainController
_mainController.dynamicBadgeType.value != .dynamicBadgeType.value !=
DynamicBadgeMode.hidden && DynamicBadgeMode.hidden &&
e['count'] > 0, e['count'] > 0,
child: e['icon'], child: e['icon'],
), ),
), ),
selectedIcon: e['selectIcon'], selectedIcon: e['selectIcon'],
label: e['label'], label: e['label'],
); );
}).toList(), }).toList(),
], ],
) )
: BottomNavigationBar( : BottomNavigationBar(
currentIndex: _mainController.selectedIndex, currentIndex: _mainController.selectedIndex,
onTap: (value) => setIndex(value), type: BottomNavigationBarType.fixed,
iconSize: 16, onTap: (value) => setIndex(value),
selectedFontSize: 12, iconSize: 16,
unselectedFontSize: 12, selectedFontSize: 12,
items: [ unselectedFontSize: 12,
..._mainController.navigationBars.map((e) { items: [
return BottomNavigationBarItem( ..._mainController.navigationBars.map((e) {
icon: Obx( return BottomNavigationBarItem(
() => Badge( icon: Obx(
label: () => Badge(
_mainController.dynamicBadgeType.value == label: _mainController
.dynamicBadgeType.value ==
DynamicBadgeMode.number DynamicBadgeMode.number
? Text(e['count'].toString()) ? Text(e['count'].toString())
: null, : null,
padding: padding:
const EdgeInsets.fromLTRB(6, 0, 6, 0), const EdgeInsets.fromLTRB(6, 0, 6, 0),
isLabelVisible: isLabelVisible: _mainController
_mainController.dynamicBadgeType.value != .dynamicBadgeType.value !=
DynamicBadgeMode.hidden && DynamicBadgeMode.hidden &&
e['count'] > 0, e['count'] > 0,
child: e['icon'], child: e['icon'],
), ),
), ),
activeIcon: e['selectIcon'], activeIcon: e['selectIcon'],
label: e['label'], label: e['label'],
); );
}).toList(), }).toList(),
], ],
), ),
), );
); },
}, )
), : null,
), ),
); );
} }

View File

@ -1,13 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:media_kit/media_kit.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/models/user/fav_folder.dart';
import 'package:pilipala/pages/main/index.dart';
import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/pages/media/index.dart';
import 'package:pilipala/utils/main_stream.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
class MediaPage extends StatefulWidget { class MediaPage extends StatefulWidget {
@ -30,26 +28,11 @@ class _MediaPageState extends State<MediaPage>
super.initState(); super.initState();
mediaController = Get.put(MediaController()); mediaController = Get.put(MediaController());
_futureBuilderFuture = mediaController.queryFavFolder(); _futureBuilderFuture = mediaController.queryFavFolder();
ScrollController scrollController = mediaController.scrollController;
StreamController<bool> mainStream =
Get.find<MainController>().bottomBarStream;
mediaController.userLogin.listen((status) { mediaController.userLogin.listen((status) {
setState(() { setState(() {
_futureBuilderFuture = mediaController.queryFavFolder(); _futureBuilderFuture = mediaController.queryFavFolder();
}); });
}); });
scrollController.addListener(
() {
final ScrollDirection direction =
scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
mainStream.add(true);
} else if (direction == ScrollDirection.reverse) {
mainStream.add(false);
}
},
);
} }
@override @override

View File

@ -65,7 +65,7 @@ class _MemberPageState extends State<MemberPage>
children: [ children: [
AppBar( AppBar(
title: StreamBuilder( title: StreamBuilder(
stream: appbarStream.stream, stream: appbarStream.stream.distinct(),
initialData: false, initialData: false,
builder: (BuildContext context, AsyncSnapshot snapshot) { builder: (BuildContext context, AsyncSnapshot snapshot) {
return AnimatedOpacity( return AnimatedOpacity(

View File

@ -59,6 +59,7 @@ class MemberCoinsItem extends StatelessWidget {
padding: const EdgeInsets.fromLTRB(5, 6, 0, 0), padding: const EdgeInsets.fromLTRB(5, 6, 0, 0),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
coinItem.title!, coinItem.title!,

View File

@ -25,7 +25,7 @@ class MemberSeasonsItem extends StatelessWidget {
child: InkWell( child: InkWell(
onTap: () async { onTap: () async {
int cid = int cid =
await SearchHttp.ab2c(aid: seasonItem.aid, bvid: seasonItem.bvid); await SearchHttp.ab2c(aid: seasonItem.aid, bvid: seasonItem.bvid);
Get.toNamed('/video?bvid=${seasonItem.bvid}&cid=$cid', Get.toNamed('/video?bvid=${seasonItem.bvid}&cid=$cid',
arguments: {'videoItem': seasonItem, 'heroTag': heroTag}); arguments: {'videoItem': seasonItem, 'heroTag': heroTag});
}, },
@ -51,8 +51,7 @@ class MemberSeasonsItem extends StatelessWidget {
bottom: 6, bottom: 6,
right: 6, right: 6,
type: 'gray', type: 'gray',
text: Utils.CustomStamp_str( text: Utils.timeFormat(seasonItem.duration),
timestamp: seasonItem.pubdate, date: 'YY-MM-DD'),
) )
], ],
); );

View File

@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/animated_dialog.dart';
@ -9,9 +8,8 @@ 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/main/index.dart';
import 'package:pilipala/pages/rank/zone/index.dart'; import 'package:pilipala/pages/rank/zone/index.dart';
import 'package:pilipala/utils/main_stream.dart';
class ZonePage extends StatefulWidget { class ZonePage extends StatefulWidget {
const ZonePage({Key? key, required this.rid}) : super(key: key); const ZonePage({Key? key, required this.rid}) : super(key: key);
@ -38,10 +36,6 @@ class _ZonePageState extends State<ZonePage>
_zoneController = Get.put(ZoneController(), tag: widget.rid.toString()); _zoneController = Get.put(ZoneController(), tag: widget.rid.toString());
_futureBuilderFuture = _zoneController.queryRankFeed('init', widget.rid); _futureBuilderFuture = _zoneController.queryRankFeed('init', widget.rid);
scrollController = _zoneController.scrollController; scrollController = _zoneController.scrollController;
StreamController<bool> mainStream =
Get.find<MainController>().bottomBarStream;
StreamController<bool> searchBarStream =
Get.find<HomeController>().searchBarStream;
scrollController.addListener( scrollController.addListener(
() { () {
if (scrollController.position.pixels >= if (scrollController.position.pixels >=
@ -51,16 +45,7 @@ class _ZonePageState extends State<ZonePage>
_zoneController.onLoad(); _zoneController.onLoad();
} }
} }
handleScrollEvent(scrollController);
final ScrollDirection direction =
scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
mainStream.add(true);
searchBarStream.add(true);
} else if (direction == ScrollDirection.reverse) {
mainStream.add(false);
searchBarStream.add(false);
}
}, },
); );
} }

View File

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:easy_debounce/easy_throttle.dart'; import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/skeleton/video_card_v.dart'; import 'package:pilipala/common/skeleton/video_card_v.dart';
@ -10,8 +9,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/utils/main_stream.dart';
import 'package:pilipala/pages/main/index.dart';
import 'controller.dart'; import 'controller.dart';
@ -35,10 +33,6 @@ class _RcmdPageState extends State<RcmdPage>
super.initState(); super.initState();
_futureBuilderFuture = _rcmdController.queryRcmdFeed('init'); _futureBuilderFuture = _rcmdController.queryRcmdFeed('init');
ScrollController scrollController = _rcmdController.scrollController; ScrollController scrollController = _rcmdController.scrollController;
StreamController<bool> mainStream =
Get.find<MainController>().bottomBarStream;
StreamController<bool> searchBarStream =
Get.find<HomeController>().searchBarStream;
scrollController.addListener( scrollController.addListener(
() { () {
if (scrollController.position.pixels >= if (scrollController.position.pixels >=
@ -49,15 +43,7 @@ class _RcmdPageState extends State<RcmdPage>
_rcmdController.onLoad(); _rcmdController.onLoad();
}); });
} }
final ScrollDirection direction = handleScrollEvent(scrollController);
scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
mainStream.add(true);
searchBarStream.add(true);
} else if (direction == ScrollDirection.reverse) {
mainStream.add(false);
searchBarStream.add(false);
}
}, },
); );
} }

View File

@ -0,0 +1,100 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/models/common/tab_type.dart';
import 'package:pilipala/utils/storage.dart';
import '../../../models/common/nav_bar_config.dart';
class NavigationBarSetPage extends StatefulWidget {
const NavigationBarSetPage({super.key});
@override
State<NavigationBarSetPage> createState() => _NavigationbarSetPageState();
}
class _NavigationbarSetPageState extends State<NavigationBarSetPage> {
Box settingStorage = GStrorage.setting;
late List defaultNavTabs;
late List<int> navBarSort;
@override
void initState() {
super.initState();
defaultNavTabs = defaultNavigationBars;
navBarSort = settingStorage
.get(SettingBoxKey.navBarSort, defaultValue: [0, 1, 2, 3]);
// 对 tabData 进行排序
defaultNavTabs.sort((a, b) {
int indexA = navBarSort.indexOf(a['id']);
int indexB = navBarSort.indexOf(b['id']);
// 如果类型在 sortOrder 中不存在,则放在末尾
if (indexA == -1) indexA = navBarSort.length;
if (indexB == -1) indexB = navBarSort.length;
return indexA.compareTo(indexB);
});
}
void saveEdit() {
List<int> sortedTabbar = defaultNavTabs
.where((i) => navBarSort.contains(i['id']))
.map<int>((i) => i['id'])
.toList();
settingStorage.put(SettingBoxKey.navBarSort, sortedTabbar);
SmartDialog.showToast('保存成功,下次启动时生效');
}
void onReorder(int oldIndex, int newIndex) {
setState(() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
final tabsItem = defaultNavTabs.removeAt(oldIndex);
defaultNavTabs.insert(newIndex, tabsItem);
});
}
@override
Widget build(BuildContext context) {
final listTiles = [
for (int i = 0; i < defaultNavTabs.length; i++) ...[
CheckboxListTile(
key: Key(defaultNavTabs[i]['label']),
value: navBarSort.contains(defaultNavTabs[i]['id']),
onChanged: (bool? newValue) {
int tabTypeId = defaultNavTabs[i]['id'];
if (!newValue!) {
navBarSort.remove(tabTypeId);
} else {
navBarSort.add(tabTypeId);
}
setState(() {});
},
title: Text(defaultNavTabs[i]['label']),
secondary: const Icon(Icons.drag_indicator_rounded),
enabled: defaultNavTabs[i]['id'] != 0,
)
]
];
return Scaffold(
appBar: AppBar(
title: const Text('Navbar编辑'),
actions: [
TextButton(onPressed: () => saveEdit(), child: const Text('保存')),
const SizedBox(width: 12)
],
),
body: ReorderableListView(
onReorder: onReorder,
physics: const NeverScrollableScrollPhysics(),
footer: SizedBox(
height: MediaQuery.of(context).padding.bottom + 30,
),
children: listTiles,
),
);
}
}

View File

@ -284,12 +284,17 @@ class _StyleSettingState extends State<StyleSetting> {
onTap: () => Get.toNamed('/tabbarSetting'), onTap: () => Get.toNamed('/tabbarSetting'),
title: Text('首页tabbar', style: titleStyle), title: Text('首页tabbar', style: titleStyle),
), ),
ListTile(
dense: false,
onTap: () => Get.toNamed('/navbarSetting'),
title: Text('底部导航栏设置', style: titleStyle),
),
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),
) ),
], ],
), ),
); );

View File

@ -6,7 +6,6 @@ import '../../models/user/sub_folder.dart';
class SubDetailController extends GetxController { class SubDetailController extends GetxController {
late SubFolderItemData item; late SubFolderItemData item;
late int seasonId; late int seasonId;
late String heroTag; late String heroTag;
int currentPage = 1; int currentPage = 1;
@ -26,17 +25,23 @@ class SubDetailController extends GetxController {
super.onInit(); super.onInit();
} }
Future<dynamic> queryUserSubFolderDetail({type = 'init'}) async { Future<dynamic> queryUserSeasonList({type = 'init'}) async {
if (type == 'onLoad' && subList.length >= mediaCount) { if (type == 'onLoad' && subList.length >= mediaCount) {
loadingText.value = '没有更多了'; loadingText.value = '没有更多了';
return; return;
} }
isLoadingMore = true; isLoadingMore = true;
var res = await UserHttp.userSubFolderDetail( var res = type == 21
seasonId: seasonId, ? await UserHttp.userSeasonList(
ps: 20, seasonId: seasonId,
pn: currentPage, ps: 20,
); pn: currentPage,
)
: await UserHttp.userResourceList(
seasonId: seasonId,
ps: 20,
pn: currentPage,
);
if (res['status']) { if (res['status']) {
subInfo.value = res['data'].info; subInfo.value = res['data'].info;
if (currentPage == 1 && type == 'init') { if (currentPage == 1 && type == 'init') {
@ -55,6 +60,6 @@ class SubDetailController extends GetxController {
} }
onLoad() { onLoad() {
queryUserSubFolderDetail(type: 'onLoad'); queryUserSeasonList(type: 'onLoad');
} }
} }

View File

@ -26,13 +26,11 @@ class _SubDetailPageState extends State<SubDetailPage> {
Get.put(SubDetailController()); Get.put(SubDetailController());
late StreamController<bool> titleStreamC; // a late StreamController<bool> titleStreamC; // a
late Future _futureBuilderFuture; late Future _futureBuilderFuture;
late String seasonId;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
seasonId = Get.parameters['seasonId']!; _futureBuilderFuture = _subDetailController.queryUserSeasonList();
_futureBuilderFuture = _subDetailController.queryUserSubFolderDetail();
titleStreamC = StreamController<bool>(); titleStreamC = StreamController<bool>();
_controller.addListener( _controller.addListener(
() { () {
@ -69,7 +67,7 @@ class _SubDetailPageState extends State<SubDetailPage> {
pinned: true, pinned: true,
titleSpacing: 0, titleSpacing: 0,
title: StreamBuilder( title: StreamBuilder(
stream: titleStreamC.stream, stream: titleStreamC.stream.distinct(),
initialData: false, initialData: false,
builder: (context, AsyncSnapshot snapshot) { builder: (context, AsyncSnapshot snapshot) {
return AnimatedOpacity( return AnimatedOpacity(
@ -161,15 +159,18 @@ class _SubDetailPageState extends State<SubDetailPage> {
), ),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Obx(
'${Utils.numFormat(_subDetailController.item.viewCount)}次播放', () => Text(
style: TextStyle( '${Utils.numFormat(_subDetailController.subInfo.value.cntInfo?['play'])}次播放',
fontSize: Theme.of(context) style: TextStyle(
.textTheme fontSize: Theme.of(context)
.labelSmall! .textTheme
.fontSize, .labelSmall!
color: Theme.of(context).colorScheme.outline), .fontSize,
), color:
Theme.of(context).colorScheme.outline),
),
)
], ],
), ),
), ),
@ -182,14 +183,12 @@ class _SubDetailPageState extends State<SubDetailPage> {
SliverToBoxAdapter( SliverToBoxAdapter(
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 15, bottom: 8, left: 14), padding: const EdgeInsets.only(top: 15, bottom: 8, left: 14),
child: Obx( child: Text(
() => Text( '${_subDetailController.item.mediaCount}条视频',
'${_subDetailController.subList.length}条视频', style: TextStyle(
style: TextStyle( fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
fontSize: color: Theme.of(context).colorScheme.outline,
Theme.of(context).textTheme.labelMedium!.fontSize, letterSpacing: 1,
color: Theme.of(context).colorScheme.outline,
letterSpacing: 1),
), ),
), ),
), ),

View File

@ -106,6 +106,7 @@ class VideoDetailController extends GetxController
BottomControlType.fit, BottomControlType.fit,
BottomControlType.fullscreen, BottomControlType.fullscreen,
].obs; ].obs;
RxDouble sheetHeight = 0.0.obs;
@override @override
void onInit() { void onInit() {
@ -172,6 +173,7 @@ class VideoDetailController extends GetxController
firstFloor: firstFloor, firstFloor: firstFloor,
replyType: ReplyType.video, replyType: ReplyType.video,
source: 'videoDetail', source: 'videoDetail',
sheetHeight: sheetHeight.value,
); );
}); });
replyReplyBottomSheetCtr?.closed.then((value) { replyReplyBottomSheetCtr?.closed.then((value) {

View File

@ -372,7 +372,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
), ),
/// 点赞收藏转发 /// 点赞收藏转发
actionGrid(context, videoIntroController), Material(child: actionGrid(context, videoIntroController)),
// 合集 videoPart 简洁 // 合集 videoPart 简洁
if (widget.videoDetail!.ugcSeason != null) ...[ if (widget.videoDetail!.ugcSeason != null) ...[
Obx( Obx(
@ -381,7 +381,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
cid: videoIntroController.lastPlayCid.value != 0 cid: videoIntroController.lastPlayCid.value != 0
? videoIntroController.lastPlayCid.value ? videoIntroController.lastPlayCid.value
: widget.videoDetail!.pages!.first.cid, : widget.videoDetail!.pages!.first.cid,
sheetHeight: sheetHeight, sheetHeight: videoDetailCtr.sheetHeight.value,
changeFuc: (bvid, cid, aid) => changeFuc: (bvid, cid, aid) =>
videoIntroController.changeSeasonOrbangu( videoIntroController.changeSeasonOrbangu(
bvid, bvid,
@ -399,7 +399,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
() => PagesPanel( () => PagesPanel(
pages: widget.videoDetail!.pages!, pages: widget.videoDetail!.pages!,
cid: videoIntroController.lastPlayCid.value, cid: videoIntroController.lastPlayCid.value,
sheetHeight: sheetHeight, sheetHeight: videoDetailCtr.sheetHeight.value,
changeFuc: (cid) => videoIntroController.changeSeasonOrbangu( changeFuc: (cid) => videoIntroController.changeSeasonOrbangu(
videoIntroController.bvid, videoIntroController.bvid,
cid, cid,

View File

@ -5,7 +5,6 @@ import 'package:get/get.dart';
import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/models/video_detail_res.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/pages/video/detail/introduction/index.dart'; import 'package:pilipala/pages/video/detail/introduction/index.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import '../../../../../common/pages_bottom_sheet.dart'; import '../../../../../common/pages_bottom_sheet.dart';
import '../../../../../models/common/video_episode_type.dart'; import '../../../../../models/common/video_episode_type.dart';
@ -35,7 +34,6 @@ class _PagesPanelState extends State<PagesPanel> {
final String heroTag = Get.arguments['heroTag']; final String heroTag = Get.arguments['heroTag'];
late VideoDetailController _videoDetailController; late VideoDetailController _videoDetailController;
final ScrollController listViewScrollCtr = ScrollController(); final ScrollController listViewScrollCtr = ScrollController();
final ItemScrollController itemScrollController = ItemScrollController();
late PersistentBottomSheetController? _bottomSheetController; late PersistentBottomSheetController? _bottomSheetController;
@override @override
@ -60,7 +58,6 @@ class _PagesPanelState extends State<PagesPanel> {
} }
void changeFucCall(item, i) async { void changeFucCall(item, i) async {
print('pages changeFucCall');
widget.changeFuc?.call(item.cid); widget.changeFuc?.call(item.cid);
currentIndex.value = i; currentIndex.value = i;
_bottomSheetController?.close(); _bottomSheetController?.close();
@ -72,11 +69,15 @@ class _PagesPanelState extends State<PagesPanel> {
// 在回调函数中获取更新后的状态 // 在回调函数中获取更新后的状态
final double offset = min((currentIndex * 150) - 75, final double offset = min((currentIndex * 150) - 75,
listViewScrollCtr.position.maxScrollExtent); listViewScrollCtr.position.maxScrollExtent);
listViewScrollCtr.animateTo( if (currentIndex.value == 0) {
offset, listViewScrollCtr.jumpTo(0);
duration: const Duration(milliseconds: 300), } else {
curve: Curves.easeInOut, listViewScrollCtr.animateTo(
); offset,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
}); });
} }

View File

@ -4,7 +4,6 @@ import 'package:pilipala/common/pages_bottom_sheet.dart';
import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/models/video_detail_res.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/id_utils.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import '../../../../../models/common/video_episode_type.dart'; import '../../../../../models/common/video_episode_type.dart';
import '../controller.dart'; import '../controller.dart';
@ -33,7 +32,6 @@ class _SeasonPanelState extends State<SeasonPanel> {
late RxInt currentIndex = (-1).obs; late RxInt currentIndex = (-1).obs;
final String heroTag = Get.arguments['heroTag']; final String heroTag = Get.arguments['heroTag'];
late VideoDetailController _videoDetailController; late VideoDetailController _videoDetailController;
final ItemScrollController itemScrollController = ItemScrollController();
late PersistentBottomSheetController? _bottomSheetController; late PersistentBottomSheetController? _bottomSheetController;
@override @override

View File

@ -19,6 +19,7 @@ class VideoReplyReplyPanel extends StatefulWidget {
this.firstFloor, this.firstFloor,
this.source, this.source,
this.replyType, this.replyType,
this.sheetHeight,
super.key, super.key,
}); });
final int? oid; final int? oid;
@ -27,6 +28,7 @@ class VideoReplyReplyPanel extends StatefulWidget {
final ReplyItemModel? firstFloor; final ReplyItemModel? firstFloor;
final String? source; final String? source;
final ReplyType? replyType; final ReplyType? replyType;
final double? sheetHeight;
@override @override
State<VideoReplyReplyPanel> createState() => _VideoReplyReplyPanelState(); State<VideoReplyReplyPanel> createState() => _VideoReplyReplyPanelState();
@ -36,7 +38,6 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
late VideoReplyReplyController _videoReplyReplyController; late VideoReplyReplyController _videoReplyReplyController;
late AnimationController replyAnimationCtl; late AnimationController replyAnimationCtl;
final Box<dynamic> localCache = GStrorage.localCache; final Box<dynamic> localCache = GStrorage.localCache;
late double sheetHeight;
Future? _futureBuilderFuture; Future? _futureBuilderFuture;
late ScrollController scrollController; late ScrollController scrollController;
@ -62,7 +63,6 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
}, },
); );
sheetHeight = localCache.get('sheetHeight');
_futureBuilderFuture = _videoReplyReplyController.queryReplyList(); _futureBuilderFuture = _videoReplyReplyController.queryReplyList();
} }
@ -77,33 +77,31 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
height: widget.source == 'videoDetail' ? sheetHeight : null, height: widget.source == 'videoDetail' ? widget.sheetHeight : null,
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
child: Column( child: Column(
children: [ children: [
if (widget.source == 'videoDetail') if (widget.source == 'videoDetail')
Container( AppBar(
height: 45, toolbarHeight: 45,
padding: const EdgeInsets.only(left: 12, right: 2), automaticallyImplyLeading: false,
child: Row( centerTitle: false,
mainAxisAlignment: MainAxisAlignment.spaceBetween, title: Text(
children: <Widget>[ '评论详情',
const Text('评论详情'), style: Theme.of(context).textTheme.titleSmall,
IconButton(
icon: const Icon(Icons.close, size: 20),
onPressed: () {
_videoReplyReplyController.currentPage = 0;
widget.closePanel?.call;
Navigator.pop(context);
},
),
],
), ),
actions: [
IconButton(
icon: const Icon(Icons.close, size: 20),
onPressed: () {
_videoReplyReplyController.currentPage = 0;
widget.closePanel?.call;
Navigator.pop(context);
},
),
const SizedBox(width: 14),
],
), ),
Divider(
height: 1,
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
Expanded( Expanded(
child: RefreshIndicator( child: RefreshIndicator(
onRefresh: () async { onRefresh: () async {
@ -140,8 +138,8 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
future: _futureBuilderFuture, future: _futureBuilderFuture,
builder: (BuildContext context, snapshot) { builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
final Map data = snapshot.data as Map; Map? data = snapshot.data;
if (data['status']) { if (data != null && data['status']) {
// 请求成功 // 请求成功
return Obx( return Obx(
() => SliverList( () => SliverList(
@ -199,7 +197,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
} else { } else {
// 请求错误 // 请求错误
return HttpError( return HttpError(
errMsg: data['msg'], errMsg: data?['msg'] ?? '请求错误',
fn: () => setState(() {}), fn: () => setState(() {}),
); );
} }

View File

@ -47,7 +47,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
late BangumiIntroController bangumiIntroController; late BangumiIntroController bangumiIntroController;
late String heroTag; late String heroTag;
PlayerStatus playerStatus = PlayerStatus.playing; Rx<PlayerStatus> playerStatus = PlayerStatus.playing.obs;
double doubleOffset = 0; double doubleOffset = 0;
final Box<dynamic> localCache = GStrorage.localCache; final Box<dynamic> localCache = GStrorage.localCache;
@ -67,6 +67,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
super.initState(); super.initState();
heroTag = Get.arguments['heroTag']; heroTag = Get.arguments['heroTag'];
vdCtr = Get.put(VideoDetailController(), tag: heroTag); vdCtr = Get.put(VideoDetailController(), tag: heroTag);
vdCtr.sheetHeight.value = localCache.get('sheetHeight');
videoIntroController = Get.put( videoIntroController = Get.put(
VideoIntroController(bvid: Get.parameters['bvid']!), VideoIntroController(bvid: Get.parameters['bvid']!),
tag: heroTag); tag: heroTag);
@ -108,10 +109,12 @@ class _VideoDetailPageState extends State<VideoDetailPage>
// 流 // 流
appbarStreamListen() { appbarStreamListen() {
appbarStream = StreamController<double>(); appbarStream = StreamController<double>.broadcast();
_extendNestCtr.addListener( _extendNestCtr.addListener(
() { () {
final double offset = _extendNestCtr.position.pixels; final double offset = _extendNestCtr.position.pixels;
vdCtr.sheetHeight.value =
Get.size.height - videoHeight - statusBarHeight + offset;
appbarStream.add(offset); appbarStream.add(offset);
}, },
); );
@ -119,7 +122,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
// 播放器状态监听 // 播放器状态监听
void playerListener(PlayerStatus? status) async { void playerListener(PlayerStatus? status) async {
playerStatus = status!; playerStatus.value = status!;
if (status == PlayerStatus.completed) { if (status == PlayerStatus.completed) {
// 结束播放退出全屏 // 结束播放退出全屏
if (autoExitFullcreen) { if (autoExitFullcreen) {
@ -271,7 +274,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16;
final sizeContext = MediaQuery.sizeOf(context); final sizeContext = MediaQuery.sizeOf(context);
final _context = MediaQuery.of(context); final _context = MediaQuery.of(context);
late double defaultVideoHeight = sizeContext.width * 9 / 16; late double defaultVideoHeight = sizeContext.width * 9 / 16;
@ -366,6 +368,18 @@ class _VideoDetailPageState extends State<VideoDetailPage>
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
Obx(() => AnimatedOpacity(
opacity: playerStatus.value != PlayerStatus.playing
? 1
: 0,
duration: const Duration(milliseconds: 100),
child: const Icon(
Icons.drag_handle_rounded,
size: 20,
color: Colors.grey,
),
)),
const SizedBox(width: 8),
SizedBox( SizedBox(
height: 32, height: 32,
child: TextButton( child: TextButton(
@ -404,7 +418,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
), ),
), ),
), ),
const SizedBox(width: 14), const SizedBox(width: 18),
], ],
), ),
)), )),
@ -557,7 +571,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
Orientation.landscape || Orientation.landscape ||
plPlayerController?.isFullScreen.value == true plPlayerController?.isFullScreen.value == true
? MediaQuery.sizeOf(context).height ? MediaQuery.sizeOf(context).height
: playerStatus != PlayerStatus.playing : playerStatus.value != PlayerStatus.playing
? kToolbarHeight ? kToolbarHeight
: pinnedHeaderHeight; : pinnedHeaderHeight;
}, },
@ -618,13 +632,13 @@ class _VideoDetailPageState extends State<VideoDetailPage>
/// 重新进入会刷新 /// 重新进入会刷新
// 播放完成/暂停播放 // 播放完成/暂停播放
StreamBuilder( StreamBuilder(
stream: appbarStream.stream, stream: appbarStream.stream.distinct(),
initialData: 0, initialData: 0,
builder: ((context, snapshot) { builder: ((context, snapshot) {
return ScrollAppBar( return ScrollAppBar(
snapshot.data!.toDouble(), snapshot.data!.toDouble(),
() => continuePlay(), () => continuePlay(),
playerStatus, playerStatus.value,
null, null,
); );
}), }),

View File

@ -11,6 +11,7 @@ import 'package:ns_danmaku/ns_danmaku.dart';
import 'package:pilipala/http/user.dart'; import 'package:pilipala/http/user.dart';
import 'package:pilipala/models/video/play/quality.dart'; import 'package:pilipala/models/video/play/quality.dart';
import 'package:pilipala/models/video/play/url.dart'; import 'package:pilipala/models/video/play/url.dart';
import 'package:pilipala/pages/dlna/index.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart'; 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';
@ -82,7 +83,6 @@ class _HeaderControlState extends State<HeaderControl> {
/// 设置面板 /// 设置面板
void showSettingSheet() { void showSettingSheet() {
// Scaffold.of(context).openDrawer();
showModalBottomSheet( showModalBottomSheet(
elevation: 0, elevation: 0,
context: context, context: context,
@ -731,9 +731,12 @@ class _HeaderControlState extends State<HeaderControl> {
margin: const EdgeInsets.all(12), margin: const EdgeInsets.all(12),
child: Column( child: Column(
children: [ children: [
SizedBox( const SizedBox(
height: 45, height: 45,
child: Center(child: Text('选择解码格式', style: titleStyle))), child: Center(
child: Text('选择解码格式', style: titleStyle),
),
),
Expanded( Expanded(
child: Material( child: Material(
child: ListView( child: ListView(
@ -1078,9 +1081,12 @@ class _HeaderControlState extends State<HeaderControl> {
margin: const EdgeInsets.all(12), margin: const EdgeInsets.all(12),
child: Column( child: Column(
children: [ children: [
SizedBox( const SizedBox(
height: 45, height: 45,
child: Center(child: Text('选择播放顺序', style: titleStyle))), child: Center(
child: Text('选择播放顺序', style: titleStyle),
),
),
Expanded( Expanded(
child: Material( child: Material(
child: ListView( child: ListView(
@ -1165,11 +1171,13 @@ class _HeaderControlState extends State<HeaderControl> {
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 200), constraints: const BoxConstraints(maxWidth: 200),
child: Text( child: Obx(
videoIntroController.videoDetail.value.title ?? '', () => Text(
style: const TextStyle( videoIntroController.videoDetail.value.title ?? '',
color: Colors.white, style: const TextStyle(
fontSize: 16, color: Colors.white,
fontSize: 16,
),
), ),
), ),
), ),
@ -1209,6 +1217,22 @@ class _HeaderControlState extends State<HeaderControl> {
// ), // ),
// fuc: () => _.screenshot(), // fuc: () => _.screenshot(),
// ), // ),
ComBtn(
icon: const Icon(
Icons.cast,
size: 19,
color: Colors.white,
),
fuc: () async {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return LiveDlnaPage(
datasource: widget.videoDetailCtr!.videoUrl);
},
);
},
),
if (isFullScreen.value) ...[ if (isFullScreen.value) ...[
SizedBox( SizedBox(
width: 56, width: 56,

View File

@ -101,7 +101,7 @@ class PlPlayerController {
bool _isFirstTime = true; bool _isFirstTime = true;
Timer? _timer; Timer? _timer;
Timer? _timerForSeek; late Timer? _timerForSeek;
Timer? _timerForVolume; Timer? _timerForVolume;
Timer? _timerForShowingVolume; Timer? _timerForShowingVolume;
Timer? _timerForGettingVolume; Timer? _timerForGettingVolume;
@ -646,9 +646,6 @@ class PlPlayerController {
/// 跳转至指定位置 /// 跳转至指定位置
Future<void> seekTo(Duration position, {type = 'seek'}) async { Future<void> seekTo(Duration position, {type = 'seek'}) async {
// if (position >= duration.value) {
// position = duration.value - const Duration(milliseconds: 100);
// }
if (position < Duration.zero) { if (position < Duration.zero) {
position = Duration.zero; position = Duration.zero;
} }
@ -661,21 +658,13 @@ class PlPlayerController {
await _videoPlayerController?.stream.buffer.first; await _videoPlayerController?.stream.buffer.first;
} }
await _videoPlayerController?.seek(position); await _videoPlayerController?.seek(position);
// if (playerStatus.stopped) {
// play();
// }
} else { } else {
print('seek duration else');
_timerForSeek?.cancel(); _timerForSeek?.cancel();
_timerForSeek = _timerForSeek ??=
Timer.periodic(const Duration(milliseconds: 200), (Timer t) async { Timer.periodic(const Duration(milliseconds: 200), (Timer t) async {
//_timerForSeek = null;
if (duration.value.inSeconds != 0) { if (duration.value.inSeconds != 0) {
await _videoPlayerController!.stream.buffer.first; await _videoPlayerController!.stream.buffer.first;
await _videoPlayerController?.seek(position); await _videoPlayerController?.seek(position);
// if (playerStatus.status.value == PlayerStatus.paused) {
// play();
// }
t.cancel(); t.cancel();
_timerForSeek = null; _timerForSeek = null;
} }

View File

@ -39,6 +39,7 @@ import '../pages/setting/pages/color_select.dart';
import '../pages/setting/pages/display_mode.dart'; import '../pages/setting/pages/display_mode.dart';
import '../pages/setting/pages/font_size_select.dart'; import '../pages/setting/pages/font_size_select.dart';
import '../pages/setting/pages/home_tabbar_set.dart'; import '../pages/setting/pages/home_tabbar_set.dart';
import '../pages/setting/pages/navigation_bar_set.dart';
import '../pages/setting/pages/play_gesture_set.dart'; import '../pages/setting/pages/play_gesture_set.dart';
import '../pages/setting/pages/play_speed_set.dart'; import '../pages/setting/pages/play_speed_set.dart';
import '../pages/setting/recommend_setting.dart'; import '../pages/setting/recommend_setting.dart';
@ -170,6 +171,9 @@ class Routes {
// 播放器手势 // 播放器手势
CustomGetPage( CustomGetPage(
name: '/playerGestureSet', page: () => const PlayGesturePage()), name: '/playerGestureSet', page: () => const PlayGesturePage()),
// navigation bar
CustomGetPage(
name: '/navbarSetting', page: () => const NavigationBarSetPage()),
]; ];
} }

View File

@ -1,5 +1,7 @@
import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@ -70,9 +72,20 @@ class DownloadUtils {
static Future<bool> downloadImg(String imgUrl, static Future<bool> downloadImg(String imgUrl,
{String imgType = 'cover'}) async { {String imgType = 'cover'}) async {
try { try {
if (!await requestPhotoPer()) { if (!Platform.isAndroid || !await requestPhotoPer()) {
return false; return false;
} }
final androidInfo = await DeviceInfoPlugin().androidInfo;
if (androidInfo.version.sdkInt <= 32) {
if (!await requestStoragePer()) {
return false;
}
} else {
if (!await requestPhotoPer()) {
return false;
}
}
SmartDialog.showLoading(msg: '保存中'); SmartDialog.showLoading(msg: '保存中');
var response = await Dio() var response = await Dio()
.get(imgUrl, options: Options(responseType: ResponseType.bytes)); .get(imgUrl, options: Options(responseType: ResponseType.bytes));

View File

@ -1,10 +1,16 @@
import 'package:hive/hive.dart';
import 'package:pilipala/utils/storage.dart';
import '../models/common/index.dart'; import '../models/common/index.dart';
Box setting = GStrorage.setting;
class GlobalData { class GlobalData {
int imgQuality = 10; int imgQuality = 10;
FullScreenGestureMode fullScreenGestureMode = FullScreenGestureMode fullScreenGestureMode =
FullScreenGestureMode.values.last; FullScreenGestureMode.values.last;
bool enablePlayerControlAnimation = true; bool enablePlayerControlAnimation = true;
final bool enableMYBar =
setting.get(SettingBoxKey.enableMYBar, defaultValue: true);
// 私有构造函数 // 私有构造函数
GlobalData._(); GlobalData._();

View File

@ -0,0 +1,32 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
import '../pages/home/index.dart';
import '../pages/main/index.dart';
void handleScrollEvent(ScrollController scrollController) {
StreamController<bool> mainStream =
Get.find<MainController>().bottomBarStream;
StreamController<bool> searchBarStream =
Get.find<HomeController>().searchBarStream;
EasyThrottle.throttle(
'stream-throttler',
const Duration(milliseconds: 300),
() {
try {
final ScrollDirection direction =
scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
mainStream.add(true);
searchBarStream.add(true);
} else if (direction == ScrollDirection.reverse) {
mainStream.add(false);
searchBarStream.add(false);
}
} catch (_) {}
},
);
}

View File

@ -148,7 +148,8 @@ class SettingBoxKey {
hideTabBar = 'hideTabBar', // 收起底栏 hideTabBar = 'hideTabBar', // 收起底栏
tabbarSort = 'tabbarSort', // 首页tabbar tabbarSort = 'tabbarSort', // 首页tabbar
dynamicBadgeMode = 'dynamicBadgeMode', dynamicBadgeMode = 'dynamicBadgeMode',
enableGradientBg = 'enableGradientBg'; enableGradientBg = 'enableGradientBg',
navBarSort = 'navBarSort';
} }
class LocalCacheKey { class LocalCacheKey {

View File

@ -409,6 +409,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.0.2" version: "1.0.2"
dlna_dart:
dependency: "direct main"
description:
name: dlna_dart
sha256: ae07c1c53077bbf58756fa589f936968719b0085441981d33e74f82f89d1d281
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.0.8"
dynamic_color: dynamic_color:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -144,6 +144,8 @@ dependencies:
disable_battery_optimization: ^1.1.1 disable_battery_optimization: ^1.1.1
# 展开/收起 # 展开/收起
expandable: ^5.0.1 expandable: ^5.0.1
# 投屏
dlna_dart: ^0.0.8
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: