correctly preview and calculate container colors
This commit is contained in:
+4
-2
@@ -49,6 +49,7 @@ import 'package:weblibre/features/geckoview/features/history/domain/repositories
|
||||
import 'package:weblibre/features/geckoview/features/readerview/presentation/controllers/readerable.dart';
|
||||
import 'package:weblibre/features/geckoview/features/readerview/presentation/widgets/reader_button.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selected_container.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/utils/container_colors.dart';
|
||||
import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
|
||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/providers.dart';
|
||||
@@ -264,7 +265,7 @@ class BrowserTabBar extends HookConsumerWidget {
|
||||
titleSpacing: 8.0,
|
||||
backgroundColor:
|
||||
(containerColor != null && displayedSheet is! ViewTabsSheet)
|
||||
? containerColor.withValues(alpha: 0.33)
|
||||
? ContainerColors.forAppBar(containerColor)
|
||||
: null,
|
||||
title:
|
||||
(selectedTabId != null && displayedSheet is! ViewTabsSheet)
|
||||
@@ -489,7 +490,8 @@ class QuickTabSwitcher extends HookConsumerWidget {
|
||||
);
|
||||
},
|
||||
itemAvatar: (item) => UrlIcon([item.url], iconSize: 16),
|
||||
itemBackgroundColor: (item) => item.color?.withValues(alpha: 0.33),
|
||||
itemBackgroundColor: (item) =>
|
||||
item.color != null ? ContainerColors.forChip(item.color!) : null,
|
||||
onSelected: (item) async {
|
||||
final animation = chipScrollController.animateTo(
|
||||
0,
|
||||
|
||||
@@ -31,6 +31,7 @@ import 'package:weblibre/features/geckoview/features/tabs/presentation/controlle
|
||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/dialogs/delete_container_dialog.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_sites.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/color_picker_dialog.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/utils/container_colors.dart';
|
||||
import 'package:weblibre/features/user/domain/services/local_authentication.dart';
|
||||
import 'package:weblibre/presentation/icons/tor_icons.dart';
|
||||
|
||||
@@ -168,7 +169,7 @@ class ContainerEditScreen extends HookConsumerWidget {
|
||||
width: 24,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: selectedColor.value,
|
||||
color: ContainerColors.preview(selectedColor.value),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/material_color_picker.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/utils/container_colors.dart';
|
||||
|
||||
class ColorPickerDialog extends HookWidget {
|
||||
final Color initialColor;
|
||||
@@ -47,6 +48,7 @@ class ColorPickerDialog extends HookWidget {
|
||||
onColorChanged: (value) {
|
||||
selectedColor.value = value;
|
||||
},
|
||||
displayAlpha: ContainerColors.defaultAlpha,
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
|
||||
@@ -33,6 +33,7 @@ import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart'
|
||||
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/gecko_inference.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/container_title.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/tab_drag_container_target.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/utils/container_colors.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:weblibre/presentation/widgets/selectable_chips.dart';
|
||||
|
||||
@@ -98,7 +99,7 @@ class ContainerChips extends HookConsumerWidget {
|
||||
enableDelete: false,
|
||||
itemId: (container) => container.id,
|
||||
itemBackgroundColor: (container) =>
|
||||
container.color.withValues(alpha: 0.33),
|
||||
ContainerColors.forChip(container.color),
|
||||
selectedBorderColor: Theme.of(context).colorScheme.primary,
|
||||
itemLabel: (container) =>
|
||||
ContainerTitle(container: container),
|
||||
|
||||
+4
-1
@@ -21,6 +21,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/container_title.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/utils/container_colors.dart';
|
||||
|
||||
class ContainerListTile extends HookWidget {
|
||||
final ContainerData container;
|
||||
@@ -41,7 +42,9 @@ class ContainerListTile extends HookWidget {
|
||||
selectedTileColor: Theme.of(context).colorScheme.primaryContainer,
|
||||
child: ListTile(
|
||||
selected: isSelected,
|
||||
leading: CircleAvatar(backgroundColor: container.color),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: ContainerColors.preview(container.color),
|
||||
),
|
||||
title: ContainerTitle(container: container),
|
||||
onTap: onTap,
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
|
||||
+15
-7
@@ -32,6 +32,7 @@ class MaterialPicker extends StatefulWidget {
|
||||
this.onPrimaryChanged,
|
||||
this.enableLabel = false,
|
||||
this.portraitOnly = false,
|
||||
this.displayAlpha,
|
||||
});
|
||||
|
||||
final Color pickerColor;
|
||||
@@ -39,6 +40,7 @@ class MaterialPicker extends StatefulWidget {
|
||||
final ValueChanged<Color>? onPrimaryChanged;
|
||||
final bool enableLabel;
|
||||
final bool portraitOnly;
|
||||
final double? displayAlpha;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _MaterialPickerState();
|
||||
@@ -124,6 +126,9 @@ class _MaterialPickerState extends State<MaterialPicker> {
|
||||
const Padding(padding: EdgeInsets.only(left: 7)),
|
||||
...colorTypes.map((List<Color> colors) {
|
||||
final Color colorType = colors[0];
|
||||
final Color displayColorType = widget.displayAlpha != null
|
||||
? colorType.withValues(alpha: widget.displayAlpha)
|
||||
: colorType;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (widget.onPrimaryChanged != null) {
|
||||
@@ -142,7 +147,7 @@ class _MaterialPickerState extends State<MaterialPicker> {
|
||||
width: 25,
|
||||
height: 25,
|
||||
decoration: BoxDecoration(
|
||||
color: colorType,
|
||||
color: displayColorType,
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: _currentColorType == colors
|
||||
? [
|
||||
@@ -158,7 +163,7 @@ class _MaterialPickerState extends State<MaterialPicker> {
|
||||
)
|
||||
else
|
||||
BoxShadow(
|
||||
color: colorType,
|
||||
color: displayColorType,
|
||||
blurRadius: 10,
|
||||
),
|
||||
]
|
||||
@@ -211,6 +216,9 @@ class _MaterialPickerState extends State<MaterialPicker> {
|
||||
Map<Color, String> colors,
|
||||
) {
|
||||
final Color color = colors.keys.first;
|
||||
final Color displayColor = widget.displayAlpha != null
|
||||
? color.withValues(alpha: widget.displayAlpha)
|
||||
: color;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() => _currentShading = color);
|
||||
@@ -233,7 +241,7 @@ class _MaterialPickerState extends State<MaterialPicker> {
|
||||
: (_currentShading == color ? 50 : 30),
|
||||
height: isPortrait ? 50 : 220,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
color: displayColor,
|
||||
boxShadow: _currentShading == color
|
||||
? [
|
||||
if ((color == Colors.white) ||
|
||||
@@ -247,7 +255,7 @@ class _MaterialPickerState extends State<MaterialPicker> {
|
||||
blurRadius: 10,
|
||||
)
|
||||
else
|
||||
BoxShadow(color: color, blurRadius: 10),
|
||||
BoxShadow(color: displayColor, blurRadius: 10),
|
||||
]
|
||||
: null,
|
||||
border:
|
||||
@@ -269,7 +277,7 @@ class _MaterialPickerState extends State<MaterialPicker> {
|
||||
Text(
|
||||
' ${colors.values.first}',
|
||||
style: TextStyle(
|
||||
color: useWhiteForeground(color)
|
||||
color: useWhiteForeground(displayColor)
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
),
|
||||
@@ -281,7 +289,7 @@ class _MaterialPickerState extends State<MaterialPicker> {
|
||||
'#${color.toString().replaceFirst('Color(0xff', '').replaceFirst(')', '').toUpperCase()} ',
|
||||
style: TextStyle(
|
||||
color:
|
||||
useWhiteForeground(color)
|
||||
useWhiteForeground(displayColor)
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -306,7 +314,7 @@ class _MaterialPickerState extends State<MaterialPicker> {
|
||||
child: Text(
|
||||
colors.values.first,
|
||||
style: TextStyle(
|
||||
color: useWhiteForeground(color)
|
||||
color: useWhiteForeground(displayColor)
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2025 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Centralized helper for container color display and theming.
|
||||
///
|
||||
/// This class handles the conversion of stored container colors (full opacity)
|
||||
/// to their display variants (with transparency) for consistent appearance
|
||||
/// across the application.
|
||||
class ContainerColors {
|
||||
ContainerColors._();
|
||||
|
||||
/// Default alpha value for container color display (33% opacity)
|
||||
static const double defaultAlpha = 0.33;
|
||||
|
||||
/// Returns the display color for container chips and backgrounds.
|
||||
///
|
||||
/// Applies semi-transparent overlay that works well for backgrounds
|
||||
/// while maintaining color distinction between containers.
|
||||
///
|
||||
/// [baseColor] The stored container color (typically full opacity)
|
||||
static Color forChip(Color baseColor) {
|
||||
return baseColor.withValues(alpha: defaultAlpha);
|
||||
}
|
||||
|
||||
/// Returns the display color for container app bar backgrounds.
|
||||
///
|
||||
/// Uses the same transparency as chips for visual consistency.
|
||||
///
|
||||
/// [baseColor] The stored container color (typically full opacity)
|
||||
static Color forAppBar(Color baseColor) {
|
||||
return baseColor.withValues(alpha: defaultAlpha);
|
||||
}
|
||||
|
||||
/// Returns the display color with theme-aware blending.
|
||||
///
|
||||
/// Uses Material Design's color blending algorithm for proper color mixing
|
||||
/// with the surface color, ensuring better visual appearance across themes.
|
||||
///
|
||||
/// [baseColor] The stored container color (typically full opacity)
|
||||
/// [surface] The surface color to blend with (typically from theme)
|
||||
static Color forSurface(Color baseColor, Color surface) {
|
||||
return Color.alphaBlend(
|
||||
baseColor.withValues(alpha: defaultAlpha),
|
||||
surface,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the preview color showing how the color will appear in the UI.
|
||||
///
|
||||
/// This is useful in color pickers to show users the actual appearance
|
||||
/// before they confirm their selection.
|
||||
///
|
||||
/// [baseColor] The color being previewed
|
||||
static Color preview(Color baseColor) {
|
||||
return baseColor.withValues(alpha: defaultAlpha);
|
||||
}
|
||||
|
||||
/// Returns the full opacity version of a container color.
|
||||
///
|
||||
/// Useful when you need the original color for comparison or display
|
||||
/// in contexts where full opacity is needed.
|
||||
///
|
||||
/// [baseColor] The color to ensure has full opacity
|
||||
static Color fullOpacity(Color baseColor) {
|
||||
return baseColor.withValues(alpha: 1.0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user