diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 5d39bc65..c78643db 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -69,7 +69,7 @@ class VideoCardH extends StatelessWidget { final double width = (boxConstraints.maxWidth - StyleString.cardSpace * 6 / - MediaQuery.of(context).textScaleFactor) / + MediaQuery.textScalerOf(context).scale(1.0)) / 2; return Container( constraints: const BoxConstraints(minHeight: 88), diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 43dd05ca..812e17cb 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -342,7 +342,8 @@ class VideoStat extends StatelessWidget { maxLines: 1, text: TextSpan( style: TextStyle( - fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + fontSize: MediaQuery.textScalerOf(context) + .scale(Theme.of(context).textTheme.labelSmall!.fontSize), color: Theme.of(context).colorScheme.outline, ), text: Utils.formatTimestampToRelativeTime(videoItem.pubdate)), diff --git a/lib/http/api.dart b/lib/http/api.dart index c8edf863..1731519b 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -467,4 +467,7 @@ class Api { /// page_size static const getSeasonDetailApi = '/x/polymer/web-space/seasons_archives_list'; + + /// 获取未读动态数 + static const getUnreadDynamic = '/x/web-interface/dynamic/entrance'; } diff --git a/lib/http/common.dart b/lib/http/common.dart new file mode 100644 index 00000000..d711a7e7 --- /dev/null +++ b/lib/http/common.dart @@ -0,0 +1,17 @@ +import 'index.dart'; + +class CommonHttp { + static Future unReadDynamic() async { + var res = await Request().get(Api.getUnreadDynamic, + data: {'alltype_offset': 0, 'video_offset': '', 'article_offset': 0}); + if (res.data['code'] == 0) { + return {'status': true, 'data': res.data['data']['dyn_basic_infos']}; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } +} diff --git a/lib/main.dart b/lib/main.dart index 29b8d118..f834d5d2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -158,9 +158,8 @@ class MyApp extends StatelessWidget { return FlutterSmartDialog( toastBuilder: (String msg) => CustomToast(msg: msg), child: MediaQuery( - data: MediaQuery.of(context).copyWith( - textScaleFactor: - MediaQuery.of(context).textScaleFactor * textScale), + data: MediaQuery.of(context) + .copyWith(textScaler: TextScaler.linear(textScale)), child: child!, ), ); diff --git a/lib/models/common/tab_type.dart b/lib/models/common/tab_type.dart index 90d19029..e530d7e5 100644 --- a/lib/models/common/tab_type.dart +++ b/lib/models/common/tab_type.dart @@ -9,6 +9,7 @@ enum TabType { live, rcmd, hot, bangumi } extension TabTypeDesc on TabType { String get description => ['直播', '推荐', '热门', '番剧'][index]; + String get id => ['live', 'rcmd', 'hot', 'bangumi'][index]; } List tabsConfig = [ diff --git a/lib/pages/bangumi/view.dart b/lib/pages/bangumi/view.dart index 9a101f16..560f09a5 100644 --- a/lib/pages/bangumi/view.dart +++ b/lib/pages/bangumi/view.dart @@ -224,7 +224,7 @@ class _BangumiPageState extends State // 列数 crossAxisCount: 3, mainAxisExtent: Get.size.width / 3 / 0.65 + - 32 * MediaQuery.of(context).textScaleFactor, + MediaQuery.textScalerOf(context).scale(32.0), ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index 614d103a..64510ee1 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -8,12 +8,13 @@ import 'package:pilipala/utils/storage.dart'; class HomeController extends GetxController with GetTickerProviderStateMixin { bool flag = false; - late List tabs; + late RxList tabs = [].obs; RxInt initialIndex = 1.obs; late TabController tabController; late List tabsCtrList; late List tabsPageList; Box userInfoCache = GStrorage.userInfo; + Box settingStorage = GStrorage.setting; RxBool userLogin = false.obs; RxString userFace = ''.obs; var userInfo; @@ -21,6 +22,8 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { late final StreamController searchBarStream = StreamController.broadcast(); late bool hideSearchBar; + late List defaultTabs; + late List tabbarSort; @override void onInit() { @@ -28,34 +31,10 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { userInfo = userInfoCache.get('userInfoCache'); userLogin.value = userInfo != null; userFace.value = userInfo != null ? userInfo.face : ''; - // 进行tabs配置 - tabs = tabsConfig; - tabsCtrList = tabsConfig.map((e) => e['ctr']).toList(); - tabsPageList = tabsConfig.map((e) => e['page']).toList(); - - tabController = TabController( - initialIndex: initialIndex.value, - length: tabs.length, - vsync: this, - ); + setTabConfig(); hideSearchBar = setting.get(SettingBoxKey.hideSearchBar, defaultValue: true); - - // 监听 tabController 切换 - tabController.animation!.addListener(() { - if (tabController.indexIsChanging) { - if (initialIndex.value != tabController.index) { - initialIndex.value = tabController.index; - } - } else { - final int temp = tabController.animation!.value.round(); - if (initialIndex.value != temp) { - initialIndex.value = temp; - tabController.index = initialIndex.value; - } - } - }); } void onRefresh() { @@ -77,4 +56,42 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { if (val) return; userFace.value = userInfo != null ? userInfo.face : ''; } + + void setTabConfig() async { + defaultTabs = tabsConfig; + tabbarSort = settingStorage.get(SettingBoxKey.tabbarSort, + defaultValue: ['live', 'rcmd', 'hot', 'bangumi']); + + tabs.value = defaultTabs + .where((i) => tabbarSort.contains((i['type'] as TabType).id)) + .toList(); + + if (tabbarSort.contains(TabType.rcmd.id)) { + initialIndex.value = tabbarSort.indexOf(TabType.rcmd.id); + } else { + initialIndex.value = 0; + } + tabsCtrList = tabs.map((e) => e['ctr']).toList(); + tabsPageList = tabs.map((e) => e['page']).toList(); + + tabController = TabController( + initialIndex: initialIndex.value, + length: tabs.length, + vsync: this, + ); + // 监听 tabController 切换 + tabController.animation!.addListener(() { + if (tabController.indexIsChanging) { + if (initialIndex.value != tabController.index) { + initialIndex.value = tabController.index; + } + } else { + final int temp = tabController.animation!.value.round(); + if (initialIndex.value != temp) { + initialIndex.value = temp; + tabController.index = initialIndex.value; + } + } + }); + } } diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index f9823334..c31460fb 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -90,7 +90,11 @@ class _HomePageState extends State ctr: _homeController, callback: showUserBottomSheet, ), - const CustomTabs(), + if (_homeController.tabs.length > 1) ...[ + const CustomTabs(), + ] else ...[ + const SizedBox(height: 6), + ], Expanded( child: TabBarView( controller: _homeController.tabController, @@ -250,17 +254,6 @@ class CustomTabs extends StatefulWidget { class _CustomTabsState extends State { final HomeController _homeController = Get.put(HomeController()); - int currentTabIndex = 1; - - @override - void initState() { - super.initState(); - _homeController.tabController.addListener(listen); - } - - void listen() { - _homeController.initialIndex.value = _homeController.tabController.index; - } void onTap(int index) { feedBack(); @@ -271,34 +264,30 @@ class _CustomTabsState extends State { _homeController.tabController.index = index; } - @override - void dispose() { - super.dispose(); - _homeController.tabController.removeListener(listen); - } - @override Widget build(BuildContext context) { return Container( height: 44, margin: const EdgeInsets.only(top: 4), - child: ListView.separated( - padding: const EdgeInsets.symmetric(horizontal: 14.0), - scrollDirection: Axis.horizontal, - itemCount: _homeController.tabs.length, - separatorBuilder: (BuildContext context, int index) { - return const SizedBox(width: 10); - }, - itemBuilder: (BuildContext context, int index) { - String label = _homeController.tabs[index]['label']; - return Obx( - () => CustomChip( - onTap: () => onTap(index), - label: label, - selected: index == _homeController.initialIndex.value, - ), - ); - }, + child: Obx( + () => ListView.separated( + padding: const EdgeInsets.symmetric(horizontal: 14.0), + scrollDirection: Axis.horizontal, + itemCount: _homeController.tabs.length, + separatorBuilder: (BuildContext context, int index) { + return const SizedBox(width: 10); + }, + itemBuilder: (BuildContext context, int index) { + String label = _homeController.tabs[index]['label']; + return Obx( + () => CustomChip( + onTap: () => onTap(index), + label: label, + selected: index == _homeController.initialIndex.value, + ), + ); + }, + ), ), ); } diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index 302d226d..f693acf1 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -162,8 +162,9 @@ class _LivePageState extends State crossAxisCount: crossAxisCount, mainAxisExtent: Get.size.width / crossAxisCount / StyleString.aspectRatio + - (crossAxisCount == 1 ? 48 : 68) * - MediaQuery.of(context).textScaleFactor, + MediaQuery.textScalerOf(context).scale( + (crossAxisCount == 1 ? 48 : 68), + ), ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index 15962da4..a55c143e 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -1,9 +1,11 @@ import 'dart:async'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.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'; @@ -27,6 +29,7 @@ class MainController extends GetxController { size: 21, ), 'label': "首页", + 'count': 0, }, { 'icon': const Icon( @@ -38,6 +41,7 @@ class MainController extends GetxController { size: 21, ), 'label': "动态", + 'count': 0, }, { 'icon': const Icon( @@ -49,6 +53,7 @@ class MainController extends GetxController { size: 21, ), 'label': "媒体库", + 'count': 0, } ].obs; final StreamController bottomBarStream = @@ -56,6 +61,10 @@ class MainController extends GetxController { Box setting = GStrorage.setting; DateTime? _lastPressedAt; late bool hideTabBar; + late PageController pageController; + int selectedIndex = 0; + Box userInfoCache = GStrorage.userInfo; + RxBool userLogin = false.obs; @override void onInit() { @@ -64,17 +73,47 @@ class MainController extends GetxController { Utils.checkUpdata(); } hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: true); + var userInfo = userInfoCache.get('userInfoCache'); + userLogin.value = userInfo != null; + getUnreadDynamic(); } - Future onBackPressed(BuildContext context) { + void onBackPressed(BuildContext context) { if (_lastPressedAt == null || DateTime.now().difference(_lastPressedAt!) > const Duration(seconds: 2)) { // 两次点击时间间隔超过2秒,重新记录时间戳 _lastPressedAt = DateTime.now(); + if (selectedIndex != 0) { + pageController.jumpTo(0); + } SmartDialog.showToast("再按一次退出Pili"); - return Future.value(false); // 不退出应用 + return; // 不退出应用 } - return Future.value(true); // 退出应用 + SystemNavigator.pop(); // 退出应用 + } + + void getUnreadDynamic() async { + if (!userLogin.value) { + return; + } + int dynamicItemIndex = + navigationBars.indexWhere((item) => item['label'] == "动态"); + var res = await CommonHttp.unReadDynamic(); + var data = res['data']; + if (dynamicItemIndex != -1) { + navigationBars[dynamicItemIndex]['count'] = + data == null ? 0 : data.length; // 修改 count 属性为新的值 + } + navigationBars.refresh(); + } + + void clearUnread() async { + int dynamicItemIndex = + navigationBars.indexWhere((item) => item['label'] == "动态"); + if (dynamicItemIndex != -1) { + navigationBars[dynamicItemIndex]['count'] = 0; // 修改 count 属性为新的值 + } + navigationBars.refresh(); } } diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index eb3444e1..5a570a8f 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -24,8 +24,6 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { final DynamicsController _dynamicController = Get.put(DynamicsController()); final MediaController _mediaController = Get.put(MediaController()); - PageController? _pageController; - int selectedIndex = 0; int? _lastSelectTime; //上次点击时间 Box setting = GStrorage.setting; late bool enableMYBar; @@ -34,13 +32,14 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { void initState() { super.initState(); _lastSelectTime = DateTime.now().millisecondsSinceEpoch; - _pageController = PageController(initialPage: selectedIndex); + _mainController.pageController = + PageController(initialPage: _mainController.selectedIndex); enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true); } void setIndex(int value) async { feedBack(); - _pageController!.jumpToPage(value); + _mainController.pageController.jumpToPage(value); var currentPage = _mainController.pages[value]; if (currentPage is HomePage) { if (_homeController.flag) { @@ -68,6 +67,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { _lastSelectTime = DateTime.now().millisecondsSinceEpoch; } _dynamicController.flag = true; + _mainController.clearUnread(); } else { _dynamicController.flag = false; } @@ -94,14 +94,17 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { localCache.put('sheetHeight', sheetHeight); localCache.put('statusBarHeight', statusBarHeight); return PopScope( - onPopInvoked: (bool status) => _mainController.onBackPressed(context), + canPop: false, + onPopInvoked: (bool didPop) async { + _mainController.onBackPressed(context); + }, child: Scaffold( extendBody: true, body: PageView( physics: const NeverScrollableScrollPhysics(), - controller: _pageController, + controller: _mainController.pageController, onPageChanged: (index) { - selectedIndex = index; + _mainController.selectedIndex = index; setState(() {}); }, children: _mainController.pages, @@ -116,36 +119,48 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { curve: Curves.easeInOutCubicEmphasized, duration: const Duration(milliseconds: 500), offset: Offset(0, snapshot.data ? 0 : 1), - child: enableMYBar - ? NavigationBar( - onDestinationSelected: (value) => setIndex(value), - selectedIndex: selectedIndex, - destinations: [ - ..._mainController.navigationBars.map((e) { - return NavigationDestination( - icon: e['icon'], - selectedIcon: e['selectIcon'], - label: e['label'], - ); - }).toList(), - ], - ) - : BottomNavigationBar( - currentIndex: selectedIndex, - onTap: (value) => setIndex(value), - iconSize: 16, - selectedFontSize: 12, - unselectedFontSize: 12, - items: [ - ..._mainController.navigationBars.map((e) { - return BottomNavigationBarItem( - icon: e['icon'], - activeIcon: e['selectIcon'], - label: e['label'], - ); - }).toList(), - ], - ), + child: Obx( + () => enableMYBar + ? NavigationBar( + onDestinationSelected: (value) => setIndex(value), + selectedIndex: _mainController.selectedIndex, + destinations: [ + ..._mainController.navigationBars.map((e) { + return NavigationDestination( + icon: Badge( + label: Text(e['count'].toString()), + padding: const EdgeInsets.fromLTRB(6, 0, 6, 0), + isLabelVisible: e['count'] > 0, + child: e['icon'], + ), + selectedIcon: e['selectIcon'], + label: e['label'], + ); + }).toList(), + ], + ) + : BottomNavigationBar( + currentIndex: _mainController.selectedIndex, + onTap: (value) => setIndex(value), + iconSize: 16, + selectedFontSize: 12, + unselectedFontSize: 12, + items: [ + ..._mainController.navigationBars.map((e) { + return BottomNavigationBarItem( + icon: Badge( + label: Text(e['count'].toString()), + padding: const EdgeInsets.fromLTRB(6, 0, 6, 0), + isLabelVisible: e['count'] > 0, + child: e['icon'], + ), + activeIcon: e['selectIcon'], + label: e['label'], + ); + }).toList(), + ], + ), + ), ); }, ), diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index 9bfac15c..c3bad4b9 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -163,7 +163,7 @@ class _MediaPageState extends State // const SizedBox(height: 10), SizedBox( width: double.infinity, - height: 200 * MediaQuery.of(context).textScaleFactor, + height: MediaQuery.textScalerOf(context).scale(200), child: FutureBuilder( future: _futureBuilderFuture, builder: (context, snapshot) { diff --git a/lib/pages/search_panel/widgets/article_panel.dart b/lib/pages/search_panel/widgets/article_panel.dart index 6e73151a..35e39640 100644 --- a/lib/pages/search_panel/widgets/article_panel.dart +++ b/lib/pages/search_panel/widgets/article_panel.dart @@ -26,10 +26,9 @@ Widget searchArticlePanel(BuildContext context, ctr, list) { StyleString.safeSpace, 5, StyleString.safeSpace, 5), child: LayoutBuilder(builder: (context, boxConstraints) { double width = (boxConstraints.maxWidth - - StyleString.cardSpace * - 6 / - MediaQuery.of(context).textScaleFactor) / - 2; + StyleString.cardSpace * + 6 / + MediaQuery.textScalerOf(context).scale(2.0)); return Container( constraints: const BoxConstraints(minHeight: 88), height: width / StyleString.aspectRatio, diff --git a/lib/pages/search_panel/widgets/live_panel.dart b/lib/pages/search_panel/widgets/live_panel.dart index 606b44f6..6fb5f5b8 100644 --- a/lib/pages/search_panel/widgets/live_panel.dart +++ b/lib/pages/search_panel/widgets/live_panel.dart @@ -17,7 +17,7 @@ Widget searchLivePanel(BuildContext context, ctr, list) { mainAxisSpacing: StyleString.cardSpace + 3, mainAxisExtent: MediaQuery.sizeOf(context).width / 2 / StyleString.aspectRatio + - 66 * MediaQuery.of(context).textScaleFactor), + MediaQuery.textScalerOf(context).scale(66.0)), itemCount: list.length, itemBuilder: (context, index) { return LiveItem(liveItem: list![index]); diff --git a/lib/pages/search_panel/widgets/media_bangumi_panel.dart b/lib/pages/search_panel/widgets/media_bangumi_panel.dart index b8ae8d2e..18799d3a 100644 --- a/lib/pages/search_panel/widgets/media_bangumi_panel.dart +++ b/lib/pages/search_panel/widgets/media_bangumi_panel.dart @@ -67,11 +67,11 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) { TextSpan( text: i['text'], style: TextStyle( - fontSize: Theme.of(context) + fontSize: MediaQuery.textScalerOf(context) + .scale(Theme.of(context) .textTheme .titleSmall! - .fontSize! * - MediaQuery.of(context).textScaleFactor, + .fontSize!), fontWeight: FontWeight.bold, color: i['type'] == 'em' ? Theme.of(context).colorScheme.primary diff --git a/lib/pages/setting/pages/home_tabbar_set.dart b/lib/pages/setting/pages/home_tabbar_set.dart new file mode 100644 index 00000000..445ca4f5 --- /dev/null +++ b/lib/pages/setting/pages/home_tabbar_set.dart @@ -0,0 +1,90 @@ +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'; + +class TabbarSetPage extends StatefulWidget { + const TabbarSetPage({super.key}); + + @override + State createState() => _TabbarSetPageState(); +} + +class _TabbarSetPageState extends State { + Box settingStorage = GStrorage.setting; + late List defaultTabs; + late List tabbarSort; + + @override + void initState() { + super.initState(); + defaultTabs = tabsConfig; + tabbarSort = settingStorage.get(SettingBoxKey.tabbarSort, + defaultValue: ['live', 'rcmd', 'hot', 'bangumi']); + } + + void saveEdit() { + List sortedTabbar = defaultTabs + .where((i) => tabbarSort.contains((i['type'] as TabType).id)) + .map((i) => (i['type'] as TabType).id) + .toList(); + if (sortedTabbar.isEmpty) { + SmartDialog.showToast('请至少设置一项!'); + return; + } + settingStorage.put(SettingBoxKey.tabbarSort, sortedTabbar); + SmartDialog.showToast('保存成功,下次启动时生效'); + } + + void onReorder(int oldIndex, int newIndex) { + setState(() { + if (newIndex > oldIndex) { + newIndex -= 1; + } + final tabsItem = defaultTabs.removeAt(oldIndex); + defaultTabs.insert(newIndex, tabsItem); + }); + } + + @override + Widget build(BuildContext context) { + final listTiles = [ + for (int i = 0; i < defaultTabs.length; i++) ...[ + CheckboxListTile( + key: Key(defaultTabs[i]['label']), + value: tabbarSort.contains((defaultTabs[i]['type'] as TabType).id), + onChanged: (bool? newValue) { + String tabTypeId = (defaultTabs[i]['type'] as TabType).id; + if (!newValue!) { + tabbarSort.remove(tabTypeId); + } else { + tabbarSort.add(tabTypeId); + } + setState(() {}); + }, + title: Text(defaultTabs[i]['label']), + secondary: const Icon(Icons.drag_indicator_rounded), + ) + ] + ]; + + return Scaffold( + appBar: AppBar( + title: const Text('Tabbar编辑'), + 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, + ), + ); + } +} diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index a4e39aa0..57ec110a 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -254,6 +254,11 @@ class _StyleSettingState extends State { onTap: () => Get.toNamed('/fontSizeSetting'), title: Text('字体大小', style: titleStyle), ), + ListTile( + dense: false, + onTap: () => Get.toNamed('/tabbarSetting'), + title: Text('首页tabbar', style: titleStyle), + ), if (Platform.isAndroid) ListTile( dense: false, diff --git a/lib/pages/video/detail/related/view.dart b/lib/pages/video/detail/related/view.dart index 5b6fdc96..51c296f3 100644 --- a/lib/pages/video/detail/related/view.dart +++ b/lib/pages/video/detail/related/view.dart @@ -9,7 +9,8 @@ import './controller.dart'; class RelatedVideoPanel extends StatelessWidget { final ReleatedController _releatedController = - Get.put(ReleatedController(), tag: Get.arguments['heroTag']); + Get.put(ReleatedController(), tag: Get.arguments?['heroTag']); + RelatedVideoPanel({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index cd541822..01c95adc 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -2,12 +2,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/storage.dart'; class VideoReplyNewDialog extends StatefulWidget { final int? oid; @@ -34,23 +32,16 @@ class _VideoReplyNewDialogState extends State final TextEditingController _replyContentController = TextEditingController(); final FocusNode replyContentFocusNode = FocusNode(); final GlobalKey _formKey = GlobalKey(); - bool ableClean = false; - Timer? timer; - Box localCache = GStrorage.localCache; - late double sheetHeight; @override void initState() { super.initState(); // 监听输入框聚焦 // replyContentFocusNode.addListener(_onFocus); - _replyContentController.addListener(_printLatestValue); // 界面观察者 必须 WidgetsBinding.instance.addObserver(this); // 自动聚焦 _autoFocus(); - - sheetHeight = localCache.get('sheetHeight'); } _autoFocus() async { @@ -60,12 +51,6 @@ class _VideoReplyNewDialogState extends State } } - _printLatestValue() { - setState(() { - ableClean = _replyContentController.text != ''; - }); - } - Future submitReplyAdd() async { feedBack(); String message = _replyContentController.text; @@ -113,12 +98,14 @@ class _VideoReplyNewDialogState extends State mainAxisSize: MainAxisSize.min, children: [ ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 200, - ), + constraints: const BoxConstraints( + maxHeight: 200, + minHeight: 120, + ), + child: Container( + padding: const EdgeInsets.only( + top: 12, right: 15, left: 15, bottom: 10), child: SingleChildScrollView( - padding: const EdgeInsets.only( - top: 6, right: 15, left: 15, bottom: 10), child: Form( key: _formKey, autovalidateMode: AutovalidateMode.onUserInteraction, @@ -137,7 +124,9 @@ class _VideoReplyNewDialogState extends State style: Theme.of(context).textTheme.bodyLarge, ), ), - )), + ), + ), + ), Divider( height: 1, color: Theme.of(context).dividerColor.withOpacity(0.1), @@ -152,34 +141,23 @@ class _VideoReplyNewDialogState extends State width: 36, height: 36, child: IconButton( - onPressed: () { - if (keyboardHeight > 0) { - FocusScope.of(context).unfocus(); - } else { - FocusScope.of(context) - .requestFocus(replyContentFocusNode); - } - }, - icon: Icon( - keyboardHeight > 0 - ? Icons.keyboard_hide - : Icons.keyboard, - size: 22, - color: Theme.of(context).colorScheme.onBackground), - highlightColor: - Theme.of(context).colorScheme.onInverseSurface, - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: - MaterialStateProperty.resolveWith((states) { - // 如果按钮被按下,返回高亮颜色 - if (states.contains(MaterialState.pressed)) { - return Theme.of(context).highlightColor; - } - // 默认状态下,返回透明颜色 - return Colors.transparent; - }), - )), + onPressed: () { + FocusScope.of(context) + .requestFocus(replyContentFocusNode); + }, + icon: Icon(Icons.keyboard, + size: 22, + color: Theme.of(context).colorScheme.onBackground), + highlightColor: + Theme.of(context).colorScheme.onInverseSurface, + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: + MaterialStateProperty.resolveWith((states) { + return Theme.of(context).highlightColor; + }), + ), + ), ), const Spacer(), TextButton( @@ -200,22 +178,3 @@ class _VideoReplyNewDialogState extends State ); } } - -typedef DebounceCallback = void Function(); - -class Debouncer { - DebounceCallback? callback; - final int? milliseconds; - Timer? _timer; - - Debouncer({this.milliseconds}); - - run(DebounceCallback callback) { - if (_timer != null) { - _timer!.cancel(); - } - _timer = Timer(Duration(milliseconds: milliseconds!), () { - callback(); - }); - } -} diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index d063c3b2..59fa39b2 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -235,7 +235,7 @@ class _VideoDetailPageState extends State videoIntroController.isPaused = false; if (_extendNestCtr.position.pixels == 0 && autoplay) { await Future.delayed(const Duration(milliseconds: 300)); - plPlayerController!.seekTo(videoDetailController.defaultST); + plPlayerController?.seekTo(videoDetailController.defaultST); plPlayerController?.play(); } plPlayerController?.addStatusLister(playerListener); diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index fd3f02c9..d35987f8 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -621,7 +621,7 @@ class PlPlayerController { if (duration.value.inSeconds != 0) { if (type != 'slider') { /// 拖动进度条调节时,不等待第一帧,防止抖动 - await _videoPlayerController!.stream.buffer.first; + await _videoPlayerController?.stream.buffer.first; } await _videoPlayerController?.seek(position); // if (playerStatus.stopped) { @@ -786,7 +786,7 @@ class PlPlayerController { volume.value = volumeNew; try { - FlutterVolumeController.showSystemUI = false; + FlutterVolumeController.updateShowSystemUI(false); await FlutterVolumeController.setVolume(volumeNew); } catch (err) { print(err); @@ -1086,12 +1086,13 @@ class PlPlayerController { localCache.put(LocalCacheKey.danmakuOpacity, opacityVal); localCache.put(LocalCacheKey.danmakuFontScale, fontSizeVal); localCache.put(LocalCacheKey.danmakuDuration, danmakuDurationVal); - - var pp = _videoPlayerController!.platform as NativePlayer; - await pp.setProperty('audio-files', ''); - removeListeners(); - await _videoPlayerController?.dispose(); - _videoPlayerController = null; + if (_videoPlayerController != null) { + var pp = _videoPlayerController!.platform as NativePlayer; + await pp.setProperty('audio-files', ''); + removeListeners(); + await _videoPlayerController?.dispose(); + _videoPlayerController = null; + } _instance = null; // 关闭所有视频页面恢复亮度 resetBrightness(); diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 860342ef..3980453b 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:audio_video_progress_bar/audio_video_progress_bar.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_volume_controller/flutter_volume_controller.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; @@ -130,7 +129,7 @@ class _PLVideoPlayerState extends State setting.get(SettingBoxKey.enableBackgroundPlay, defaultValue: false); Future.microtask(() async { try { - FlutterVolumeController.showSystemUI = true; + FlutterVolumeController.updateShowSystemUI(true); _ctr.volumeValue.value = (await FlutterVolumeController.getVolume())!; FlutterVolumeController.addListener((double value) { if (mounted && !_ctr.volumeInterceptEventStream.value) { @@ -154,7 +153,7 @@ class _PLVideoPlayerState extends State Future setVolume(double value) async { try { - FlutterVolumeController.showSystemUI = false; + FlutterVolumeController.updateShowSystemUI(false); await FlutterVolumeController.setVolume(value); } catch (_) {} _ctr.volumeValue.value = value; @@ -703,7 +702,7 @@ class _PLVideoPlayerState extends State ), ); } else { - return nil; + return const SizedBox(); } }), diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index d6b2f9a9..5a86ecf6 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -36,6 +36,7 @@ import '../pages/setting/index.dart'; import '../pages/setting/pages/color_select.dart'; import '../pages/setting/pages/display_mode.dart'; import '../pages/setting/pages/font_size_select.dart'; +import '../pages/setting/pages/home_tabbar_set.dart'; import '../pages/setting/pages/play_speed_set.dart'; import '../pages/setting/recommend_setting.dart'; import '../pages/setting/play_setting.dart'; @@ -114,6 +115,8 @@ class Routes { // CustomGetPage(name: '/blackListPage', page: () => const BlackListPage()), CustomGetPage(name: '/colorSetting', page: () => const ColorSelectPage()), + // 首页tabbar + CustomGetPage(name: '/tabbarSetting', page: () => const TabbarSetPage()), CustomGetPage( name: '/fontSizeSetting', page: () => const FontSizeSelectPage()), // 屏幕帧率 diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index fdb9ee02..b689145d 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -151,7 +151,8 @@ class SettingBoxKey { customRows = 'customRows', // 自定义列 enableMYBar = 'enableMYBar', hideSearchBar = 'hideSearchBar', // 收起顶栏 - hideTabBar = 'hideTabBar'; // 收起底栏 + hideTabBar = 'hideTabBar', // 收起底栏 + tabbarSort = 'tabbarSort'; // 首页tabbar } class LocalCacheKey {