diff --git a/lib/common/widgets/dialog/dialog.dart b/lib/common/widgets/dialog/dialog.dart index 99823fd6..b5325c39 100644 --- a/lib/common/widgets/dialog/dialog.dart +++ b/lib/common/widgets/dialog/dialog.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -void showConfirmDialog({ +Future showConfirmDialog({ required BuildContext context, required String title, dynamic content, required VoidCallback onConfirm, }) { - showDialog( + return showDialog( context: context, builder: (context) { return AlertDialog( diff --git a/lib/common/widgets/image/image_view.dart b/lib/common/widgets/image/image_view.dart index f4128a9e..023d7ba4 100644 --- a/lib/common/widgets/image/image_view.dart +++ b/lib/common/widgets/image/image_view.dart @@ -151,8 +151,8 @@ Widget imageView( src: item.url, width: imageWidth, height: imageHeight, - isLongPic: () => item.isLongPic, - callback: () => item.width <= item.height, + isLongPic: item.isLongPic, + forceUseCacheWidth: item.width <= item.height, getPlaceHolder: () { return Container( width: imageWidth, diff --git a/lib/common/widgets/image/network_img_layer.dart b/lib/common/widgets/image/network_img_layer.dart index 0adb8d52..30cd8d5f 100644 --- a/lib/common/widgets/image/network_img_layer.dart +++ b/lib/common/widgets/image/network_img_layer.dart @@ -1,7 +1,9 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/models/common/image_type.dart'; +import 'package:PiliPlus/utils/context_ext.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/image_util.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; @@ -19,8 +21,8 @@ class NetworkImgLayer extends StatelessWidget { this.semanticsLabel, this.radius, this.imageBuilder, - this.isLongPic, - this.callback, + this.isLongPic = false, + this.forceUseCacheWidth = false, this.getPlaceHolder, this.boxFit, }); @@ -35,30 +37,34 @@ class NetworkImgLayer extends StatelessWidget { final String? semanticsLabel; final double? radius; final ImageWidgetBuilder? imageBuilder; - final Function? isLongPic; - final Function? callback; - final Function? getPlaceHolder; + final bool isLongPic; + final bool forceUseCacheWidth; + final Widget Function()? getPlaceHolder; final BoxFit? boxFit; + static Color? reduceLuxColor = Pref.reduceLuxColor; + @override Widget build(BuildContext context) { + final reduce = + quality != 100 && reduceLuxColor != null && context.isDarkMode; return src?.isNotEmpty == true ? type == ImageType.avatar - ? ClipOval(child: _buildImage(context)) + ? ClipOval(child: _buildImage(context, reduce)) : radius == 0 || type == ImageType.emote - ? _buildImage(context) + ? _buildImage(context, reduce) : ClipRRect( borderRadius: radius != null ? BorderRadius.circular(radius!) : StyleString.mdRadius, - child: _buildImage(context), + child: _buildImage(context, reduce), ) - : getPlaceHolder?.call() ?? placeholder(context); + : getPlaceHolder?.call() ?? _placeholder(context, reduce); } - Widget _buildImage(BuildContext context) { + Widget _buildImage(BuildContext context, bool reduce) { int? memCacheWidth, memCacheHeight; - if (height == null || callback?.call() == true || width <= height!) { + if (height == null || forceUseCacheWidth || width <= height!) { memCacheWidth = width.cacheSize(context); } else { memCacheHeight = height.cacheSize(context); @@ -70,20 +76,20 @@ class NetworkImgLayer extends StatelessWidget { memCacheWidth: memCacheWidth, memCacheHeight: memCacheHeight, fit: boxFit ?? BoxFit.cover, - alignment: isLongPic?.call() == true - ? Alignment.topCenter - : Alignment.center, + alignment: isLongPic ? Alignment.topCenter : Alignment.center, fadeOutDuration: fadeOutDuration ?? const Duration(milliseconds: 120), fadeInDuration: fadeInDuration ?? const Duration(milliseconds: 120), filterQuality: FilterQuality.low, placeholder: (BuildContext context, String url) => - getPlaceHolder?.call() ?? placeholder(context), + getPlaceHolder?.call() ?? _placeholder(context, reduce), imageBuilder: imageBuilder, - errorWidget: (context, url, error) => placeholder(context), + errorWidget: (context, url, error) => _placeholder(context, reduce), + colorBlendMode: reduce ? BlendMode.modulate : null, + color: reduce ? reduceLuxColor : null, ); } - Widget placeholder(BuildContext context) { + Widget _placeholder(BuildContext context, bool reduce) { return Container( width: width, height: height, @@ -108,6 +114,8 @@ class NetworkImgLayer extends StatelessWidget { width: width, height: height, cacheWidth: width.cacheSize(context), + colorBlendMode: reduce ? BlendMode.modulate : null, + color: reduce ? reduceLuxColor : null, ), ), ); diff --git a/lib/pages/setting/models/style_settings.dart b/lib/pages/setting/models/style_settings.dart index 00cea1bc..cd114a31 100644 --- a/lib/pages/setting/models/style_settings.dart +++ b/lib/pages/setting/models/style_settings.dart @@ -2,6 +2,8 @@ import 'dart:io'; import 'dart:math'; import 'package:PiliPlus/common/widgets/custom_toast.dart'; +import 'package:PiliPlus/common/widgets/dialog/dialog.dart'; +import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/scroll_physics.dart'; import 'package:PiliPlus/main.dart'; import 'package:PiliPlus/models/common/dynamic/dynamic_badge_mode.dart'; @@ -15,6 +17,7 @@ import 'package:PiliPlus/pages/main/controller.dart'; import 'package:PiliPlus/pages/mine/controller.dart'; import 'package:PiliPlus/pages/setting/models/model.dart'; import 'package:PiliPlus/pages/setting/pages/color_select.dart'; +import 'package:PiliPlus/pages/setting/slide_color_picker.dart'; import 'package:PiliPlus/pages/setting/widgets/multi_select_dialog.dart'; import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart'; import 'package:PiliPlus/pages/setting/widgets/slide_dialog.dart'; @@ -451,6 +454,67 @@ List get styleSettings => [ ), ), ), + SettingsModel( + settingsType: SettingsType.normal, + onTap: (setState) { + showDialog( + context: Get.context!, + builder: (context) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 16), + title: const Text('Color Picker'), + content: SlideColorPicker( + showResetBtn: false, + color: Pref.reduceLuxColor ?? Colors.white, + callback: (Color? color) async { + if (color != null && color != Pref.reduceLuxColor) { + if (color == Colors.white) { + NetworkImgLayer.reduceLuxColor = null; + GStorage.setting.delete(SettingBoxKey.reduceLuxColor); + SmartDialog.showToast('设置成功'); + setState(); + } else { + void onConfirm() { + NetworkImgLayer.reduceLuxColor = color; + GStorage.setting.put( + SettingBoxKey.reduceLuxColor, + color.toARGB32(), + ); + SmartDialog.showToast('设置成功'); + setState(); + } + + if (color.computeLuminance() < 0.2) { + await showConfirmDialog( + context: context, + title: + '确认使用#${(color.toARGB32() & 0xFFFFFF).toRadixString(16).toUpperCase().padLeft(6)}?', + content: '所选颜色过于昏暗,可能会影响图片观看', + onConfirm: onConfirm, + ); + } else { + onConfirm(); + } + } + } + }, + ), + ), + ); + }, + title: '深色下图片颜色叠加', + subtitle: '显示颜色=图片原色x所选颜色,大图查看不受影响', + leading: const Icon(Icons.format_color_fill_outlined), + getTrailing: () => Container( + padding: const EdgeInsets.only(right: 8.0), + width: 20, + height: 20, + decoration: BoxDecoration( + color: Pref.reduceLuxColor ?? Colors.white, + shape: BoxShape.circle, + ), + ), + ), SettingsModel( settingsType: SettingsType.normal, onTap: (setState) async { diff --git a/lib/pages/video/view.dart b/lib/pages/video/view.dart index 66e3ad2c..8e2b89d4 100644 --- a/lib/pages/video/view.dart +++ b/lib/pages/video/view.dart @@ -5,6 +5,7 @@ import 'dart:ui'; import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/custom_icon.dart'; +import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/scroll_physics.dart'; import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart' show ReplyInfo; @@ -50,7 +51,6 @@ import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage_key.dart'; import 'package:auto_orientation/auto_orientation.dart'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:floating/floating.dart'; @@ -1640,17 +1640,14 @@ class _VideoDetailPageVState extends State child: GestureDetector( onTap: handlePlay, child: Obx( - () => CachedNetworkImage( - imageUrl: videoDetailController.cover.value.http2https, + () => NetworkImgLayer( + src: videoDetailController.cover.value, width: width, height: height, - fit: BoxFit.cover, + boxFit: BoxFit.cover, fadeOutDuration: const Duration(milliseconds: 120), fadeInDuration: const Duration(milliseconds: 120), - memCacheWidth: width.cacheSize(context), - placeholder: (context, url) => Center( - child: Image.asset('assets/images/loading.png'), - ), + forceUseCacheWidth: true, ), ), ), diff --git a/lib/utils/storage_key.dart b/lib/utils/storage_key.dart index 2fb1da95..bc41af6e 100644 --- a/lib/utils/storage_key.dart +++ b/lib/utils/storage_key.dart @@ -202,7 +202,8 @@ class SettingBoxKey { hiddenSettingUnlocked = 'hiddenSettingUnlocked', enableGradientBg = 'enableGradientBg', navBarSort = 'navBarSort', - tempPlayerConf = 'tempPlayerConf'; + tempPlayerConf = 'tempPlayerConf', + reduceLuxColor = 'reduceLuxColor'; } class LocalCacheKey { diff --git a/lib/utils/storage_pref.dart b/lib/utils/storage_pref.dart index 5bbfebd3..199c7bec 100644 --- a/lib/utils/storage_pref.dart +++ b/lib/utils/storage_pref.dart @@ -779,4 +779,12 @@ class Pref { static bool get tempPlayerConf => _setting.get(SettingBoxKey.tempPlayerConf, defaultValue: false); + + static Color? get reduceLuxColor { + final int? color = _setting.get(SettingBoxKey.reduceLuxColor); + if (color != null && color != 0xFFFFFFFF) { + return Color(color); + } + return null; + } }