From 6c3062ba2d60f4352a078178dc0b85a2ff336414 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Fri, 14 Feb 2025 16:10:10 +0800 Subject: [PATCH] feat: pure black theme Closes #254 Signed-off-by: bggRGjQaUbCoE --- lib/common/widgets/list_sheet.dart | 333 +++++++++--------- lib/common/widgets/stat/danmu.dart | 3 +- lib/common/widgets/stat/view.dart | 3 +- lib/main.dart | 8 +- lib/pages/bangumi/introduction/view.dart | 1 + lib/pages/dynamics/view.dart | 1 - lib/pages/dynamics/widgets/dynamic_panel.dart | 2 +- lib/pages/home/view.dart | 1 + lib/pages/member/new/member_page.dart | 23 +- lib/pages/search/widgets/hot_keyword.dart | 1 + lib/pages/search/widgets/search_text.dart | 8 +- .../search_panel/widgets/article_panel.dart | 2 - .../search_panel/widgets/user_panel.dart | 2 - .../search_panel/widgets/video_panel.dart | 64 +--- lib/pages/setting/widgets/model.dart | 12 + lib/pages/video/detail/introduction/view.dart | 2 + .../introduction/widgets/fav_panel.dart | 1 + .../introduction/widgets/group_panel.dart | 1 + lib/pages/video/detail/related/view.dart | 8 +- .../detail/reply/widgets/reply_item.dart | 5 +- .../detail/reply/widgets/reply_item_grpc.dart | 3 +- lib/pages/video/detail/view.dart | 122 +++---- lib/utils/extension.dart | 18 + lib/utils/storage.dart | 4 + lib/utils/utils.dart | 50 +++ 25 files changed, 361 insertions(+), 317 deletions(-) diff --git a/lib/common/widgets/list_sheet.dart b/lib/common/widgets/list_sheet.dart index 916e3a8a..381473bc 100644 --- a/lib/common/widgets/list_sheet.dart +++ b/lib/common/widgets/list_sheet.dart @@ -275,144 +275,145 @@ class _ListSheetContentState extends State @override Widget build(BuildContext context) { - return Container( - height: Utils.getSheetHeight(context), + return Material( color: Theme.of(context).colorScheme.surface, - child: Column( - children: [ - Container( - height: 45, - padding: EdgeInsets.symmetric( - horizontal: widget.showTitle != false ? 14 : 6), - child: Row( - children: [ - if (widget.showTitle != false) - Text( - '合集(${_isList ? widget.season.epCount : episodes?.length ?? ''})', - style: Theme.of(context).textTheme.titleMedium, - ), - StreamBuilder( - stream: _favStream?.stream, - builder: (context, snapshot) => snapshot.hasData - ? mediumButton( - tooltip: _seasonFav == 1 ? '取消订阅' : '订阅', - icon: _seasonFav == 1 - ? Icons.notifications_off_outlined - : Icons.notifications_active_outlined, - onPressed: () async { - dynamic result = await VideoHttp.seasonFav( - isFav: _seasonFav == 1, - seasonId: widget.season.id, - ); - if (result['status']) { - SmartDialog.showToast( - '${_seasonFav == 1 ? '取消' : ''}订阅成功'); - _seasonFav = _seasonFav == 1 ? 0 : 1; - _favStream?.add(_seasonFav); - } else { - SmartDialog.showToast(result['msg']); - } - }, - ) - : const SizedBox.shrink(), - ), - mediumButton( - tooltip: '跳至顶部', - icon: Icons.vertical_align_top, - onPressed: () { - try { - itemScrollController[_ctr?.index ?? 0].scrollTo( - index: !reverse[_ctr?.index ?? 0] - ? 0 - : _isList - ? widget.season.sections[_ctr?.index].episodes - .length - - 1 - : episodes.length - 1, - duration: const Duration(milliseconds: 200), - ); - } catch (_) {} - }, - ), - mediumButton( - tooltip: '跳至底部', - icon: Icons.vertical_align_bottom, - onPressed: () { - try { - itemScrollController[_ctr?.index ?? 0].scrollTo( - index: !reverse[_ctr?.index ?? 0] - ? _isList - ? widget.season.sections[_ctr?.index].episodes - .length - - 1 - : episodes.length - 1 - : 0, - duration: const Duration(milliseconds: 200), - ); - } catch (_) {} - }, - ), - mediumButton( - tooltip: '跳至当前', - icon: Icons.my_location, - onPressed: () async { - if (_ctr != null && _ctr?.index != (_index)) { - _ctr?.animateTo(_index); - await Future.delayed(const Duration(milliseconds: 225)); - } - try { - itemScrollController[_ctr?.index ?? 0].scrollTo( - index: currentIndex, - duration: const Duration(milliseconds: 200), - ); - } catch (_) {} - }, - ), - if (widget.isSupportReverse == true) - if (!_isList) - _reverseButton - else - StreamBuilder( - stream: _indexStream?.stream, - initialData: _index, - builder: (context, snapshot) { - return snapshot.data == _index - ? _reverseButton - : const SizedBox.shrink(); - }, + child: SizedBox( + height: Utils.getSheetHeight(context), + child: Column( + children: [ + Container( + height: 45, + padding: EdgeInsets.symmetric( + horizontal: widget.showTitle != false ? 14 : 6), + child: Row( + children: [ + if (widget.showTitle != false) + Text( + '合集(${_isList ? widget.season.epCount : episodes?.length ?? ''})', + style: Theme.of(context).textTheme.titleMedium, ), - const Spacer(), - StreamBuilder( - stream: _indexStream?.stream, - initialData: _index, - builder: (context, snapshot) => mediumButton( - tooltip: reverse[snapshot.data] ? '顺序' : '倒序', - icon: !reverse[snapshot.data] - ? MdiIcons.sortNumericAscending - : MdiIcons.sortNumericDescending, + StreamBuilder( + stream: _favStream?.stream, + builder: (context, snapshot) => snapshot.hasData + ? mediumButton( + tooltip: _seasonFav == 1 ? '取消订阅' : '订阅', + icon: _seasonFav == 1 + ? Icons.notifications_off_outlined + : Icons.notifications_active_outlined, + onPressed: () async { + dynamic result = await VideoHttp.seasonFav( + isFav: _seasonFav == 1, + seasonId: widget.season.id, + ); + if (result['status']) { + SmartDialog.showToast( + '${_seasonFav == 1 ? '取消' : ''}订阅成功'); + _seasonFav = _seasonFav == 1 ? 0 : 1; + _favStream?.add(_seasonFav); + } else { + SmartDialog.showToast(result['msg']); + } + }, + ) + : const SizedBox.shrink(), + ), + mediumButton( + tooltip: '跳至顶部', + icon: Icons.vertical_align_top, onPressed: () { - setState(() { - reverse[_ctr?.index ?? 0] = !reverse[_ctr?.index ?? 0]; - }); + try { + itemScrollController[_ctr?.index ?? 0].scrollTo( + index: !reverse[_ctr?.index ?? 0] + ? 0 + : _isList + ? widget.season.sections[_ctr?.index].episodes + .length - + 1 + : episodes.length - 1, + duration: const Duration(milliseconds: 200), + ); + } catch (_) {} }, ), - ), - if (widget.onClose != null) mediumButton( - tooltip: '关闭', - icon: Icons.close, - onPressed: widget.onClose, + tooltip: '跳至底部', + icon: Icons.vertical_align_bottom, + onPressed: () { + try { + itemScrollController[_ctr?.index ?? 0].scrollTo( + index: !reverse[_ctr?.index ?? 0] + ? _isList + ? widget.season.sections[_ctr?.index].episodes + .length - + 1 + : episodes.length - 1 + : 0, + duration: const Duration(milliseconds: 200), + ); + } catch (_) {} + }, ), - ], + mediumButton( + tooltip: '跳至当前', + icon: Icons.my_location, + onPressed: () async { + if (_ctr != null && _ctr?.index != (_index)) { + _ctr?.animateTo(_index); + await Future.delayed(const Duration(milliseconds: 225)); + } + try { + itemScrollController[_ctr?.index ?? 0].scrollTo( + index: currentIndex, + duration: const Duration(milliseconds: 200), + ); + } catch (_) {} + }, + ), + if (widget.isSupportReverse == true) + if (!_isList) + _reverseButton + else + StreamBuilder( + stream: _indexStream?.stream, + initialData: _index, + builder: (context, snapshot) { + return snapshot.data == _index + ? _reverseButton + : const SizedBox.shrink(); + }, + ), + const Spacer(), + StreamBuilder( + stream: _indexStream?.stream, + initialData: _index, + builder: (context, snapshot) => mediumButton( + tooltip: reverse[snapshot.data] ? '顺序' : '倒序', + icon: !reverse[snapshot.data] + ? MdiIcons.sortNumericAscending + : MdiIcons.sortNumericDescending, + onPressed: () { + setState(() { + reverse[_ctr?.index ?? 0] = + !reverse[_ctr?.index ?? 0]; + }); + }, + ), + ), + if (widget.onClose != null) + mediumButton( + tooltip: '关闭', + icon: Icons.close, + onPressed: widget.onClose, + ), + ], + ), ), - ), - Divider( - height: 1, - color: Theme.of(context).dividerColor.withOpacity(0.1), - ), - if (_isList) - Material( - child: TabBar( + Divider( + height: 1, + color: Theme.of(context).dividerColor.withOpacity(0.1), + ), + if (_isList) + TabBar( controller: _ctr, padding: const EdgeInsets.only(right: 60), isScrollable: true, @@ -422,20 +423,20 @@ class _ListSheetContentState extends State dividerHeight: 1, dividerColor: Theme.of(context).dividerColor.withOpacity(0.1), ), + Expanded( + child: _isList + ? TabBarView( + controller: _ctr, + children: List.generate( + widget.season.sections.length, + (index) => _buildBody( + index, widget.season.sections[index].episodes), + ), + ) + : _buildBody(null, episodes), ), - Expanded( - child: _isList - ? TabBarView( - controller: _ctr, - children: List.generate( - widget.season.sections.length, - (index) => _buildBody( - index, widget.season.sections[index].episodes), - ), - ) - : _buildBody(null, episodes), - ), - ], + ], + ), ), ); } @@ -464,30 +465,28 @@ class _ListSheetContentState extends State }, ); - Widget _buildBody(i, episodes) => Material( - child: ScrollablePositionedList.separated( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).padding.bottom + 80, - ), - reverse: reverse[i ?? 0], - itemCount: episodes.length, - itemBuilder: (BuildContext context, int index) { - return buildEpisodeListItem( - episodes[index], - index, - episodes.length, - i != null - ? i == (_index) - ? currentIndex == index - : false - : currentIndex == index, - ); - }, - itemScrollController: itemScrollController[i ?? 0], - separatorBuilder: (context, index) => Divider( - height: 1, - color: Theme.of(context).dividerColor.withOpacity(0.1), - ), + Widget _buildBody(i, episodes) => ScrollablePositionedList.separated( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom + 80, + ), + reverse: reverse[i ?? 0], + itemCount: episodes.length, + itemBuilder: (BuildContext context, int index) { + return buildEpisodeListItem( + episodes[index], + index, + episodes.length, + i != null + ? i == (_index) + ? currentIndex == index + : false + : currentIndex == index, + ); + }, + itemScrollController: itemScrollController[i ?? 0], + separatorBuilder: (context, index) => Divider( + height: 1, + color: Theme.of(context).dividerColor.withOpacity(0.1), ), ); } diff --git a/lib/common/widgets/stat/danmu.dart b/lib/common/widgets/stat/danmu.dart index f3fb0ed1..74d1f917 100644 --- a/lib/common/widgets/stat/danmu.dart +++ b/lib/common/widgets/stat/danmu.dart @@ -6,13 +6,14 @@ Widget statDanMu({ String? theme, dynamic danmu, String? size, + Color? textColor, }) { Map colorObject = { 'white': Colors.white, 'gray': Theme.of(context).colorScheme.outline.withOpacity(0.8), 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.7), }; - Color color = colorObject[theme]!; + Color color = textColor ?? colorObject[theme]!; return Row( children: [ Icon( diff --git a/lib/common/widgets/stat/view.dart b/lib/common/widgets/stat/view.dart index df0829de..4ee4e13b 100644 --- a/lib/common/widgets/stat/view.dart +++ b/lib/common/widgets/stat/view.dart @@ -7,13 +7,14 @@ Widget statView({ dynamic view, String? size, String? goto, + Color? textColor, }) { Map colorObject = { 'white': Colors.white, 'gray': Theme.of(context).colorScheme.outline.withOpacity(0.8), 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.7), }; - Color color = colorObject[theme]!; + Color color = textColor ?? colorObject[theme]!; return Row( children: [ Icon( diff --git a/lib/main.dart b/lib/main.dart index 510db1c2..a54f6d0e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:PiliPlus/build_config.dart'; import 'package:PiliPlus/utils/cache_manage.dart'; +import 'package:PiliPlus/utils/utils.dart'; import 'package:flex_seed_scheme/flex_seed_scheme.dart'; import 'package:flutter/services.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; @@ -177,6 +178,7 @@ class MyApp extends StatelessWidget { // tones: FlexTones.soft(Brightness.dark), ); } + // 图片缓存 // PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20; return GetMaterialApp( @@ -229,7 +231,7 @@ class MyApp extends StatelessWidget { bool isDark = false, required FlexSchemeVariant variant, }) { - return ThemeData( + ThemeData themeData = ThemeData( colorScheme: colorScheme, useMaterial3: true, appBarTheme: AppBarTheme( @@ -282,6 +284,10 @@ class MyApp extends StatelessWidget { ), ), ); + if (isDark && GStorage.isPureBlackTheme) { + themeData = Utils.darkenTheme(themeData); + } + return themeData; } } diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index f30f0bcf..21194fc6 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -450,6 +450,7 @@ class _BangumiInfoState extends State return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return Material( + color: Theme.of(context).colorScheme.surface, child: Padding( padding: const EdgeInsets.only(top: 1), child: SizedBox( diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 17535839..32ef5482 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -136,7 +136,6 @@ class _DynamicsPageState extends State super.build(context); return Scaffold( resizeToAvoidBottomInset: false, - backgroundColor: Colors.transparent, appBar: AppBar( leading: upPanelPosition == UpPanelPosition.rightDrawer ? _createDynamicBtn(false) diff --git a/lib/pages/dynamics/widgets/dynamic_panel.dart b/lib/pages/dynamics/widgets/dynamic_panel.dart index 3519cbe7..f8e74055 100644 --- a/lib/pages/dynamics/widgets/dynamic_panel.dart +++ b/lib/pages/dynamics/widgets/dynamic_panel.dart @@ -39,7 +39,7 @@ class DynamicPanel extends StatelessWidget { child: Material( elevation: 0, clipBehavior: Clip.hardEdge, - color: Theme.of(context).cardColor.withOpacity(0.5), + color: Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5), ), diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 797410c5..34812b30 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -47,6 +47,7 @@ class _HomePageState extends State if (_homeController.tabs.length > 1) ...[ const SizedBox(height: 4), Material( + color: Theme.of(context).colorScheme.surface, child: SizedBox( height: 42, child: TabBar( diff --git a/lib/pages/member/new/member_page.dart b/lib/pages/member/new/member_page.dart index f5ed0d03..54d6d91c 100644 --- a/lib/pages/member/new/member_page.dart +++ b/lib/pages/member/new/member_page.dart @@ -134,14 +134,17 @@ class _MemberPageNewState extends State ); } - Widget get _buildTab => TabBar( - controller: _userController.tabController, - tabs: _userController.tabs, - onTap: (value) { - if (_userController.tabController?.indexIsChanging == false) { - _key.currentState?.outerController.animToTop(); - } - }, + Widget get _buildTab => Material( + color: Theme.of(context).colorScheme.surface, + child: TabBar( + controller: _userController.tabController, + tabs: _userController.tabs, + onTap: (value) { + if (_userController.tabController?.indexIsChanging == false) { + _key.currentState?.outerController.animToTop(); + } + }, + ), ); Widget get _buildBody => SafeArea( @@ -198,9 +201,7 @@ class _MemberPageNewState extends State bottom: needTab && (_userController.tab2?.length ?? -1) > 1 ? PreferredSize( preferredSize: Size.fromHeight(48), - child: Material( - child: _buildTab, - ), + child: _buildTab, ) : null, actions: [ diff --git a/lib/pages/search/widgets/hot_keyword.dart b/lib/pages/search/widgets/hot_keyword.dart index f231af6a..e38d70fa 100644 --- a/lib/pages/search/widgets/hot_keyword.dart +++ b/lib/pages/search/widgets/hot_keyword.dart @@ -23,6 +23,7 @@ class HotKeyword extends StatelessWidget { SizedBox( width: width! / 2 - 4, child: Material( + color: Colors.transparent, borderRadius: BorderRadius.circular(3), clipBehavior: Clip.hardEdge, child: InkWell( diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index d46cce25..9ec0cdee 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -31,9 +31,11 @@ class SearchText extends StatelessWidget { onTap: () { onTap?.call(text); }, - onLongPress: () { - onLongPress?.call(text); - }, + onLongPress: onLongPress != null + ? () { + onLongPress!(text); + } + : null, borderRadius: BorderRadius.circular(6), child: Padding( padding: padding ?? diff --git a/lib/pages/search_panel/widgets/article_panel.dart b/lib/pages/search_panel/widgets/article_panel.dart index a5eb5eef..f1f7c45e 100644 --- a/lib/pages/search_panel/widgets/article_panel.dart +++ b/lib/pages/search_panel/widgets/article_panel.dart @@ -294,7 +294,6 @@ class ArticlePanelController extends GetxController { await ctr.onRefresh(); SmartDialog.dismiss(); }, - onLongPress: (_) {}, bgColor: item['value'] == currentOrderFilterval.value ? Theme.of(context).colorScheme.secondaryContainer : null, @@ -328,7 +327,6 @@ class ArticlePanelController extends GetxController { await ctr.onRefresh(); SmartDialog.dismiss(); }, - onLongPress: (_) {}, bgColor: item['value'] == currentZoneFilterval.value ? Theme.of(context).colorScheme.secondaryContainer : null, diff --git a/lib/pages/search_panel/widgets/user_panel.dart b/lib/pages/search_panel/widgets/user_panel.dart index a7f13059..ff39f077 100644 --- a/lib/pages/search_panel/widgets/user_panel.dart +++ b/lib/pages/search_panel/widgets/user_panel.dart @@ -222,7 +222,6 @@ class UserPanelController extends GetxController { await ctr.onRefresh(); SmartDialog.dismiss(); }, - onLongPress: (_) {}, bgColor: item['value'] == currentOrderFilterval.value ? Theme.of(context).colorScheme.secondaryContainer : null, @@ -256,7 +255,6 @@ class UserPanelController extends GetxController { await ctr.onRefresh(); SmartDialog.dismiss(); }, - onLongPress: (_) {}, bgColor: item['value'] == currentUserTypeFilterval.value ? Theme.of(context).colorScheme.secondaryContainer : null, diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index 1c6d8034..072bcbf1 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -38,12 +38,15 @@ Widget searchVideoPanel(context, ctr, LoadingState loadingState) { // spacing: , children: [ for (var i in controller.filterList) ...[ - CustomFilterChip( - label: i['label'], - type: i['type'], - selectedType: controller.selectedType.value, - callFn: (bool selected) async { - debugPrint('selected: $selected'); + SearchText( + fontSize: 13, + text: i['label'], + bgColor: Colors.transparent, + textColor: + controller.selectedType.value == i['type'] + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + onTap: (value) async { controller.selectedType.value = i['type']; ctr.order.value = i['type'].toString().split('.').last; @@ -122,51 +125,6 @@ Widget searchVideoPanel(context, ctr, LoadingState loadingState) { ); } -class CustomFilterChip extends StatelessWidget { - const CustomFilterChip({ - this.label, - this.type, - this.selectedType, - this.callFn, - super.key, - }); - - final String? label; - final ArchiveFilterType? type; - final ArchiveFilterType? selectedType; - final Function? callFn; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 34, - child: FilterChip( - padding: const EdgeInsets.only(left: 8, right: 8), - labelPadding: EdgeInsets.zero, - label: Text( - label!, - style: const TextStyle(fontSize: 13), - ), - labelStyle: TextStyle( - color: type == selectedType - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline), - selected: type == selectedType, - showCheckmark: false, - shape: ContinuousRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - selectedColor: Colors.transparent, - // backgroundColor: - // Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), - backgroundColor: Colors.transparent, - side: BorderSide.none, - onSelected: (bool selected) => callFn?.call(selected), - ), - ); - } -} - class VideoPanelController extends GetxController { RxList filterList = [{}].obs; Rx selectedType = ArchiveFilterType.values.first.obs; @@ -304,7 +262,6 @@ class VideoPanelController extends GetxController { } }); }, - onLongPress: (_) {}, bgColor: currentPubTimeFilterval == -1 && (isFirst ? customPubBegin : customPubEnd) ? Theme.of(context).colorScheme.secondaryContainer @@ -380,7 +337,6 @@ class VideoPanelController extends GetxController { await ctr.onRefresh(); SmartDialog.dismiss(); }, - onLongPress: (_) {}, bgColor: item['value'] == currentPubTimeFilterval ? Theme.of(context) .colorScheme @@ -431,7 +387,6 @@ class VideoPanelController extends GetxController { await ctr.onRefresh(); SmartDialog.dismiss(); }, - onLongPress: (_) {}, bgColor: item['value'] == currentTimeFilterval ? Theme.of(context) .colorScheme @@ -469,7 +424,6 @@ class VideoPanelController extends GetxController { await ctr.onRefresh(); SmartDialog.dismiss(); }, - onLongPress: (_) {}, bgColor: item['value'] == currentZoneFilterval ? Theme.of(context) .colorScheme diff --git a/lib/pages/setting/widgets/model.dart b/lib/pages/setting/widgets/model.dart index 04c2aecd..dbcf6357 100644 --- a/lib/pages/setting/widgets/model.dart +++ b/lib/pages/setting/widgets/model.dart @@ -480,6 +480,18 @@ List get styleSettings => [ title: '主题模式', getSubtitle: () => '当前模式:${GStorage.themeType.description}', ), + SettingsModel( + settingsType: SettingsType.sw1tch, + leading: const Icon(Icons.invert_colors), + title: '纯黑主题', + setKey: SettingBoxKey.isPureBlackTheme, + defaultVal: false, + onChanged: (value) { + if (Theme.of(Get.context!).brightness == Brightness.dark) { + Get.forceAppUpdate(); + } + }, + ), SettingsModel( settingsType: SettingsType.normal, onTap: (setState) => Get.toNamed('/colorSetting'), diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 9a628785..f9b40e03 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -557,6 +557,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ? videoDetail.stat?.view ?? '-' : videoItem['stat']?.view ?? '-', size: 'medium', + textColor: t.colorScheme.outline, ), const SizedBox(width: 10), statDanMu( @@ -566,6 +567,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ? videoDetail.stat?.danmu ?? '-' : videoItem['stat']?.danmu ?? '-', size: 'medium', + textColor: t.colorScheme.outline, ), const SizedBox(width: 10), Text( diff --git a/lib/pages/video/detail/introduction/widgets/fav_panel.dart b/lib/pages/video/detail/introduction/widgets/fav_panel.dart index 43112413..85ef5ee7 100644 --- a/lib/pages/video/detail/introduction/widgets/fav_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/fav_panel.dart @@ -88,6 +88,7 @@ class _FavPanelState extends State { ), Expanded( child: Material( + color: Theme.of(context).colorScheme.surface, child: FutureBuilder( future: _futureBuilderFuture, builder: (BuildContext context, AsyncSnapshot snapshot) { diff --git a/lib/pages/video/detail/introduction/widgets/group_panel.dart b/lib/pages/video/detail/introduction/widgets/group_panel.dart index 69c2c165..306e259e 100644 --- a/lib/pages/video/detail/introduction/widgets/group_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/group_panel.dart @@ -98,6 +98,7 @@ class _GroupPanelState extends State { ), Expanded( child: Material( + color: Theme.of(context).colorScheme.surface, child: FutureBuilder( future: _futureBuilderFuture, builder: (BuildContext context, AsyncSnapshot snapshot) { diff --git a/lib/pages/video/detail/related/view.dart b/lib/pages/video/detail/related/view.dart index e867544b..30f23d6e 100644 --- a/lib/pages/video/detail/related/view.dart +++ b/lib/pages/video/detail/related/view.dart @@ -60,11 +60,9 @@ class _RelatedVideoPanelState extends State height: MediaQuery.of(context).padding.bottom, ); } else { - return Material( - child: VideoCardH( - videoItem: loadingState.response[index], - showPubdate: true, - ), + return VideoCardH( + videoItem: loadingState.response[index], + showPubdate: true, ); } }, childCount: loadingState.response.length + 1), diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index ac48b855..714c9663 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -134,10 +134,7 @@ class ReplyItem extends StatelessWidget { indent: 55, endIndent: 15, height: 0.3, - color: Theme.of(context) - .colorScheme - .onInverseSurface - .withOpacity(0.5), + color: Theme.of(context).colorScheme.outline.withOpacity(0.08), ) ], ), diff --git a/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart b/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart index 4e45e230..d45b1bf7 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item_grpc.dart @@ -160,8 +160,7 @@ class ReplyItemGrpc extends StatelessWidget { indent: 55, endIndent: 15, height: 0.3, - color: - Theme.of(context).colorScheme.onInverseSurface.withOpacity(0.5), + color: Theme.of(context).colorScheme.outline.withOpacity(0.08), ) ], ); diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index fc894d90..552d64cf 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -1280,75 +1280,72 @@ class _VideoDetailPageState extends State ), ), ), - child: Material( - child: Row( - children: [ - if (tabs.isEmpty) - const Spacer() - else - Flexible( - flex: tabs.length == 3 ? 2 : 1, - child: showReply ? Obx(() => tabbar()) : tabbar(), - ), + child: Row( + children: [ + if (tabs.isEmpty) + const Spacer() + else Flexible( - flex: 1, - child: Center( - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - SizedBox( - height: 32, - child: TextButton( - style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - ), - onPressed: videoDetailController.showShootDanmakuSheet, - child: Text( - '发弹幕', - style: TextStyle( - fontSize: 12, - color: - Theme.of(context).colorScheme.onSurfaceVariant, - ), + flex: tabs.length == 3 ? 2 : 1, + child: showReply ? Obx(() => tabbar()) : tabbar(), + ), + Flexible( + flex: 1, + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + height: 32, + child: TextButton( + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: videoDetailController.showShootDanmakuSheet, + child: Text( + '发弹幕', + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), ), - SizedBox( - width: 38, - height: 38, - child: Obx( - () => IconButton( - onPressed: () { - videoDetailController - .plPlayerController.isOpenDanmu.value = - !videoDetailController - .plPlayerController.isOpenDanmu.value; - setting.put( - SettingBoxKey.enableShowDanmaku, - videoDetailController - .plPlayerController.isOpenDanmu.value); - }, - icon: SvgPicture.asset( - videoDetailController - .plPlayerController.isOpenDanmu.value - ? 'assets/images/video/danmu_open.svg' - : 'assets/images/video/danmu_close.svg', - // ignore: deprecated_member_use - color: videoDetailController - .plPlayerController.isOpenDanmu.value - ? Theme.of(context).colorScheme.secondary - : Theme.of(context).colorScheme.outline, - ), + ), + SizedBox( + width: 38, + height: 38, + child: Obx( + () => IconButton( + onPressed: () { + videoDetailController + .plPlayerController.isOpenDanmu.value = + !videoDetailController + .plPlayerController.isOpenDanmu.value; + setting.put( + SettingBoxKey.enableShowDanmaku, + videoDetailController + .plPlayerController.isOpenDanmu.value); + }, + icon: SvgPicture.asset( + videoDetailController + .plPlayerController.isOpenDanmu.value + ? 'assets/images/video/danmu_open.svg' + : 'assets/images/video/danmu_close.svg', + // ignore: deprecated_member_use + color: videoDetailController + .plPlayerController.isOpenDanmu.value + ? Theme.of(context).colorScheme.secondary + : Theme.of(context).colorScheme.outline, ), ), ), - const SizedBox(width: 14), - ], - ), + ), + const SizedBox(width: 14), + ], ), ), - ], - ), + ), + ], ), ); } @@ -1521,7 +1518,10 @@ class _VideoDetailPageState extends State height: 1, indent: 12, endIndent: 12, - color: Theme.of(context).dividerColor.withOpacity(0.06), + color: Theme.of(context) + .colorScheme + .outline + .withOpacity(0.08), ), ), ), diff --git a/lib/utils/extension.dart b/lib/utils/extension.dart index 24fd90cf..1201dcda 100644 --- a/lib/utils/extension.dart +++ b/lib/utils/extension.dart @@ -97,3 +97,21 @@ extension Unique on List { return list; } } + +extension ColorExtension on Color { + Color darken([double amount = .5]) { + assert(amount >= 0 && amount <= 1, 'Amount must be between 0 and 1'); + return Color.lerp(this, Colors.black, amount)!; + } + + Color blend(Color color, [double fraction = 0.5]) { + assert(fraction >= 0 && fraction <= 1, 'Fraction must be between 0 and 1'); + final blendedRed = (red * (1 - fraction) + color.red * fraction).toInt(); + final blendedGreen = + (green * (1 - fraction) + color.green * fraction).toInt(); + final blendedBlue = (blue * (1 - fraction) + color.blue * fraction).toInt(); + final blendedAlpha = + (alpha * (1 - fraction) + color.alpha * fraction).toInt(); + return Color.fromARGB(blendedAlpha, blendedRed, blendedGreen, blendedBlue); + } +} diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 72accc89..1c08754f 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -367,6 +367,9 @@ class GStorage { static bool get coinWithLike => GStorage.setting.get(SettingBoxKey.coinWithLike, defaultValue: false); + static bool get isPureBlackTheme => + GStorage.setting.get(SettingBoxKey.isPureBlackTheme, defaultValue: false); + static List get dynamicDetailRatio => List.from(setting .get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0])); @@ -599,6 +602,7 @@ class SettingBoxKey { showDmChart = 'showDmChart', enableCommAntifraud = 'enableCommAntifraud', coinWithLike = 'coinWithLike', + isPureBlackTheme = 'isPureBlackTheme', // Sponsor Block enableSponsorBlock = 'enableSponsorBlock', diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index a81e152a..61d77d90 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -44,6 +44,56 @@ class Utils { static const channel = MethodChannel("PiliPlus"); + static darkenTheme(ThemeData themeData) { + // return themeData; + Color color = themeData.colorScheme.surfaceContainerHighest.darken(0.7); + return themeData.copyWith( + scaffoldBackgroundColor: Colors.black, + appBarTheme: themeData.appBarTheme.copyWith( + backgroundColor: Colors.black, + ), + cardTheme: themeData.cardTheme.copyWith( + color: Colors.black, + ), + dialogTheme: themeData.dialogTheme.copyWith( + backgroundColor: color, + ), + bottomSheetTheme: + themeData.bottomSheetTheme.copyWith(backgroundColor: color), + bottomNavigationBarTheme: + themeData.bottomNavigationBarTheme.copyWith(backgroundColor: color), + navigationBarTheme: + themeData.navigationBarTheme.copyWith(backgroundColor: color), + navigationRailTheme: + themeData.navigationRailTheme.copyWith(backgroundColor: Colors.black), + colorScheme: themeData.colorScheme.copyWith( + primary: themeData.colorScheme.primary.darken(0.1), + onPrimary: themeData.colorScheme.onPrimary.darken(0.1), + primaryContainer: themeData.colorScheme.primaryContainer.darken(0.1), + onPrimaryContainer: + themeData.colorScheme.onPrimaryContainer.darken(0.1), + inversePrimary: themeData.colorScheme.inversePrimary.darken(0.1), + secondary: themeData.colorScheme.secondary.darken(0.1), + onSecondary: themeData.colorScheme.onSecondary.darken(0.1), + secondaryContainer: + themeData.colorScheme.secondaryContainer.darken(0.1), + onSecondaryContainer: + themeData.colorScheme.onSecondaryContainer.darken(0.1), + error: themeData.colorScheme.error.darken(0.1), + surface: Colors.black, + onSurface: themeData.colorScheme.onSurface.darken(0.15), + surfaceTint: themeData.colorScheme.surfaceTint.darken(), + inverseSurface: themeData.colorScheme.inverseSurface.darken(), + onInverseSurface: themeData.colorScheme.onInverseSurface.darken(), + surfaceContainer: themeData.colorScheme.surfaceContainer.darken(), + surfaceContainerHigh: + themeData.colorScheme.surfaceContainerHigh.darken(), + surfaceContainerHighest: + themeData.colorScheme.surfaceContainerHighest.darken(0.4), + ), + ); + } + static void onCopyOrMove({ required BuildContext context, required bool isCopy,