refactor: уменьшил отступы от краев + сделал кастомизацию разворачиваемой
This commit is contained in:
@@ -3897,7 +3897,7 @@ class _ChatScreenState extends State<ChatScreen>
|
|||||||
final cs = Theme.of(context).colorScheme;
|
final cs = Theme.of(context).colorScheme;
|
||||||
final accent = cs.primary;
|
final accent = cs.primary;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(12, 8, 12, 6),
|
padding: const EdgeInsets.fromLTRB(8, 8, 8, 6),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|||||||
@@ -1,187 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
|
||||||
|
|
||||||
import '../../widgets/connection_status.dart';
|
|
||||||
|
|
||||||
import '../../../core/utils/haptics.dart';
|
|
||||||
import '../../widgets/glossy_pill.dart';
|
|
||||||
import 'app_icon_screen.dart';
|
|
||||||
import 'appearance_screen.dart';
|
|
||||||
import 'chat_background_screen.dart';
|
|
||||||
import 'font_settings_screen.dart';
|
|
||||||
import 'message_actions_screen.dart';
|
|
||||||
import 'theme_settings_screen.dart';
|
|
||||||
|
|
||||||
class _CustomizationCategory {
|
|
||||||
final IconData icon;
|
|
||||||
final String title;
|
|
||||||
final String subtitle;
|
|
||||||
final WidgetBuilder builder;
|
|
||||||
|
|
||||||
const _CustomizationCategory({
|
|
||||||
required this.icon,
|
|
||||||
required this.title,
|
|
||||||
required this.subtitle,
|
|
||||||
required this.builder,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class CustomizationScreen extends StatelessWidget {
|
|
||||||
const CustomizationScreen({super.key});
|
|
||||||
|
|
||||||
static const List<_CustomizationCategory> _categories = [
|
|
||||||
_CustomizationCategory(
|
|
||||||
icon: Symbols.dark_mode,
|
|
||||||
title: 'Тема',
|
|
||||||
subtitle: 'Светлая, тёмная, AMOLED, расписание',
|
|
||||||
builder: _buildThemeSettings,
|
|
||||||
),
|
|
||||||
_CustomizationCategory(
|
|
||||||
icon: Symbols.palette,
|
|
||||||
title: 'Внешний вид',
|
|
||||||
subtitle: 'Акцентный цвет интерфейса',
|
|
||||||
builder: _buildAppearance,
|
|
||||||
),
|
|
||||||
_CustomizationCategory(
|
|
||||||
icon: Symbols.wallpaper,
|
|
||||||
title: 'Фон чатов',
|
|
||||||
subtitle: 'Общие обои и темы для всех чатов',
|
|
||||||
builder: _buildChatBackground,
|
|
||||||
),
|
|
||||||
_CustomizationCategory(
|
|
||||||
icon: Symbols.text_fields,
|
|
||||||
title: 'Шрифты',
|
|
||||||
subtitle: 'Шрифт приложения, свои шрифты, размер текста',
|
|
||||||
builder: _buildFontSettings,
|
|
||||||
),
|
|
||||||
_CustomizationCategory(
|
|
||||||
icon: Symbols.touch_app,
|
|
||||||
title: 'Меню действий',
|
|
||||||
subtitle: 'Радиальное или список — для долгого нажатия на сообщение',
|
|
||||||
builder: _buildMessageActions,
|
|
||||||
),
|
|
||||||
_CustomizationCategory(
|
|
||||||
icon: Symbols.apps,
|
|
||||||
title: 'Иконка приложения',
|
|
||||||
subtitle: 'Default или Minimal — иконка на главном экране',
|
|
||||||
builder: _buildAppIcon,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
static Widget _buildAppearance(BuildContext context) =>
|
|
||||||
const AppearanceScreen();
|
|
||||||
|
|
||||||
static Widget _buildChatBackground(BuildContext context) =>
|
|
||||||
const ChatBackgroundScreen();
|
|
||||||
|
|
||||||
static Widget _buildFontSettings(BuildContext context) =>
|
|
||||||
const FontSettingsScreen();
|
|
||||||
|
|
||||||
static Widget _buildThemeSettings(BuildContext context) =>
|
|
||||||
const ThemeSettingsScreen();
|
|
||||||
|
|
||||||
static Widget _buildMessageActions(BuildContext context) =>
|
|
||||||
const MessageActionsScreen();
|
|
||||||
|
|
||||||
static Widget _buildAppIcon(BuildContext context) => const AppIconScreen();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final cs = Theme.of(context).colorScheme;
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: cs.surface,
|
|
||||||
appBar: ConnectionTitleBar(
|
|
||||||
titleText: 'Кастомизация',
|
|
||||||
backgroundColor: cs.surface,
|
|
||||||
),
|
|
||||||
body: SafeArea(
|
|
||||||
top: false,
|
|
||||||
child: ListView(
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 120),
|
|
||||||
children: [
|
|
||||||
for (final category in _categories) ...[
|
|
||||||
_CategoryCard(
|
|
||||||
category: category,
|
|
||||||
onTap: () {
|
|
||||||
Haptics.tap();
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(builder: category.builder),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CategoryCard extends StatelessWidget {
|
|
||||||
final _CustomizationCategory category;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
const _CategoryCard({required this.category, required this.onTap});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final cs = Theme.of(context).colorScheme;
|
|
||||||
return GlossyPill(
|
|
||||||
color: cs.surfaceContainerHigh,
|
|
||||||
borderRadius: BorderRadius.circular(28),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 18),
|
|
||||||
depth: 6,
|
|
||||||
onTap: onTap,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 48,
|
|
||||||
height: 48,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: cs.primaryContainer,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Icon(
|
|
||||||
category.icon,
|
|
||||||
color: cs.onPrimaryContainer,
|
|
||||||
size: 24,
|
|
||||||
weight: 500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
category.title,
|
|
||||||
style: TextStyle(
|
|
||||||
color: cs.onSurface,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 3),
|
|
||||||
Text(
|
|
||||||
category.subtitle,
|
|
||||||
style: TextStyle(
|
|
||||||
color: cs.onSurfaceVariant,
|
|
||||||
fontSize: 13,
|
|
||||||
height: 1.3,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Icon(Symbols.chevron_right, color: cs.outline, size: 22),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
|
||||||
|
import '../../../core/utils/haptics.dart';
|
||||||
|
import '../../widgets/glossy_pill.dart';
|
||||||
|
import '../../widgets/settings_card.dart';
|
||||||
|
import 'app_icon_screen.dart';
|
||||||
|
import 'appearance_screen.dart';
|
||||||
|
import 'chat_background_screen.dart';
|
||||||
|
import 'font_settings_screen.dart';
|
||||||
|
import 'message_actions_screen.dart';
|
||||||
|
import 'theme_settings_screen.dart';
|
||||||
|
|
||||||
|
class _CustomizationCategory {
|
||||||
|
final IconData icon;
|
||||||
|
final String title;
|
||||||
|
final WidgetBuilder builder;
|
||||||
|
|
||||||
|
const _CustomizationCategory({
|
||||||
|
required this.icon,
|
||||||
|
required this.title,
|
||||||
|
required this.builder,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomizationSection extends StatefulWidget {
|
||||||
|
const CustomizationSection({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CustomizationSection> createState() => _CustomizationSectionState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CustomizationSectionState extends State<CustomizationSection> {
|
||||||
|
bool _expanded = false;
|
||||||
|
|
||||||
|
static final List<_CustomizationCategory> _categories = [
|
||||||
|
_CustomizationCategory(
|
||||||
|
icon: Symbols.dark_mode,
|
||||||
|
title: 'Тема',
|
||||||
|
builder: (context) => const ThemeSettingsScreen(),
|
||||||
|
),
|
||||||
|
_CustomizationCategory(
|
||||||
|
icon: Symbols.palette,
|
||||||
|
title: 'Внешний вид',
|
||||||
|
builder: (context) => const AppearanceScreen(),
|
||||||
|
),
|
||||||
|
_CustomizationCategory(
|
||||||
|
icon: Symbols.wallpaper,
|
||||||
|
title: 'Фон чатов',
|
||||||
|
builder: (context) => const ChatBackgroundScreen(),
|
||||||
|
),
|
||||||
|
_CustomizationCategory(
|
||||||
|
icon: Symbols.text_fields,
|
||||||
|
title: 'Шрифты',
|
||||||
|
builder: (context) => const FontSettingsScreen(),
|
||||||
|
),
|
||||||
|
_CustomizationCategory(
|
||||||
|
icon: Symbols.touch_app,
|
||||||
|
title: 'Меню действий',
|
||||||
|
builder: (context) => const MessageActionsScreen(),
|
||||||
|
),
|
||||||
|
_CustomizationCategory(
|
||||||
|
icon: Symbols.apps,
|
||||||
|
title: 'Иконка приложения',
|
||||||
|
builder: (context) => const AppIconScreen(),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
void _toggle() {
|
||||||
|
Haptics.tap();
|
||||||
|
setState(() => _expanded = !_expanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _open(_CustomizationCategory category) {
|
||||||
|
Haptics.tap();
|
||||||
|
Navigator.push(context, MaterialPageRoute(builder: category.builder));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
|
return GlossyPill(
|
||||||
|
color: cs.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
depth: 6,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildHeader(cs),
|
||||||
|
AnimatedSize(
|
||||||
|
duration: const Duration(milliseconds: 220),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
child: _expanded
|
||||||
|
? Column(children: _buildCategoryTiles(cs))
|
||||||
|
: const SizedBox(width: double.infinity),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHeader(ColorScheme cs) {
|
||||||
|
return Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: _toggle,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 17),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.palette,
|
||||||
|
color: cs.onSurfaceVariant,
|
||||||
|
size: 22,
|
||||||
|
weight: 400,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'Кастомизация',
|
||||||
|
style: TextStyle(
|
||||||
|
color: cs.onSurface,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AnimatedRotation(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
turns: _expanded ? 0.5 : 0,
|
||||||
|
child: Icon(
|
||||||
|
Symbols.expand_more,
|
||||||
|
color: cs.outline,
|
||||||
|
size: 22,
|
||||||
|
weight: 400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _buildCategoryTiles(ColorScheme cs) {
|
||||||
|
final tiles = <Widget>[];
|
||||||
|
for (var i = 0; i < _categories.length; i++) {
|
||||||
|
final category = _categories[i];
|
||||||
|
tiles.add(
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 58),
|
||||||
|
child: Divider(
|
||||||
|
height: 1,
|
||||||
|
thickness: 1,
|
||||||
|
color: cs.outlineVariant.withValues(alpha: 0.35),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
tiles.add(
|
||||||
|
SettingsNavTile(
|
||||||
|
icon: category.icon,
|
||||||
|
label: category.title,
|
||||||
|
onTap: () => _open(category),
|
||||||
|
isLast: i == _categories.length - 1,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return tiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ import '../digital_id/digital_id_screen.dart';
|
|||||||
import '../digital_id/digital_id_web_screen.dart';
|
import '../digital_id/digital_id_web_screen.dart';
|
||||||
import '../webapp/web_app_screen.dart';
|
import '../webapp/web_app_screen.dart';
|
||||||
import 'cloud_storage_screen.dart';
|
import 'cloud_storage_screen.dart';
|
||||||
import 'customization_screen.dart';
|
import 'customization_section.dart';
|
||||||
import 'debug_menu_screen.dart';
|
import 'debug_menu_screen.dart';
|
||||||
import 'devices_screen.dart';
|
import 'devices_screen.dart';
|
||||||
import 'edit_profile_screen.dart';
|
import 'edit_profile_screen.dart';
|
||||||
@@ -298,26 +298,10 @@ class _SettingsTabState extends State<SettingsTab> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
const SliverToBoxAdapter(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
|
padding: EdgeInsets.fromLTRB(16, 12, 16, 0),
|
||||||
child: _buildSection(
|
child: CustomizationSection(),
|
||||||
context,
|
|
||||||
items: [
|
|
||||||
_SettingsItem(
|
|
||||||
icon: Symbols.palette,
|
|
||||||
label: 'Кастомизация',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const CustomizationScreen(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
|
|||||||
@@ -530,8 +530,8 @@ class MessageBubble extends StatelessWidget {
|
|||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
left: 12,
|
left: 8,
|
||||||
right: 12,
|
right: 8,
|
||||||
top: topMargin,
|
top: topMargin,
|
||||||
bottom: bottomMargin,
|
bottom: bottomMargin,
|
||||||
),
|
),
|
||||||
@@ -545,9 +545,7 @@ class MessageBubble extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
if (showAvatar)
|
if (showAvatar)
|
||||||
_buildLeadingAvatar(cs)
|
_buildLeadingAvatar(cs)
|
||||||
else if (showAvatarSlot && chatType != "CHAT")
|
else if (showAvatarSlot && chatType == "CHAT")
|
||||||
const SizedBox(width: 0)
|
|
||||||
else if (showAvatarSlot)
|
|
||||||
const CircleAvatar(
|
const CircleAvatar(
|
||||||
radius: 15,
|
radius: 15,
|
||||||
backgroundColor: Color(0x00000000),
|
backgroundColor: Color(0x00000000),
|
||||||
|
|||||||
Reference in New Issue
Block a user