Refactor AppShell layout: Implement responsive design for desktop and mobile modes, update application title and identifier, and add global.json for SDK versioning

This commit is contained in:
Данил Ярош 2026-01-20 10:37:41 +03:00
parent 388b440c28
commit 293179b3c0
5 changed files with 112 additions and 32 deletions

View File

@ -13,9 +13,9 @@
FlyoutWidth="220"
Shell.NavBarIsVisible="False"
x:Name="MainShell"
FlyoutBehavior="Flyout">
FlyoutBehavior="Disabled">
<!-- Flyout Header -->
<!-- Flyout Header (показывается только в режиме Flyout) -->
<Shell.FlyoutHeader>
<Grid HeightRequest="100" BackgroundColor="#0A5ED7" Padding="15">
<VerticalStackLayout VerticalOptions="Center" Spacing="8">
@ -58,24 +58,33 @@
</DataTemplate>
</Shell.ItemTemplate>
<!-- FlyoutItems с Shell.TabBarIsVisible для контроля TabBar -->
<FlyoutItem Title="Новости" Icon="feed.png" FlyoutDisplayOptions="AsSingleItem">
<!-- TabBar для узких экранов (мобильные устройства) -->
<TabBar x:Name="MobileTabBar">
<ShellContent Title="Новости" Icon="feed.png" ContentTemplate="{DataTemplate pages:PostsPage}" />
<ShellContent Title="Сотрудники" Icon="employees.png" ContentTemplate="{DataTemplate pages:EmployeesPage}" />
<ShellContent Title="Отделы" Icon="department.png" ContentTemplate="{DataTemplate pages:DepartmentsPage}" />
<ShellContent Title="Заявления" Icon="statements.png" ContentTemplate="{DataTemplate pages:RequestsPage}" />
<ShellContent Title="Профиль" Icon="profile.png" ContentTemplate="{DataTemplate pages:ProfilePage}" />
</TabBar>
<!-- FlyoutItems для широких экранов (скрыт по умолчанию, управляется программно) -->
<FlyoutItem x:Name="DesktopFlyoutNews" Title="Новости" Icon="feed.png" FlyoutDisplayOptions="AsSingleItem" IsVisible="False">
<ShellContent ContentTemplate="{DataTemplate pages:PostsPage}" />
</FlyoutItem>
<FlyoutItem Title="Сотрудники" Icon="employees.png" FlyoutDisplayOptions="AsSingleItem">
<FlyoutItem x:Name="DesktopFlyoutEmployees" Title="Сотрудники" Icon="employees.png" FlyoutDisplayOptions="AsSingleItem" IsVisible="False">
<ShellContent ContentTemplate="{DataTemplate pages:EmployeesPage}" />
</FlyoutItem>
<FlyoutItem Title="Отделы" Icon="department.png" FlyoutDisplayOptions="AsSingleItem">
<FlyoutItem x:Name="DesktopFlyoutDepartments" Title="Отделы" Icon="department.png" FlyoutDisplayOptions="AsSingleItem" IsVisible="False">
<ShellContent ContentTemplate="{DataTemplate pages:DepartmentsPage}" />
</FlyoutItem>
<FlyoutItem Title="Заявления" Icon="statements.png" FlyoutDisplayOptions="AsSingleItem">
<FlyoutItem x:Name="DesktopFlyoutRequests" Title="Заявления" Icon="statements.png" FlyoutDisplayOptions="AsSingleItem" IsVisible="False">
<ShellContent ContentTemplate="{DataTemplate pages:RequestsPage}" />
</FlyoutItem>
<FlyoutItem Title="Профиль" Icon="profile.png" FlyoutDisplayOptions="AsSingleItem">
<FlyoutItem x:Name="DesktopFlyoutProfile" Title="Профиль" Icon="profile.png" FlyoutDisplayOptions="AsSingleItem" IsVisible="False">
<ShellContent ContentTemplate="{DataTemplate pages:ProfilePage}" />
</FlyoutItem>

View File

@ -7,6 +7,7 @@ public partial class AppShell : Shell
{
// Порог ширины для переключения на вертикальное меню (в пикселях)
private const double WideScreenThreshold = 768;
private bool _isDesktopMode;
public AppShell()
{
@ -29,7 +30,7 @@ public partial class AppShell : Shell
Trace.WriteLine("=== APPSHELL CONSTRUCTOR END ===");
// Устанавливаем начальный режим меню
UpdateFlyoutBehavior();
UpdateLayoutMode();
// Инициализация без ожидания (так await нельзя из синхронного конструктора)
_ = InitializeAsync();
@ -116,13 +117,15 @@ public partial class AppShell : Shell
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
UpdateFlyoutBehavior(width);
UpdateLayoutMode(width);
}
/// <summary>
/// Обновляет режим меню в зависимости от ширины экрана
/// Обновляет режим макета в зависимости от ширины экрана
/// На широких экранах: Flyout (вертикальное меню слева)
/// На узких экранах: TabBar (нижняя панель)
/// </summary>
private void UpdateFlyoutBehavior(double? width = null)
private void UpdateLayoutMode(double? width = null)
{
try
{
@ -135,32 +138,94 @@ public partial class AppShell : Shell
currentWidth = DeviceDisplay.MainDisplayInfo.Width / DeviceDisplay.MainDisplayInfo.Density;
}
Trace.WriteLine($"[AppShell] UpdateFlyoutBehavior: width={currentWidth}, threshold={WideScreenThreshold}");
bool shouldBeDesktop = currentWidth >= WideScreenThreshold;
if (currentWidth >= WideScreenThreshold)
Trace.WriteLine($"[AppShell] UpdateLayoutMode: width={currentWidth}, threshold={WideScreenThreshold}, shouldBeDesktop={shouldBeDesktop}, currentMode={_isDesktopMode}");
// Проверяем, нужно ли менять режим
if (shouldBeDesktop == _isDesktopMode)
{
// Широкий экран - Flyout всегда открыт (Locked)
if (FlyoutBehavior != FlyoutBehavior.Locked)
{
FlyoutBehavior = FlyoutBehavior.Locked;
FlyoutIsPresented = true;
Trace.WriteLine("[AppShell] ✅ Режим: Locked (вертикальное меню слева)");
}
return; // Режим не изменился
}
_isDesktopMode = shouldBeDesktop;
if (_isDesktopMode)
{
// Десктопный режим - показываем Flyout, скрываем TabBar
SwitchToDesktopMode();
}
else
{
// Узкий экран - Flyout выдвигается по кнопке/свайпу
if (FlyoutBehavior != FlyoutBehavior.Flyout)
{
FlyoutBehavior = FlyoutBehavior.Flyout;
FlyoutIsPresented = false;
Trace.WriteLine("[AppShell] ✅ Режим: Flyout (выдвижное меню)");
}
// Мобильный режим - показываем TabBar, скрываем Flyout
SwitchToMobileMode();
}
}
catch (Exception ex)
{
Trace.WriteLine($"[AppShell] ⚠️ Ошибка UpdateFlyoutBehavior: {ex.Message}");
Trace.WriteLine($"[AppShell] ⚠️ Ошибка UpdateLayoutMode: {ex.Message}");
}
}
private void SwitchToDesktopMode()
{
try
{
Trace.WriteLine("[AppShell] >>> Переключение на десктопный режим (Flyout)");
// Скрываем TabBar
MobileTabBar.IsVisible = false;
// Показываем FlyoutItems
DesktopFlyoutNews.IsVisible = true;
DesktopFlyoutEmployees.IsVisible = true;
DesktopFlyoutDepartments.IsVisible = true;
DesktopFlyoutRequests.IsVisible = true;
DesktopFlyoutProfile.IsVisible = true;
// Включаем Flyout в режим Locked (всегда видим)
FlyoutBehavior = FlyoutBehavior.Locked;
FlyoutIsPresented = true;
// Переходим на первый FlyoutItem
CurrentItem = DesktopFlyoutNews;
Trace.WriteLine("[AppShell] ✅ Десктопный режим активирован");
}
catch (Exception ex)
{
Trace.WriteLine($"[AppShell] ❌ Ошибка SwitchToDesktopMode: {ex.Message}");
}
}
private void SwitchToMobileMode()
{
try
{
Trace.WriteLine("[AppShell] >>> Переключение на мобильный режим (TabBar)");
// Скрываем FlyoutItems
DesktopFlyoutNews.IsVisible = false;
DesktopFlyoutEmployees.IsVisible = false;
DesktopFlyoutDepartments.IsVisible = false;
DesktopFlyoutRequests.IsVisible = false;
DesktopFlyoutProfile.IsVisible = false;
// Отключаем Flyout
FlyoutBehavior = FlyoutBehavior.Disabled;
FlyoutIsPresented = false;
// Показываем TabBar
MobileTabBar.IsVisible = true;
// Переходим на TabBar
CurrentItem = MobileTabBar;
Trace.WriteLine("[AppShell] ✅ Мобильный режим активирован");
}
catch (Exception ex)
{
Trace.WriteLine($"[AppShell] ❌ Ошибка SwitchToMobileMode: {ex.Message}");
}
}
}

View File

@ -21,10 +21,10 @@
<Nullable>enable</Nullable>
<!-- Display name -->
<ApplicationTitle>CORP-MAUI</ApplicationTitle>
<ApplicationTitle>HiRo</ApplicationTitle>
<!-- App Identifier -->
<ApplicationId>com.companyname.corpmaui</ApplicationId>
<ApplicationId>com.robotail.hiro</ApplicationId>
<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
@ -50,7 +50,7 @@
<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" ForegroundScale="0.85" Color="#512BD4" />
<!-- Splash Screen -->

6
global.json Normal file
View File

@ -0,0 +1,6 @@
{
"sdk": {
"version": "9.0.306",
"rollForward": "latestMinor"
}
}

BIN
hiro.keystore Normal file

Binary file not shown.