From 8210e6c3767ed5dbcc93da0e79308c5f5a5614c0 Mon Sep 17 00:00:00 2001 From: jganenok Date: Thu, 4 Dec 2025 09:04:50 +0700 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B0=D0=BB=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=20=D0=BD?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B5=D0=BA=20=D0=B2=D0=B8=D0=B7?= =?UTF-8?q?=D1=83=D0=B0=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/appearance_settings_screen.dart | 54 +- .../settings/customization_screen.dart | 821 +++++++++++++----- lib/utils/theme_provider.dart | 13 +- lib/widgets/chat_message_bubble.dart | 8 +- 4 files changed, 614 insertions(+), 282 deletions(-) diff --git a/lib/screens/settings/appearance_settings_screen.dart b/lib/screens/settings/appearance_settings_screen.dart index be92d99..c6489ad 100644 --- a/lib/screens/settings/appearance_settings_screen.dart +++ b/lib/screens/settings/appearance_settings_screen.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:gwid/utils/theme_provider.dart'; import 'package:gwid/screens/settings/customization_screen.dart'; -import 'package:gwid/screens/settings/animations_screen.dart'; class AppearanceSettingsScreen extends StatelessWidget { final bool isModal; @@ -29,11 +28,11 @@ class AppearanceSettingsScreen extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildSectionTitle("Кастомизация", colors), + _buildSectionTitle("Персонализация", colors), ListTile( contentPadding: EdgeInsets.zero, leading: const Icon(Icons.palette_outlined), - title: const Text("Настройки тем"), + title: const Text("Персонализация"), subtitle: const Text("Тема, обои и другие настройки"), trailing: const Icon(Icons.chevron_right_rounded), onTap: () { @@ -44,21 +43,6 @@ class AppearanceSettingsScreen extends StatelessWidget { ); }, ), - - ListTile( - contentPadding: EdgeInsets.zero, - leading: const Icon(Icons.animation), - title: const Text("Настройки анимаций"), - subtitle: const Text("Анимации сообщений и переходов"), - trailing: const Icon(Icons.chevron_right_rounded), - onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => AnimationsScreen(), - ), - ); - }, - ), ], ), ), @@ -103,11 +87,11 @@ class AppearanceSettingsScreen extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildSectionTitle("Кастомизация", colors), + _buildSectionTitle("Персонализация", colors), ListTile( contentPadding: EdgeInsets.zero, leading: const Icon(Icons.palette_outlined), - title: const Text("Настройки тем"), + title: const Text("Персонализация"), trailing: const Icon(Icons.chevron_right), onTap: () { Navigator.of(context).push( @@ -117,19 +101,6 @@ class AppearanceSettingsScreen extends StatelessWidget { ); }, ), - ListTile( - contentPadding: EdgeInsets.zero, - leading: const Icon(Icons.animation_outlined), - title: const Text("Анимации"), - trailing: const Icon(Icons.chevron_right), - onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => const AnimationsScreen(), - ), - ); - }, - ), ], ), ), @@ -232,11 +203,11 @@ class AppearanceSettingsScreen extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildSectionTitle("Кастомизация", colors), + _buildSectionTitle("Персонализация", colors), ListTile( contentPadding: EdgeInsets.zero, leading: const Icon(Icons.palette_outlined), - title: const Text("Настройки тем"), + title: const Text("Персонализация"), trailing: const Icon(Icons.chevron_right), onTap: () { Navigator.of(context).push( @@ -246,19 +217,6 @@ class AppearanceSettingsScreen extends StatelessWidget { ); }, ), - ListTile( - contentPadding: EdgeInsets.zero, - leading: const Icon(Icons.animation_outlined), - title: const Text("Анимации"), - trailing: const Icon(Icons.chevron_right), - onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => const AnimationsScreen(), - ), - ); - }, - ), ], ), ), diff --git a/lib/screens/settings/customization_screen.dart b/lib/screens/settings/customization_screen.dart index 66e359b..e0f9d76 100644 --- a/lib/screens/settings/customization_screen.dart +++ b/lib/screens/settings/customization_screen.dart @@ -99,11 +99,11 @@ class _CustomizationScreenState extends State { : Colors.blue.shade100; final Color theirBubbleFallback = isCurrentlyDark ? const Color(0xFF182533) - : Colors.grey.shade200; + : const Color(0xFF464646); // RGB(70, 70, 70) return Scaffold( appBar: AppBar( - title: const Text("Кастомизация"), + title: const Text("Персонализация"), surfaceTintColor: Colors.transparent, backgroundColor: colors.surface, ), @@ -275,202 +275,229 @@ class _CustomizationScreenState extends State { _ModernSection( title: "Сообщения", children: [ - _SliderTile( - icon: Icons.text_fields, - label: "Непрозрачность текста", - value: theme.messageTextOpacity, - min: 0.1, - max: 1.0, - divisions: 18, - onChanged: (value) => theme.setMessageTextOpacity(value), - displayValue: "${(theme.messageTextOpacity * 100).round()}%", - ), - _SliderTile( - icon: Icons.blur_circular, - label: "Интенсивность тени", - value: theme.messageShadowIntensity, - min: 0.0, - max: 0.5, - divisions: 10, - onChanged: (value) => theme.setMessageShadowIntensity(value), - displayValue: - "${(theme.messageShadowIntensity * 100).round()}%", - ), - _SliderTile( - icon: Icons.rounded_corner, - label: "Скругление углов", - value: theme.messageBorderRadius, - min: 4.0, - max: 50.0, - divisions: 23, - onChanged: (value) => theme.setMessageBorderRadius(value), - displayValue: "${theme.messageBorderRadius.round()}px", - ), - const Divider(height: 24), - _SliderTile( - icon: Icons.menu, - label: "Непрозрачность меню", - value: theme.messageMenuOpacity, - min: 0.1, - max: 1.0, - divisions: 18, - onChanged: (value) => theme.setMessageMenuOpacity(value), - displayValue: "${(theme.messageMenuOpacity * 100).round()}%", - ), - _SliderTile( - icon: Icons.blur_on, - label: "Размытие меню", - value: theme.messageMenuBlur, - min: 0.0, - max: 20.0, - divisions: 20, - onChanged: (value) => theme.setMessageMenuBlur(value), - displayValue: theme.messageMenuBlur.toStringAsFixed(1), - ), - const Divider(height: 24), - if (MediaQuery.of(context).size.height < 600) - const SizedBox(height: 5), - _SliderTile( - icon: Icons.opacity, - label: "Непрозрачность сообщений", - value: 1.0 - theme.messageBubbleOpacity, - min: 0.0, - max: 1.0, - divisions: 20, - onChanged: (value) => - theme.setMessageBubbleOpacity(1.0 - value), - displayValue: - "${((1.0 - theme.messageBubbleOpacity) * 100).round()}%", - ), + // Предпросмотр баблов + const _MessageBubblesPreview(), const SizedBox(height: 16), - _CustomSettingTile( - icon: Icons.format_color_fill, - title: "Тип отображения", - child: IgnorePointer( - ignoring: isSystemTheme, - child: Opacity( - opacity: isSystemTheme ? 0.5 : 1.0, - child: DropdownButton( - value: theme.messageBubbleType, - underline: const SizedBox.shrink(), - onChanged: (value) { - if (value != null) theme.setMessageBubbleType(value); - }, - items: MessageBubbleType.values.map((type) { - return DropdownMenuItem( - value: type, - child: Text(type.displayName), - ); - }).toList(), - ), + + // Прозрачность (сворачиваемый, по умолчанию свернут) + _ExpandableSection( + title: "Прозрачность", + initiallyExpanded: false, + children: [ + _SliderTile( + icon: Icons.text_fields, + label: "Непрозрачность текста", + value: theme.messageTextOpacity, + min: 0.1, + max: 1.0, + divisions: 18, + onChanged: (value) => theme.setMessageTextOpacity(value), + displayValue: "${(theme.messageTextOpacity * 100).round()}%", ), - ), + _SliderTile( + icon: Icons.blur_circular, + label: "Интенсивность тени", + value: theme.messageShadowIntensity, + min: 0.0, + max: 0.5, + divisions: 10, + onChanged: (value) => theme.setMessageShadowIntensity(value), + displayValue: + "${(theme.messageShadowIntensity * 100).round()}%", + ), + _SliderTile( + icon: Icons.menu, + label: "Непрозрачность меню", + value: theme.messageMenuOpacity, + min: 0.1, + max: 1.0, + divisions: 18, + onChanged: (value) => theme.setMessageMenuOpacity(value), + displayValue: "${(theme.messageMenuOpacity * 100).round()}%", + ), + _SliderTile( + icon: Icons.blur_on, + label: "Размытие меню", + value: theme.messageMenuBlur, + min: 0.0, + max: 20.0, + divisions: 20, + onChanged: (value) => theme.setMessageMenuBlur(value), + displayValue: theme.messageMenuBlur.toStringAsFixed(1), + ), + _SliderTile( + icon: Icons.opacity, + label: "Непрозрачность сообщений", + value: 1.0 - theme.messageBubbleOpacity, + min: 0.0, + max: 1.0, + divisions: 20, + onChanged: (value) => + theme.setMessageBubbleOpacity(1.0 - value), + displayValue: + "${((1.0 - theme.messageBubbleOpacity) * 100).round()}%", + ), + ], ), - const SizedBox(height: 16), - _CustomSettingTile( - icon: Icons.palette, - title: "Цвет моих сообщений", - child: IgnorePointer( - ignoring: isSystemTheme, - child: Opacity( - opacity: isSystemTheme ? 0.5 : 1.0, - child: GestureDetector( - onTap: () async { - final initial = myBubbleColorToShow ?? myBubbleFallback; - _showColorPicker( - context, - initialColor: initial, - onColorChanged: (color) => myBubbleSetter(color), - ); - }, - child: Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: myBubbleColorToShow ?? myBubbleFallback, - borderRadius: BorderRadius.circular(8), - border: Border.all(color: Colors.grey), + const SizedBox(height: 8), + + // Вид (сворачиваемый) + _ExpandableSection( + title: "Вид", + initiallyExpanded: false, + children: [ + _SliderTile( + icon: Icons.rounded_corner, + label: "Скругление углов", + value: theme.messageBorderRadius, + min: 4.0, + max: 50.0, + divisions: 23, + onChanged: (value) => theme.setMessageBorderRadius(value), + displayValue: "${theme.messageBorderRadius.round()}px", + ), + const SizedBox(height: 16), + _CustomSettingTile( + icon: Icons.format_color_fill, + title: "Тип отображения", + child: IgnorePointer( + ignoring: isSystemTheme, + child: Opacity( + opacity: isSystemTheme ? 0.5 : 1.0, + child: DropdownButton( + value: theme.messageBubbleType, + underline: const SizedBox.shrink(), + onChanged: (value) { + if (value != null) theme.setMessageBubbleType(value); + }, + items: MessageBubbleType.values.map((type) { + return DropdownMenuItem( + value: type, + child: Text(type.displayName), + ); + }).toList(), ), ), ), ), - ), - ), - const SizedBox(height: 16), - _CustomSettingTile( - icon: Icons.palette_outlined, - title: "Цвет сообщений собеседника", - child: IgnorePointer( - ignoring: isSystemTheme, - child: Opacity( - opacity: isSystemTheme ? 0.5 : 1.0, - child: GestureDetector( - onTap: () async { - final initial = - theirBubbleColorToShow ?? theirBubbleFallback; - _showColorPicker( - context, - initialColor: initial, - onColorChanged: (color) => theirBubbleSetter(color), - ); - }, - child: Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: theirBubbleColorToShow ?? theirBubbleFallback, - borderRadius: BorderRadius.circular(8), - border: Border.all(color: Colors.grey), + const SizedBox(height: 16), + _CustomSettingTile( + icon: Icons.palette, + title: "Цвет моих сообщений", + child: IgnorePointer( + ignoring: isSystemTheme, + child: Opacity( + opacity: isSystemTheme ? 0.5 : 1.0, + child: GestureDetector( + onTap: () async { + final initial = myBubbleColorToShow ?? myBubbleFallback; + _showColorPicker( + context, + initialColor: initial, + onColorChanged: (color) => myBubbleSetter(color), + ); + }, + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: myBubbleColorToShow ?? myBubbleFallback, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey), + ), + ), ), ), ), ), - ), + const SizedBox(height: 16), + _CustomSettingTile( + icon: Icons.palette_outlined, + title: "Цвет сообщений собеседника", + child: IgnorePointer( + ignoring: isSystemTheme, + child: Opacity( + opacity: isSystemTheme ? 0.5 : 1.0, + child: GestureDetector( + onTap: () async { + final initial = + theirBubbleColorToShow ?? theirBubbleFallback; + _showColorPicker( + context, + initialColor: initial, + onColorChanged: (color) => theirBubbleSetter(color), + ); + }, + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: theirBubbleColorToShow ?? theirBubbleFallback, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey), + ), + ), + ), + ), + ), + ), + const Divider(height: 24), + _CustomSettingTile( + icon: Icons.reply, + title: "Автоцвет панели ответа", + subtitle: "", + child: Switch( + value: theme.useAutoReplyColor, + onChanged: (value) => theme.setUseAutoReplyColor(value), + ), + ), + if (!theme.useAutoReplyColor) ...[ + const SizedBox(height: 16), + _ColorPickerTile( + title: "Цвет панели ответа", + subtitle: "Фиксированный цвет", + color: theme.customReplyColor ?? Colors.blue, + onColorChanged: (color) => theme.setCustomReplyColor(color), + ), + ], + ], ), - const Divider(height: 24), - _CustomSettingTile( - icon: Icons.reply, - title: "Автоцвет панели ответа", - subtitle: "", - child: Switch( - value: theme.useAutoReplyColor, - onChanged: (value) => theme.setUseAutoReplyColor(value), - ), - ), - if (!theme.useAutoReplyColor) ...[ - const SizedBox(height: 16), - _ColorPickerTile( - title: "Цвет панели ответа", - subtitle: "Фиксированный цвет", - color: theme.customReplyColor ?? Colors.blue, - onColorChanged: (color) => theme.setCustomReplyColor(color), - ), - ], ], ), const SizedBox(height: 24), _ModernSection( title: "Всплывающие окна", children: [ - _SliderTile( - icon: Icons.opacity, - label: "Прозрачность фона (профиль)", - value: theme.profileDialogOpacity, - min: 0.0, - max: 1.0, - divisions: 20, - onChanged: (value) => theme.setProfileDialogOpacity(value), - displayValue: "${(theme.profileDialogOpacity * 100).round()}%", - ), - _SliderTile( - icon: Icons.blur_on, - label: "Размытие фона (профиль)", - value: theme.profileDialogBlur, - min: 0.0, - max: 30.0, - divisions: 30, - onChanged: (value) => theme.setProfileDialogBlur(value), - displayValue: theme.profileDialogBlur.toStringAsFixed(1), + // Предпросмотр всплывающего окна + _DialogPreview(), + const SizedBox(height: 16), + + // Развернуть настройки + _ExpandableSection( + title: "Настройки", + initiallyExpanded: false, + children: [ + _SliderTile( + icon: Icons.opacity, + label: "Прозрачность фона (профиль)", + value: theme.profileDialogOpacity, + min: 0.0, + max: 1.0, + divisions: 20, + onChanged: (value) => theme.setProfileDialogOpacity(value), + displayValue: "${(theme.profileDialogOpacity * 100).round()}%", + ), + _SliderTile( + icon: Icons.blur_on, + label: "Размытие фона (профиль)", + value: theme.profileDialogBlur, + min: 0.0, + max: 30.0, + divisions: 30, + onChanged: (value) => theme.setProfileDialogBlur(value), + displayValue: theme.profileDialogBlur.toStringAsFixed(1), + ), + ], ), ], ), @@ -493,6 +520,11 @@ class _CustomizationScreenState extends State { _ModernSection( title: "Панели чата", children: [ + // Предпросмотр панелей + _PanelsPreview(), + const SizedBox(height: 16), + + // Галочка включения эффекта стекла _CustomSettingTile( icon: Icons.tune, title: "Эффект стекла для панелей", @@ -502,46 +534,52 @@ class _CustomizationScreenState extends State { onChanged: (value) => theme.setUseGlassPanels(value), ), ), - if (theme.useGlassPanels) ...[ - const Divider(height: 24, indent: 16, endIndent: 16), - _SliderTile( - label: "Непрозрачность верхней панели", - value: theme.topBarOpacity, - min: 0.1, - max: 1.0, - divisions: 18, - onChanged: (value) => theme.setTopBarOpacity(value), - displayValue: "${(theme.topBarOpacity * 100).round()}%", - ), - _SliderTile( - label: "Размытие верхней панели", - value: theme.topBarBlur, - min: 0.0, - max: 20.0, - divisions: 40, - onChanged: (value) => theme.setTopBarBlur(value), - displayValue: theme.topBarBlur.toStringAsFixed(1), - ), - const Divider(height: 24, indent: 16, endIndent: 16), - _SliderTile( - label: "Непрозрачность нижней панели", - value: theme.bottomBarOpacity, - min: 0.1, - max: 1.0, - divisions: 18, - onChanged: (value) => theme.setBottomBarOpacity(value), - displayValue: "${(theme.bottomBarOpacity * 100).round()}%", - ), - _SliderTile( - label: "Размытие нижней панели", - value: theme.bottomBarBlur, - min: 0.0, - max: 20.0, - divisions: 40, - onChanged: (value) => theme.setBottomBarBlur(value), - displayValue: theme.bottomBarBlur.toStringAsFixed(1), - ), - ], + const SizedBox(height: 8), + + // Развернуть настройки + _ExpandableSection( + title: "Настройки", + initiallyExpanded: false, + children: [ + _SliderTile( + label: "Непрозрачность верхней панели", + value: theme.topBarOpacity, + min: 0.1, + max: 1.0, + divisions: 18, + onChanged: (value) => theme.setTopBarOpacity(value), + displayValue: "${(theme.topBarOpacity * 100).round()}%", + ), + _SliderTile( + label: "Размытие верхней панели", + value: theme.topBarBlur, + min: 0.0, + max: 20.0, + divisions: 40, + onChanged: (value) => theme.setTopBarBlur(value), + displayValue: theme.topBarBlur.toStringAsFixed(1), + ), + const Divider(height: 24, indent: 16, endIndent: 16), + _SliderTile( + label: "Непрозрачность нижней панели", + value: theme.bottomBarOpacity, + min: 0.1, + max: 1.0, + divisions: 18, + onChanged: (value) => theme.setBottomBarOpacity(value), + displayValue: "${(theme.bottomBarOpacity * 100).round()}%", + ), + _SliderTile( + label: "Размытие нижней панели", + value: theme.bottomBarBlur, + min: 0.0, + max: 20.0, + divisions: 40, + onChanged: (value) => theme.setBottomBarBlur(value), + displayValue: theme.bottomBarBlur.toStringAsFixed(1), + ), + ], + ), ], ), ], @@ -1501,3 +1539,346 @@ class _VideoWallpaperState extends State<_VideoWallpaper> { ); } } + +class _ExpandableSection extends StatefulWidget { + final String title; + final List children; + final bool initiallyExpanded; + + const _ExpandableSection({ + required this.title, + required this.children, + this.initiallyExpanded = false, + }); + + @override + State<_ExpandableSection> createState() => _ExpandableSectionState(); +} + +class _ExpandableSectionState extends State<_ExpandableSection> { + late bool _isExpanded; + + @override + void initState() { + super.initState(); + _isExpanded = widget.initiallyExpanded; + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + InkWell( + onTap: () => setState(() => _isExpanded = !_isExpanded), + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 4.0), + child: Row( + children: [ + Text( + widget.title, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + const Spacer(), + Icon( + _isExpanded ? Icons.expand_less : Icons.expand_more, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ], + ), + ), + ), + if (_isExpanded) ...[ + const SizedBox(height: 8), + ...widget.children, + ], + ], + ); + } +} + +class _MessageBubblesPreview extends StatelessWidget { + const _MessageBubblesPreview(); + + @override + Widget build(BuildContext context) { + final colors = Theme.of(context).colorScheme; + + final mockMyMessage = Message( + id: '1', + senderId: 100, + text: "Выглядит отлично! 🔥", + time: DateTime.now().millisecondsSinceEpoch, + attaches: const [], + ); + final mockTheirMessage = Message( + id: '2', + senderId: 200, + text: "Привет! Как тебе новый вид?", + time: DateTime.now().millisecondsSinceEpoch, + attaches: const [], + ); + + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: colors.surfaceVariant.withOpacity(0.3), + borderRadius: BorderRadius.circular(12), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: ChatMessageBubble( + message: mockTheirMessage, + isMe: false, + ), + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + const Spacer(), + Expanded( + child: ChatMessageBubble( + message: mockMyMessage, + isMe: true, + ), + ), + ], + ), + ], + ), + ); + } +} + +class _DialogPreview extends StatelessWidget { + const _DialogPreview(); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + final colors = Theme.of(context).colorScheme; + + return Container( + height: 120, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: colors.surfaceVariant.withOpacity(0.3), + borderRadius: BorderRadius.circular(12), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: Stack( + children: [ + // Фон с размытием + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + colors.primary.withOpacity(0.1), + colors.secondary.withOpacity(0.1), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ), + ), + // Размытие фона + if (theme.profileDialogBlur > 0) + BackdropFilter( + filter: ImageFilter.blur( + sigmaX: theme.profileDialogBlur, + sigmaY: theme.profileDialogBlur, + ), + child: Container(color: Colors.transparent), + ), + // Всплывающее окно + Center( + child: Container( + width: 200, + height: 80, + decoration: BoxDecoration( + color: colors.surface.withOpacity(theme.profileDialogOpacity), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: colors.outline.withOpacity(0.2), + ), + ), + child: Center( + child: Icon( + Icons.person, + color: colors.onSurface, + size: 32, + ), + ), + ), + ), + ], + ), + ), + ); + } +} + +class _PanelsPreview extends StatelessWidget { + const _PanelsPreview(); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + final colors = Theme.of(context).colorScheme; + + return Container( + height: 100, + decoration: BoxDecoration( + color: colors.surfaceVariant.withOpacity(0.3), + borderRadius: BorderRadius.circular(12), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Stack( + children: [ + // Фон - градиент от беловатого к серому для лучшей видимости эффекта стекла + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Colors.grey.shade300, // Беловатый сверху + Colors.grey.shade600, // Серый снизу + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + ), + Column( + children: [ + // Верхняя панель + if (theme.useGlassPanels) + ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: theme.topBarBlur, + sigmaY: theme.topBarBlur, + ), + child: Container( + height: 30, + color: colors.surface.withOpacity(theme.topBarOpacity), + child: Row( + children: [ + const SizedBox(width: 12), + CircleAvatar( + backgroundColor: colors.primaryContainer, + radius: 8, + ), + const SizedBox(width: 8), + Expanded( + child: Container( + height: 8, + decoration: BoxDecoration( + color: colors.primaryContainer, + borderRadius: BorderRadius.circular(4), + ), + ), + ), + const SizedBox(width: 40), + ], + ), + ), + ), + ) + else + Container( + height: 30, + color: colors.surface, + child: Row( + children: [ + const SizedBox(width: 12), + CircleAvatar( + backgroundColor: colors.primaryContainer, + radius: 8, + ), + const SizedBox(width: 8), + Expanded( + child: Container( + height: 8, + decoration: BoxDecoration( + color: colors.primaryContainer, + borderRadius: BorderRadius.circular(4), + ), + ), + ), + const SizedBox(width: 40), + ], + ), + ), + const Spacer(), + // Нижняя панель + if (theme.useGlassPanels) + ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: theme.bottomBarBlur, + sigmaY: theme.bottomBarBlur, + ), + child: Container( + height: 30, + color: colors.surface.withOpacity(theme.bottomBarOpacity), + child: Row( + children: [ + const SizedBox(width: 12), + Expanded( + child: Container( + height: 20, + decoration: BoxDecoration( + color: colors.surfaceVariant, + borderRadius: BorderRadius.circular(10), + ), + ), + ), + const SizedBox(width: 8), + Icon(Icons.send, color: colors.primary, size: 20), + const SizedBox(width: 12), + ], + ), + ), + ), + ) + else + Container( + height: 30, + color: colors.surface, + child: Row( + children: [ + const SizedBox(width: 12), + Expanded( + child: Container( + height: 20, + decoration: BoxDecoration( + color: colors.surfaceVariant, + borderRadius: BorderRadius.circular(10), + ), + ), + ), + const SizedBox(width: 8), + Icon(Icons.send, color: colors.primary, size: 20), + const SizedBox(width: 12), + ], + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/utils/theme_provider.dart b/lib/utils/theme_provider.dart index 34caada..8923963 100644 --- a/lib/utils/theme_provider.dart +++ b/lib/utils/theme_provider.dart @@ -786,17 +786,8 @@ class ThemeProvider with ChangeNotifier { myLightLight, ).toColor(); - final double theirLightSat = (hslLight.saturation * 0.2).clamp(0.05, 0.25); - final double theirLightLight = (hslLight.lightness * 0.1 + 0.85).clamp( - 0.85, - 0.98, - ); - final Color theirColorLight = HSLColor.fromAHSL( - hslLight.alpha, - hslLight.hue, - theirLightSat, - theirLightLight, - ).toColor(); + // Для светлой темы используем RGB(70, 70, 70) по умолчанию + final Color theirColorLight = const Color(0xFF464646); // RGB(70, 70, 70) if (_myBubbleColorLight == myColorLight && _theirBubbleColorLight == theirColorLight && diff --git a/lib/widgets/chat_message_bubble.dart b/lib/widgets/chat_message_bubble.dart index 7ed7c1d..9db1415 100644 --- a/lib/widgets/chat_message_bubble.dart +++ b/lib/widgets/chat_message_bubble.dart @@ -1206,7 +1206,7 @@ class ChatMessageBubble extends StatelessWidget { !message.isReply && !message.isForwarded; - final bubbleColor = _getBubbleColor(isMe, themeProvider, messageOpacity); + final bubbleColor = _getBubbleColor(isMe, themeProvider, messageOpacity, context); final textColor = _getTextColor( isMe, bubbleColor, @@ -1773,7 +1773,7 @@ class ChatMessageBubble extends StatelessWidget { final themeProvider = Provider.of(context); final isUltraOptimized = themeProvider.ultraOptimizeChats; final messageOpacity = themeProvider.messageBubbleOpacity; - final bubbleColor = _getBubbleColor(isMe, themeProvider, messageOpacity); + final bubbleColor = _getBubbleColor(isMe, themeProvider, messageOpacity, context); final textColor = _getTextColor( isMe, bubbleColor, @@ -3889,10 +3889,12 @@ class ChatMessageBubble extends StatelessWidget { bool isMe, ThemeProvider themeProvider, double messageOpacity, + BuildContext context, ) { + final bool isDark = Theme.of(context).brightness == Brightness.dark; final baseColor = isMe ? (themeProvider.myBubbleColor ?? const Color(0xFF2b5278)) - : (themeProvider.theirBubbleColor ?? const Color(0xFF182533)); + : (themeProvider.theirBubbleColor ?? (isDark ? const Color(0xFF182533) : const Color(0xFF464646))); return baseColor.withOpacity(1.0 - messageOpacity); }