Compare commits

...
1 Commits
6 changed files with 885 additions and 724 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
</p> </p>
<p align="center"> <p align="center">
<img src="https://img.shields.io/badge/Version-2.0.14-blueviolet?style=for-the-badge" alt="Version 2.0.14" /> <img src="https://img.shields.io/badge/Version-2.0.15-blueviolet?style=for-the-badge" alt="Version 2.0.15" />
<img src="https://img.shields.io/badge/Flutter-3.47+-02569B?style=for-the-badge&logo=flutter&logoColor=white" alt="Flutter" /> <img src="https://img.shields.io/badge/Flutter-3.47+-02569B?style=for-the-badge&logo=flutter&logoColor=white" alt="Flutter" />
<img src="https://img.shields.io/badge/Platform-Android%20%7C%20Linux%20%7C%20Windows-brightgreen?style=for-the-badge" alt="Platforms" /> <img src="https://img.shields.io/badge/Platform-Android%20%7C%20Linux%20%7C%20Windows-brightgreen?style=for-the-badge" alt="Platforms" />
<img src="https://img.shields.io/badge/Gitea-RarDog%2FLunaris-orange?style=for-the-badge&logo=gitea&logoColor=white" alt="Gitea" /> <img src="https://img.shields.io/badge/Gitea-RarDog%2FLunaris-orange?style=for-the-badge&logo=gitea&logoColor=white" alt="Gitea" />
+2 -2
View File
@@ -1,3 +1,3 @@
const appDisplayVersion = '2.0.14'; const appDisplayVersion = '2.0.15';
const appBuildNumber = 36; const appBuildNumber = 37;
const appReleaseChannel = 'Stable'; const appReleaseChannel = 'Stable';
+11
View File
@@ -1,4 +1,15 @@
const lunarisChangelog = [ const lunarisChangelog = [
LunarisChange(
version: '2.0.15',
title: 'Settings 2.0 Layout Fixes & Pinned Quick Navigation',
bullets: [
'Fixed vertical text wrapping on mobile: separated title and full-width SegmentedButtons for Theme, Language, and Media Quality so text never collapses.',
'Pinned Category Quick Navigation: bar now stays at the top of the screen and reliably scrolls to all 7 sections.',
'SingleChildScrollView Migration: ensures all section keys remain mounted and scrollable on demand.',
'Full-width Folder Structure Tile: added clean monospace path card with dedicated change button.',
'Header text overflow protections: wrapped title texts in Expanded across all settings cards.',
],
),
LunarisChange( LunarisChange(
version: '2.0.14', version: '2.0.14',
title: 'Settings 2.0: Modern Grouped Design & Quick Ergonomics', title: 'Settings 2.0: Modern Grouped Design & Quick Ergonomics',
@@ -69,8 +69,8 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
Scrollable.ensureVisible( Scrollable.ensureVisible(
ctx, ctx,
duration: const Duration(milliseconds: 380), duration: const Duration(milliseconds: 380),
curve: Curves.easeOutCubic, curve: Curves.easeInOutCubic,
alignment: 0.04, alignment: 0.0,
); );
} }
} }
@@ -79,6 +79,7 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context);
final isAndroid = Platform.isAndroid; final isAndroid = Platform.isAndroid;
final strings = AppStrings(settings.languageCode); final strings = AppStrings(settings.languageCode);
final isRu = strings.ru; final isRu = strings.ru;
@@ -139,21 +140,43 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
), ),
]; ];
return Align( return Column(
alignment: Alignment.topCenter, children: [
// Pinned Top Category Quick Navigation
Container(
padding: const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: theme.scaffoldBackgroundColor,
border: Border(
bottom: BorderSide(
color: theme.colorScheme.outlineVariant.withValues(alpha: 0.25),
),
),
),
child: Align(
alignment: Alignment.center,
child: ConstrainedBox( child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 860), constraints: const BoxConstraints(maxWidth: 860),
child: ListView( child: _CategoryQuickNav(
controller: _scrollController,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
children: [
// Top Quick-Nav Bar
_CategoryQuickNav(
categories: navCategories, categories: navCategories,
onSelect: _scrollTo, onSelect: _scrollTo,
), ),
const SizedBox(height: 16), ),
),
),
// Scrollable Settings Cards
Expanded(
child: SingleChildScrollView(
controller: _scrollController,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Align(
alignment: Alignment.topCenter,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 860),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// 1. General Section // 1. General Section
_SettingsCardGroup( _SettingsCardGroup(
sectionKey: _generalKey, sectionKey: _generalKey,
@@ -161,19 +184,13 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
icon: Icons.tune_rounded, icon: Icons.tune_rounded,
accentColor: const Color(0xFF6366F1), accentColor: const Color(0xFF6366F1),
children: [ children: [
_SettingsTile( _SettingsSegmentedTile<String>(
icon: Icons.brightness_6_rounded, icon: Icons.brightness_6_rounded,
iconColor: const Color(0xFF6366F1), iconColor: const Color(0xFF6366F1),
title: strings.theme, title: strings.theme,
subtitle: isRu subtitle: isRu
? 'Оформление цветовой схемы приложения' ? 'Оформление цветовой схемы приложения'
: 'App theme brightness mode', : 'App theme brightness mode',
trailing: SegmentedButton<String>(
showSelectedIcon: false,
style: const ButtonStyle(
visualDensity: VisualDensity.compact,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
segments: [ segments: [
ButtonSegment( ButtonSegment(
value: 'dark', value: 'dark',
@@ -197,7 +214,6 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
settings.copyWith(themeMode: set.first), settings.copyWith(themeMode: set.first),
), ),
), ),
),
const _SettingsDivider(), const _SettingsDivider(),
_SettingsSwitchTile( _SettingsSwitchTile(
icon: Icons.explicit_rounded, icon: Icons.explicit_rounded,
@@ -281,27 +297,21 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
icon: Icons.palette_rounded, icon: Icons.palette_rounded,
accentColor: const Color(0xFF8B5CF6), accentColor: const Color(0xFF8B5CF6),
children: [ children: [
_SettingsTile( _SettingsSegmentedTile<String>(
icon: Icons.translate_rounded, icon: Icons.translate_rounded,
iconColor: const Color(0xFF8B5CF6), iconColor: const Color(0xFF8B5CF6),
title: strings.language, title: strings.language,
subtitle: isRu subtitle: isRu
? 'Язык интерфейса приложения' ? 'Язык интерфейса приложения'
: 'App user interface language', : 'App user interface language',
trailing: SegmentedButton<String>(
showSelectedIcon: false,
style: const ButtonStyle(
visualDensity: VisualDensity.compact,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
segments: const [ segments: const [
ButtonSegment( ButtonSegment(
value: 'ru', value: 'ru',
label: Text('🇷🇺 RU'), label: Text('🇷🇺 Русский'),
), ),
ButtonSegment( ButtonSegment(
value: 'en', value: 'en',
label: Text('🇬🇧 EN'), label: Text('🇬🇧 English'),
), ),
], ],
selected: {settings.languageCode}, selected: {settings.languageCode},
@@ -310,7 +320,6 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
settings.copyWith(languageCode: set.first), settings.copyWith(languageCode: set.first),
), ),
), ),
),
const _SettingsDivider(), const _SettingsDivider(),
_SettingsSwitchTile( _SettingsSwitchTile(
icon: Icons.contrast_rounded, icon: Icons.contrast_rounded,
@@ -389,19 +398,13 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
icon: Icons.dashboard_customize_rounded, icon: Icons.dashboard_customize_rounded,
accentColor: const Color(0xFF10B981), accentColor: const Color(0xFF10B981),
children: [ children: [
_SettingsTile( _SettingsSegmentedTile<String>(
icon: Icons.high_quality_rounded, icon: Icons.high_quality_rounded,
iconColor: const Color(0xFF10B981), iconColor: const Color(0xFF10B981),
title: strings.mediaQuality, title: strings.mediaQuality,
subtitle: isRu subtitle: isRu
? 'Разрешение загружаемых картинок в сетке' ? 'Разрешение загружаемых картинок в сетке'
: 'Resolution profile for feed thumbnails', : 'Resolution profile for feed thumbnails',
trailing: SegmentedButton<String>(
showSelectedIcon: false,
style: const ButtonStyle(
visualDensity: VisualDensity.compact,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
segments: [ segments: [
for (final mode in MediaQualityMode.values) for (final mode in MediaQualityMode.values)
ButtonSegment( ButtonSegment(
@@ -410,7 +413,7 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
isRu isRu
? switch (mode) { ? switch (mode) {
MediaQualityMode.auto => 'Авто', MediaQualityMode.auto => 'Авто',
MediaQualityMode.dataSaver => 'Трафик', MediaQualityMode.dataSaver => 'Экономия',
MediaQualityMode.highQuality => 'HQ', MediaQualityMode.highQuality => 'HQ',
} }
: mode.label, : mode.label,
@@ -423,7 +426,6 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
settings.copyWith(mediaQualityMode: set.first), settings.copyWith(mediaQualityMode: set.first),
), ),
), ),
),
if (isAndroid) ...[ if (isAndroid) ...[
const _SettingsDivider(), const _SettingsDivider(),
Padding( Padding(
@@ -459,21 +461,16 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
isRu isRu
? 'Частота обновления экрана' ? 'Частота обновления экрана'
: 'Animation refresh profile', : 'Animation refresh profile',
style: Theme.of(context) style: theme.textTheme.bodyLarge
.textTheme
.bodyLarge
?.copyWith(fontWeight: FontWeight.w600), ?.copyWith(fontWeight: FontWeight.w600),
), ),
Text( Text(
isRu isRu
? 'Оптимизация плавности скролла ленты' ? 'Оптимизация плавности скролла ленты'
: 'Fluid 120-165 Hz scrolling optimization', : 'Fluid 120-165 Hz scrolling optimization',
style: Theme.of(context) style: theme.textTheme.bodyMedium
.textTheme
.bodyMedium
?.copyWith( ?.copyWith(
color: Theme.of(context) color: theme.colorScheme
.colorScheme
.onSurfaceVariant, .onSurfaceVariant,
), ),
), ),
@@ -482,7 +479,7 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
), ),
], ],
), ),
const SizedBox(height: 10), const SizedBox(height: 12),
Wrap( Wrap(
spacing: 8, spacing: 8,
runSpacing: 8, runSpacing: 8,
@@ -534,8 +531,8 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
iconColor: const Color(0xFF3B82F6), iconColor: const Color(0xFF3B82F6),
title: strings.desktopColumns, title: strings.desktopColumns,
subtitle: isRu subtitle: isRu
? 'Количество столбцов постов на ПК и широких экранах' ? 'Количество столбцов постов на ПК'
: 'Number of columns on desktop layout', : 'Columns on desktop layout',
value: settings.desktopColumns, value: settings.desktopColumns,
min: 3, min: 3,
max: 8, max: 8,
@@ -548,8 +545,8 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
iconColor: const Color(0xFF6366F1), iconColor: const Color(0xFF6366F1),
title: strings.mobileColumns, title: strings.mobileColumns,
subtitle: isRu subtitle: isRu
? 'Количество столбцов постов на телефонах' ? 'Количество столбцов постов на телефоне'
: 'Number of columns on phone layout', : 'Columns on phone layout',
value: settings.mobileColumns, value: settings.mobileColumns,
min: 1, min: 1,
max: 3, max: 3,
@@ -816,6 +813,10 @@ class _SettingsContentState extends ConsumerState<_SettingsContent> {
], ],
), ),
), ),
),
),
),
],
); );
} }
@@ -1058,8 +1059,9 @@ class _CategoryQuickNav extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
return SizedBox( return SizedBox(
height: 44, height: 40,
child: ListView.separated( child: ListView.separated(
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: categories.length, itemCount: categories.length,
separatorBuilder: (_, __) => const SizedBox(width: 8), separatorBuilder: (_, __) => const SizedBox(width: 8),
@@ -1067,14 +1069,14 @@ class _CategoryQuickNav extends StatelessWidget {
final cat = categories[index]; final cat = categories[index];
return Material( return Material(
color: theme.colorScheme.surfaceContainer, color: theme.colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(22), borderRadius: BorderRadius.circular(20),
child: InkWell( child: InkWell(
borderRadius: BorderRadius.circular(22), borderRadius: BorderRadius.circular(20),
onTap: () => onSelect(cat.key), onTap: () => onSelect(cat.key),
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22), borderRadius: BorderRadius.circular(20),
border: Border.all( border: Border.all(
color: theme.colorScheme.outlineVariant.withValues(alpha: 0.35), color: theme.colorScheme.outlineVariant.withValues(alpha: 0.35),
), ),
@@ -1148,13 +1150,15 @@ class _SettingsCardGroup extends StatelessWidget {
child: Icon(icon, size: 18, color: accentColor), child: Icon(icon, size: 18, color: accentColor),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Text( Expanded(
child: Text(
title, title,
style: theme.textTheme.titleMedium?.copyWith( style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
letterSpacing: -0.2, letterSpacing: -0.2,
), ),
), ),
),
], ],
), ),
), ),
@@ -1250,6 +1254,90 @@ class _SettingsTile extends StatelessWidget {
} }
} }
/// Full-width segmented tile so titles never get squeezed vertically on narrow screens.
class _SettingsSegmentedTile<T> extends StatelessWidget {
const _SettingsSegmentedTile({
required this.icon,
required this.iconColor,
required this.title,
required this.segments,
required this.selected,
required this.onSelectionChanged,
this.subtitle,
});
final IconData icon;
final Color iconColor;
final String title;
final String? subtitle;
final List<ButtonSegment<T>> segments;
final Set<T> selected;
final ValueChanged<Set<T>> onSelectionChanged;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Container(
width: 38,
height: 38,
decoration: BoxDecoration(
color: iconColor.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, size: 20, color: iconColor),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: theme.textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.w600,
),
),
if (subtitle != null) ...[
const SizedBox(height: 2),
Text(
subtitle!,
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
],
),
),
],
),
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: SegmentedButton<T>(
showSelectedIcon: false,
style: const ButtonStyle(
visualDensity: VisualDensity.compact,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
segments: segments,
selected: selected,
onSelectionChanged: onSelectionChanged,
),
),
],
),
);
}
}
class _SettingsSwitchTile extends StatelessWidget { class _SettingsSwitchTile extends StatelessWidget {
const _SettingsSwitchTile({ const _SettingsSwitchTile({
required this.icon, required this.icon,
@@ -1404,44 +1492,96 @@ class _SettingsFolderStructureTile extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
return _SettingsTile( return InkWell(
icon: Icons.folder_copy_rounded, onTap: () => _showPicker(context),
iconColor: const Color(0xFF0EA5E9), child: Padding(
title: isRu ? 'Папки скачивания' : 'Download folder structure', padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
subtitle: isRu child: Column(
? 'Шаблон организации сохраняемых файлов' crossAxisAlignment: CrossAxisAlignment.start,
: 'Path template for organized downloads', children: [
trailing: Container( Row(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), children: [
Container(
width: 38,
height: 38,
decoration: BoxDecoration( decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHighest, color: const Color(0xFF0EA5E9).withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(12),
border: Border.all( ),
color: theme.colorScheme.outlineVariant.withValues(alpha: 0.4), child: const Icon(
Icons.folder_copy_rounded,
size: 20,
color: Color(0xFF0EA5E9),
), ),
), ),
child: Row( const SizedBox(width: 14),
mainAxisSize: MainAxisSize.min, Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
currentTemplate, isRu ? 'Папки скачивания' : 'Download folder structure',
style: TextStyle( style: theme.textTheme.bodyLarge?.copyWith(
fontFamily: 'monospace',
fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: theme.colorScheme.primary,
), ),
), ),
const SizedBox(width: 4), const SizedBox(height: 2),
Icon( Text(
Icons.unfold_more_rounded, isRu
size: 16, ? 'Шаблон организации сохраняемых файлов'
: 'Path template for organized downloads',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant, color: theme.colorScheme.onSurfaceVariant,
), ),
),
],
),
),
const SizedBox(width: 8),
Icon(
Icons.chevron_right_rounded,
color: theme.colorScheme.onSurfaceVariant,
),
],
),
const SizedBox(height: 10),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHighest.withValues(alpha: 0.5),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: theme.colorScheme.outlineVariant.withValues(alpha: 0.35),
),
),
child: Row(
children: [
const Icon(Icons.folder_open_rounded, size: 16),
const SizedBox(width: 8),
Expanded(
child: Text(
currentTemplate,
style: TextStyle(
fontFamily: 'monospace',
fontSize: 13,
fontWeight: FontWeight.w600,
color: theme.colorScheme.primary,
),
),
),
Text(
isRu ? 'Выбрать' : 'Select',
style: theme.textTheme.labelMedium?.copyWith(
color: theme.colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
],
),
),
], ],
), ),
), ),
onTap: () => _showPicker(context),
); );
} }
@@ -1627,12 +1767,14 @@ class _TagListEditorState extends State<_TagListEditor> {
children: [ children: [
Icon(widget.icon, size: 20, color: widget.accentColor), Icon(widget.icon, size: 20, color: widget.accentColor),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Expanded(
child: Text(
widget.title, widget.title,
style: theme.textTheme.titleMedium?.copyWith( style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
),
const SizedBox(width: 8), const SizedBox(width: 8),
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
@@ -1743,12 +1885,14 @@ class _ColorSwatches extends StatelessWidget {
children: [ children: [
const Icon(Icons.palette_outlined, size: 20, color: Color(0xFF8B5CF6)), const Icon(Icons.palette_outlined, size: 20, color: Color(0xFF8B5CF6)),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Expanded(
child: Text(
'Акцентный цвет интерфейса', 'Акцентный цвет интерфейса',
style: theme.textTheme.titleSmall?.copyWith( style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
),
], ],
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
@@ -1830,12 +1974,14 @@ class _TabVisibilityEditor extends StatelessWidget {
children: [ children: [
const Icon(Icons.tab_rounded, size: 20, color: Color(0xFF8B5CF6)), const Icon(Icons.tab_rounded, size: 20, color: Color(0xFF8B5CF6)),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Expanded(
child: Text(
'Отображение вкладок навигации', 'Отображение вкладок навигации',
style: theme.textTheme.titleSmall?.copyWith( style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
),
], ],
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
+1 -1
View File
@@ -1,7 +1,7 @@
name: gel_rule_app name: gel_rule_app
description: Local backend/core layer for a Flutter booru browser app. description: Local backend/core layer for a Flutter booru browser app.
publish_to: "none" publish_to: "none"
version: 2.0.14+36 version: 2.0.15+37
environment: environment:
sdk: ">=3.3.0 <4.0.0" sdk: ">=3.3.0 <4.0.0"
+15 -11
View File
@@ -6,8 +6,9 @@ import 'package:gel_rule_app/features/settings/presentation/settings_controller.
import 'package:gel_rule_app/features/settings/presentation/settings_screen.dart'; import 'package:gel_rule_app/features/settings/presentation/settings_screen.dart';
void main() { void main() {
testWidgets('SettingsScreen renders category quick nav and sections', (tester) async { testWidgets('SettingsScreen renders category quick nav and sections without overflow', (tester) async {
tester.view.physicalSize = const Size(1200, 2400); // Test on typical mobile viewport: 390x844
tester.view.physicalSize = const Size(390, 844);
tester.view.devicePixelRatio = 1.0; tester.view.devicePixelRatio = 1.0;
addTearDown(() { addTearDown(() {
tester.view.resetPhysicalSize(); tester.view.resetPhysicalSize();
@@ -34,22 +35,25 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Verify Category Quick Nav renders // Verify pinned Category Quick Nav renders
expect(find.text('Основное'), findsWidgets); expect(find.text('Основное'), findsWidgets);
expect(find.text('Внешний вид'), findsWidgets); expect(find.text('Внешний вид'), findsWidgets);
expect(find.text('Лента и раскладка'), findsWidgets);
expect(find.text('Фильтры'), findsWidgets);
expect(find.text('Хранилище'), findsWidgets);
// Verify Theme mode segment buttons // Verify Theme mode segment buttons
expect(find.text('Темная'), findsOneWidget); expect(find.text('Темная'), findsOneWidget);
expect(find.text('Светлая'), findsOneWidget); expect(find.text('Светлая'), findsOneWidget);
// Both ThemeMode and MediaQualityMode have 'Авто'
expect(find.text('Авто'), findsNWidgets(2));
// Verify Language segmented buttons // Tap a quick nav chip to test scrolling
expect(find.text('🇷🇺 RU'), findsOneWidget); await tester.tap(find.text('Внешний вид').first);
expect(find.text('🇬🇧 EN'), findsOneWidget); await tester.pumpAndSettle();
// Verify language segment is visible and tappable
expect(find.text('🇷🇺 Русский'), findsOneWidget);
expect(find.text('🇬🇧 English'), findsOneWidget);
// Tap English to test selection
await tester.tap(find.text('🇬🇧 English'));
await tester.pumpAndSettle();
}); });
} }