Fix: dark icon hard to see (#407)

## 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 <aadarsh@zuzu.in>
This commit is contained in:
Aadarsh Patel 2024-01-03 12:48:23 +05:30 committed by GitHub
parent 8b9592c06e
commit 6b4e4b6822
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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<String, String> _simpleIcons = {};
final Map<String, CustomIconData> _customIcons = {};
// Map of icon-color to its luminance
final Map<Color, double> _colorLuminance = {};
Future<void> 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<void> _loadJson() async {
try {
final simpleIconData = await rootBundle