From 6b4e4b682257e3b1ebbea086444713bd79b5f19e Mon Sep 17 00:00:00 2001 From: Aadarsh Patel <53324291+aadarsh-patel@users.noreply.github.com> Date: Wed, 3 Jan 2024 12:48:23 +0530 Subject: [PATCH] Fix: dark icon hard to see (#407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Type of Change - [x] ✨ New feature (non-breaking change which adds functionality) ## Description Fall-back to theme's icon-color when the icon-color is too light or dark. Fixes #403 Logic 1. If RGB values are almost equal (`#000000`, `#0F0F11`, `#212121`, `#27272A`, `#464949`, `#FFFFFF`) 2. Compute its luminance/brightness (cache this value as it is an expensive task) 3. If its too bright or dark, return theme's icon-color I've manually set the threshold values for brightness in light-theme to be `0.7` and in dark-theme to be `0.05` https://github.com/ente-io/auth/assets/53324291/aa1e8413-631d-4039-8c08-f8c4d1856fdb Co-authored-by: aadarsh-patel --- lib/ui/utils/icon_utils.dart | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/lib/ui/utils/icon_utils.dart b/lib/ui/utils/icon_utils.dart index 91a72425b..298a14333 100644 --- a/lib/ui/utils/icon_utils.dart +++ b/lib/ui/utils/icon_utils.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:ente_auth/ente_theme_data.dart'; import 'package:ente_auth/theme/ente_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -14,6 +15,8 @@ class IconUtils { // Map of icon-title to the color code in HEX final Map _simpleIcons = {}; final Map _customIcons = {}; + // Map of icon-color to its luminance + final Map _colorLuminance = {}; Future init() async { await _loadJson(); @@ -31,6 +34,7 @@ class IconUtils { title, _customIcons[title]!.color, width, + context, ); } else if (_simpleIcons.containsKey(title)) { return _getSVGIcon( @@ -38,6 +42,7 @@ class IconUtils { title, _simpleIcons[title], width, + context, ); } else if (title.isNotEmpty) { bool showLargeIcon = width > 24; @@ -63,20 +68,45 @@ class IconUtils { String title, String? color, double width, + BuildContext context, ) { + final iconColor = _getAdaptiveColor(color, context); return SvgPicture.asset( path, width: width, semanticsLabel: title, - colorFilter: color != null + colorFilter: iconColor != null ? ColorFilter.mode( - Color(int.parse("0xFF" + color)), + iconColor, BlendMode.srcIn, ) : null, ); } + Color? _getAdaptiveColor(String? hexColor, BuildContext context) { + if (hexColor == null) return null; + final theme = Theme.of(context).brightness; + final color = Color(int.parse("0xFF" + hexColor)); + // Color is close to neutral-grey and it's too light or dark for theme + if (_isCloseToNeutralGrey(color) && + ((theme == Brightness.light && _getColorLuminance(color) > 0.70) || + (theme == Brightness.dark && _getColorLuminance(color) < 0.05))) { + return Theme.of(context).colorScheme.iconColor; + } + return color; + } + + double _getColorLuminance(Color color) { + return _colorLuminance.putIfAbsent(color, () => color.computeLuminance()); + } + + bool _isCloseToNeutralGrey(Color color, {double tolerance = 3}) { + return (color.red - color.green).abs() <= tolerance && + (color.green - color.blue).abs() <= tolerance && + (color.blue - color.red).abs() <= tolerance; + } + Future _loadJson() async { try { final simpleIconData = await rootBundle