# Flutter 9: Desktop (macOS & Windows) — Window Management, Menu, Shortcuts va Moslashuvchan UI

> **Holat:** 2026-yil. Flutter endi faqat mobil ramka emas — u macOS, Windows va Linux uchun to'laqonli 60/120 FPS native ishlovchi desktop platformasidir. Desktop ilovalar mobil ilovalardan tubdan farq qiladi: sichqoncha bilan hover, klaviatura tugmalari (Shortcuts), oyna o'lchamlarini o'zgartirish (Window Resizing), ko'p ustunli interfeys (Master-Detail) va tizim menyulari (Menu Bar). Ushbu darsda Flutter bilan desktop ilovalar yaratishning barcha nozikliklarini amaliy kodlar bilan ko'rib chiqamiz.

---

## Mundarija

1. [Desktop va Mobile o'rtasidagi asosiy farqlar](#farqlar)
2. [Desktop platformalari arxitekturasi va Runner tuzilishi](#arxitektura)
3. [Window Management: window_manager bilan oynani boshqarish](#window-manager)
4. [Tizim menyusi: PlatformMenuBar va Context Menu](#menu-bar)
5. [Klaviatura Shortcuts va Actions tizimi](#shortcuts)
6. [Sichqoncha, Hover effektlar va Kursorni boshqarish](#mouse-hover)
7. [Desktop Scrollbar va Wheel aylantirish qoidalari](#scrollbars)
8. [Moslashuvchan (Adaptive & Responsive) Dizayn: Breakpointlar](#adaptive-layout)
9. [To'liq Desktop & Mobile moslashuvchan kod namunasi](#full-code)
10. [Build va Distribution: macOS Sandbox va Windows Installer](#packaging)
11. [Xulosa va Maslahatlar](#xulosa)
12. [Rasmiy Manbalar](#manbalar)

---

## 1. Desktop va Mobile o'rtasidagi asosiy farqlar {#farqlar}

Mobil ilovani shunchaki desktop ekraniga cho'zib qo'yish — bu eng katta xato (anti-pattern) hisoblanadi. 

```
┌─────────────────────────────────┐      ┌────────────────────────────────────────────────────────┐
│        MOBILE INTERFACE         │      │                   DESKTOP INTERFACE                    │
├─────────────────────────────────┤      ├────────────────────────────────────────────────────────┤
│ • Kichik sensorli ekran (Touch) │      │ • Katta displey, sichqoncha (Mouse/Trackpad) + Klaviat.│
│ • Bitta oyna, to'liq ekran      │      │ • Erkin o'lchamli oyna (Resizable, Min/Max/Restore)   │
│ • BottomNavigationBar (pastda)  │      │ • NavigationRail / Sidebar (chapda) + Split View       │
│ • Orqaga surish (Swipe back)    │      │ • Klaviatura tugmalari (Cmd/Ctrl+Z, Esc, Enter)        │
│ • Yengil dialoglar (ActionSheet)│      │ • Sichqonchaning o'ng tugmasi (Context Menu) + Hover   │
│ • Virtual klaviatura            │      │ • Native Menu Bar (macOS Apple menyusi, Win titlebar) │
└─────────────────────────────────┘      └────────────────────────────────────────────────────────┘
```

| Xususiyat | Mobil (iOS / Android) | Desktop (macOS / Windows) |
| :--- | :--- | :--- |
| **Kiritish qurilmasi** | Barmoq teginishi (Touch, Gestures) | Kursor, g'ildirak (Wheel), klaviatura |
| **Hover holati** | Mavjud emas | Juda muhim (tugmalar, havolalar, ro'yxatlar) |
| **Navigatsiya** | `BottomNavigationBar` yoki `Drawer` | `NavigationRail` yoki doimiy `Sidebar` |
| **Oyna o'lchami** | Ruxsat etilgan qat'iy ekran | Foydalanuvchi erkin o'zgartiradi |
| **Ko'rinish zichligi** | Katta teginish maydonlari (min 44-48px) | Yilqiq, zich ma'lumotlar (`VisualDensity.compact`) |
| **Scrollbar** | Sukut bo'yicha yashirin, surilganda chiqadi | Har doim ko'rinib turishi maqsadga muvofiq |

---

## 2. Desktop platformalari arxitekturasi va Runner tuzilishi {#arxitektura}

Flutter Desktop ilovasi qanday ishga tushadi?
- **macOS:** Ilova `macos/` papkasidagi Cocoa (Objective-C/Swift) `NSApplication` konteyneri ichida ishlaydi. Metal / Impeller grafik dvigateli orqali bevosita GPU ga render qilinadi.
- **Windows:** Ilova `windows/` papkasidagi Win32 (C++) ilovasi ichida ishlaydi. DirectX 11/12 (yoki Angle/Skia) orqali render qilinadi.

Loyihada desktopni faollashtirish:
```bash
flutter config --enable-macos-desktop
flutter config --enable-windows-desktop
flutter create --platforms=macos,windows,ios,android .
```

---

## 3. Window Management: `window_manager` bilan oynani boshqarish {#window-manager}

Desktop ilovada oyna boshlang'ich o'lchami, uning markazda ochilishi, sarlavhasi (title) va minimal/maksimal cheklovlarni boshqarish uchun `window_manager` paketi ishlatiladi:

```yaml
# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  window_manager: ^0.4.3
```

### `main.dart` da oynani ishga tushirish:

```dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:window_manager/window_manager.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Faqat Desktop platformalarda oynani sozlaymiz
  if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
    await windowManager.ensureInitialized();

    const windowOptions = WindowOptions(
      size: Size(1100, 750),
      minimumSize: Size(650, 500),
      center: true,
      backgroundColor: Colors.transparent,
      skipTaskbar: false,
      title: 'DevStudio Desktop',
      titleBarStyle: TitleBarStyle.normal, // yoki TitleBarStyle.hidden macOS sarlavhasi uchun
    );

    await windowManager.waitUntilReadyToShow(windowOptions, () async {
      await windowManager.show();
      await windowManager.focus();
    });
  }

  runApp(const MultiplatformApp());
}
```

---

## 4. Tizim menyusi: `PlatformMenuBar` va Context Menu {#menu-bar}

macOS da ekranning eng yuqorisida (Apple logotipi yonida) tizim menyusi mavjud. Flutter buni `PlatformMenuBar` orqali native macOS menyusiga ulaydi:

```dart
class MultiplatformApp extends StatelessWidget {
  const MultiplatformApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: Colors.indigo,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: PlatformMenuBar(
        menus: [
          PlatformMenu(
            label: 'DevStudio',
            menus: [
              PlatformMenuItem(
                label: 'Dastur haqida...',
                onSelected: () => debugPrint('Haqida bosildi'),
              ),
              const PlatformMenuItemGroup(
                members: [
                  PlatformProvidedMenuItem(type: PlatformProvidedMenuItemType.quit),
                ],
              ),
            ],
          ),
          PlatformMenu(
            label: 'Fayl',
            menus: [
              PlatformMenuItem(
                label: 'Yangi loyiha',
                shortcut: const SingleActivator(LogicalKeyboardKey.keyN, meta: true),
                onSelected: () => debugPrint('Yangi loyiha ochildi'),
              ),
              PlatformMenuItem(
                label: 'Saqlash',
                shortcut: const SingleActivator(LogicalKeyboardKey.keyS, meta: true),
                onSelected: () => debugPrint('Saqlandi'),
              ),
            ],
          ),
        ],
        child: const HomeScreen(),
      ),
    );
  }
}
```

### Sichqonchaning o'ng tugmasi (Context Menu):
Ixtiyoriy element ustiga o'ng tugma bosilganda kontekst menyu chiqarish:

```dart
GestureDetector(
  onSecondaryTapUp: (details) {
    showMenu(
      context: context,
      position: RelativeRect.fromLTRB(
        details.globalPosition.dx,
        details.globalPosition.dy,
        details.globalPosition.dx + 1,
        details.globalPosition.dy + 1,
      ),
      items: const [
        PopupMenuItem(value: 'copy', child: Text('Nusxa olish')),
        PopupMenuItem(value: 'edit', child: Text('Tahrirlash')),
        PopupMenuDivider(),
        PopupMenuItem(value: 'delete', child: Text('O\'chirish', style: TextStyle(color: Colors.red))),
      ],
    );
  },
  child: const Card(child: ListTile(title: Text('O\'ng tugmani bosing'))),
)
```

---

## 5. Klaviatura Shortcuts va Actions tizimi {#shortcuts}

Desktop foydalanuvchisi sichqonchaga teginmasdan klaviatura yordamida ishlashni afzal ko'radi (masalan: `Cmd+K` yoki `Ctrl+K` bilan qidiruv ochish, `Esc` bilan yopish).

Flutter'da bu **Shortcuts + Actions + Intent** modeli orqali ishlaydi:

```dart
// 1. Intent e'lon qilamiz
class SearchIntent extends Intent {
  const SearchIntent();
}

class RefreshIntent extends Intent {
  const RefreshIntent();
}

// 2. Widget ichida bog'laymiz
Widget buildKeyboardShortcutsWrapper({required Widget child, required BuildContext context}) {
  return Shortcuts(
    shortcuts: <ShortcutActivator, Intent>{
      // Cmd+K (macOS) yoki Ctrl+K (Windows)
      LogicalKeySet(
        Platform.isMacOS ? LogicalKeyboardKey.meta : LogicalKeyboardKey.control,
        LogicalKeyboardKey.keyK,
      ): const SearchIntent(),
      // Cmd+R yoki Ctrl+R
      LogicalKeySet(
        Platform.isMacOS ? LogicalKeyboardKey.meta : LogicalKeyboardKey.control,
        LogicalKeyboardKey.keyR,
      ): const RefreshIntent(),
    },
    child: Actions(
      actions: <Type, Action<Intent>>{
        SearchIntent: CallbackAction<SearchIntent>(
          onInvoke: (intent) => openSearchDialog(context),
        ),
        RefreshIntent: CallbackAction<RefreshIntent>(
          onInvoke: (intent) => refreshData(),
        ),
      },
      child: Focus(
        autofocus: true,
        child: child,
      ),
    ),
  );
}
```

---

## 6. Sichqoncha, Hover effektlar va Kursorni boshqarish {#mouse-hover}

Desktopda element ustiga kursor olib kelinganda foydalanuvchiga fikr-mulohaza (feedback) berish zarur:
- Kursor shakli o'zgarishi (`SystemMouseCursors.click`)
- Fon rangi yoki soya o'zgarishi (Hover highlight)

```dart
class DesktopInteractiveCard extends StatefulWidget {
  final String title;
  final VoidCallback onTap;

  const DesktopInteractiveCard({super.key, required this.title, required this.onTap});

  @override
  State<DesktopInteractiveCard> createState() => _DesktopInteractiveCardState();
}

class _DesktopInteractiveCardState extends State<DesktopInteractiveCard> {
  bool _isHovered = false;

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      cursor: SystemMouseCursors.click,
      onEnter: (_) => setState(() => _isHovered = true),
      onExit: (_) => setState(() => _isHovered = false),
      child: GestureDetector(
        onTap: widget.onTap,
        child: AnimatedContainer(
          duration: const Duration(milliseconds: 200),
          curve: Curves.easeOutCubic,
          padding: const EdgeInsets.all(16),
          decoration: BoxDecoration(
            color: _isHovered ? Colors.indigo.shade50 : Colors.white,
            borderRadius: BorderRadius.circular(12),
            border: Border.all(
              color: _isHovered ? Colors.indigo : Colors.grey.shade300,
              width: _isHovered ? 1.5 : 1,
            ),
            boxShadow: _isHovered
                ? [BoxShadow(color: Colors.indigo.withOpacity(0.12), blurRadius: 16, offset: const Offset(0, 6))]
                : [],
          ),
          child: Row(
            children: [
              Icon(Icons.dashboard_rounded, color: _isHovered ? Colors.indigo : Colors.grey.shade700),
              const SizedBox(width: 12),
              Text(
                widget.title,
                style: TextStyle(
                  fontWeight: _isHovered ? FontWeight.w600 : FontWeight.w500,
                  color: _isHovered ? Colors.indigo : Colors.black87,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
```

---

## 7. Desktop Scrollbar va Wheel aylantirish qoidalari {#scrollbars}

Mobil telefonda scrollbar odatda yashirin bo'ladi. Ammo Windows va macOS da uzun ro'yxatlarda scrollbar doimo ko'rinib turishi yoki hover qilinganda qalinlashishi shart.

Desktop uchun global Scrollbar sozlamasi:

```dart
MaterialApp(
  theme: ThemeData(
    scrollbarTheme: ScrollbarThemeData(
      thumbVisibility: WidgetStateProperty.all(true), // Har doim ko'rinsin
      thickness: WidgetStateProperty.resolveWith((states) {
        if (states.contains(WidgetState.hovered)) return 10.0;
        return 6.0;
      }),
      radius: const Radius.circular(8),
      thumbColor: WidgetStateProperty.all(Colors.grey.shade400),
    ),
  ),
);
```

Har bir `ListView` yoki `SingleChildScrollView` ga `ScrollController` bog'lash shart, aks holda Flutter `ScrollController not attached` ogohlantirishini beradi:

```dart
final scrollController = ScrollController();

Scrollbar(
  controller: scrollController,
  thumbVisibility: true,
  child: ListView.builder(
    controller: scrollController,
    itemCount: 100,
    itemBuilder: (context, index) => ListTile(title: Text('Element #$index')),
  ),
);
```

---

## 8. Moslashuvchan (Adaptive & Responsive) Dizayn: Breakpointlar {#adaptive-layout}

Bitta kod bazasidan foydalangan holda ilova mobil telefonda ham, planshetda ham, 4K monitorli Mac va Windows kompyuterda ham mukammal ko'rinishi kerak:

```
┌────────────────────────────────────────────────────────────────────────┐
│                        RESPONSIVE BREAKPOINTS                          │
├──────────────────┬───────────────────────┬─────────────────────────────┤
│ Ekran kengligi   │ Qurilma turi          │ Tavsiya etilgan navigatsiya │
├──────────────────┼───────────────────────┼─────────────────────────────┤
│ < 600px          │ Smartfon (Mobile)     │ BottomNavigationBar         │
│ 600px - 1024px   │ Planshet / Kichik Win │ NavigationRail (ixcham)     │
│ > 1024px         │ Desktop / Katta Mac   │ Doimiy Sidebar + Split View │
└──────────────────┴───────────────────────┴─────────────────────────────┘
```

### Breakpointlarni aniqlash klassi:

```dart
enum ScreenType { mobile, tablet, desktop }

class ResponsiveHelper {
  static ScreenType getScreenType(BuildContext context) {
    final width = MediaQuery.sizeOf(context).width;
    if (width < 600) return ScreenType.mobile;
    if (width < 1024) return ScreenType.tablet;
    return ScreenType.desktop;
  }

  static bool isMobile(BuildContext context) => getScreenType(context) == ScreenType.mobile;
  static bool isDesktop(BuildContext context) => getScreenType(context) == ScreenType.desktop;
}
```

---

## 9. To'liq Desktop & Mobile moslashuvchan kod namunasi {#full-code}

Quyidagi kod — ham iOS/Android'da (Bottom Bar), ham macOS/Windows'da (Yonbosh Sidebar + Master-Detail ko'rinish) moslashib ishlaydigan mukammal shablondir:

```dart
import 'package:flutter/material.dart';

class AdaptiveDashboardScreen extends StatefulWidget {
  const AdaptiveDashboardScreen({super.key});

  @override
  State<AdaptiveDashboardScreen> createState() => _AdaptiveDashboardScreenState();
}

class _AdaptiveDashboardScreenState extends State<AdaptiveDashboardScreen> {
  int _selectedIndex = 0;
  int? _selectedItemId = 1; // Master-detail uchun tanlangan element

  final List<String> _navigationDestinations = [
    'Bosh sahifa',
    'Buyurtmalar',
    'Statistika',
    'Sozlamalar',
  ];

  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.sizeOf(context).width;
    final isDesktop = screenWidth >= 1024;
    final isTablet = screenWidth >= 600 && screenWidth < 1024;

    // MOBIL KO'RINISH (< 600px)
    if (screenWidth < 600) {
      return Scaffold(
        appBar: AppBar(title: Text(_navigationDestinations[_selectedIndex])),
        body: _buildContentArea(isMasterDetail: false),
        bottomNavigationBar: NavigationBar(
          selectedIndex: _selectedIndex,
          onDestinationSelected: (idx) => setState(() => _selectedIndex = idx),
          destinations: const [
            NavigationDestination(icon: Icon(Icons.home_outlined), selectedIcon: Icon(Icons.home), label: 'Asosiy'),
            NavigationDestination(icon: Icon(Icons.shopping_bag_outlined), selectedIcon: Icon(Icons.shopping_bag), label: 'Buyurtma'),
            NavigationDestination(icon: Icon(Icons.bar_chart_outlined), selectedIcon: Icon(Icons.bar_chart), label: 'Tahlil'),
            NavigationDestination(icon: Icon(Icons.settings_outlined), selectedIcon: Icon(Icons.settings), label: 'Sozlama'),
          ],
        ),
      );
    }

    // DESKTOP VA TABLET KO'RINISH (>= 600px)
    return Scaffold(
      body: Row(
        children: [
          // YONBOSH MENYU (NavigationRail yoki Sidebar)
          NavigationRail(
            extended: isDesktop, // Desktopda yozuvlari bilan kengayadi
            minExtendedWidth: 200,
            selectedIndex: _selectedIndex,
            onDestinationSelected: (idx) => setState(() => _selectedIndex = idx),
            leading: Padding(
              padding: const EdgeInsets.symmetric(vertical: 20),
              child: Row(
                children: [
                  const Icon(Icons.bolt, color: Colors.indigo, size: 28),
                  if (isDesktop) ...[
                    const SizedBox(width: 8),
                    const Text('DEVSTUDIO', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
                  ],
                ],
              ),
            ),
            destinations: const [
              NavigationRailDestination(icon: Icon(Icons.home_outlined), selectedIcon: Icon(Icons.home), label: Text('Asosiy')),
              NavigationRailDestination(icon: Icon(Icons.shopping_bag_outlined), selectedIcon: Icon(Icons.shopping_bag), label: Text('Buyurtmalar')),
              NavigationRailDestination(icon: Icon(Icons.bar_chart_outlined), selectedIcon: Icon(Icons.bar_chart), label: Text('Statistika')),
              NavigationRailDestination(icon: Icon(Icons.settings_outlined), selectedIcon: Icon(Icons.settings), label: Text('Sozlamalar')),
            ],
          ),
          const VerticalDivider(width: 1, thickness: 1),

          // ASOSIY TARKIB (MASTER-DETAIL VIEW DESKTOPDA)
          Expanded(
            child: _buildContentArea(isMasterDetail: isDesktop),
          ),
        ],
      ),
    );
  }

  Widget _buildContentArea({required bool isMasterDetail}) {
    if (!isMasterDetail) {
      // Yagona ustunli ro'yxat (Mobil va planshet)
      return ListView.separated(
        padding: const EdgeInsets.all(16),
        itemCount: 20,
        separatorBuilder: (_, __) => const SizedBox(height: 8),
        itemBuilder: (context, i) => ListTile(
          tileColor: Colors.grey.shade100,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
          title: Text('Loyiha #$i'),
          subtitle: const Text('Status: Faol ishlov jarayonida'),
          trailing: const Icon(Icons.chevron_right),
          onTap: () {
            // Mobilda alohida sahifaga o'tiladi
          },
        ),
      );
    }

    // Desktopda ikkita ustun: Chapda Ro'yxat (Master), O'ngda Tafsilot (Detail)
    return Row(
      children: [
        // Master ustun (350px kenglik)
        SizedBox(
          width: 360,
          child: ListView.builder(
            padding: const EdgeInsets.all(16),
            itemCount: 20,
            itemBuilder: (context, i) {
              final isSelected = _selectedItemId == i;
              return Padding(
                padding: const EdgeInsets.only(bottom: 8),
                child: ListTile(
                  selected: isSelected,
                  selectedTileColor: Colors.indigo.shade50,
                  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
                  title: Text('Mijoz loyihasi #$i', style: TextStyle(fontWeight: isSelected ? FontWeight.bold : FontWeight.normal)),
                  subtitle: Text('Jami mablag\': \$${(i + 1) * 250}'),
                  onTap: () => setState(() => _selectedItemId = i),
                ),
              );
            },
          ),
        ),
        const VerticalDivider(width: 1, thickness: 1),

        // Detail ustun (Qolgan barcha bo'sh joy)
        Expanded(
          child: Container(
            color: Colors.white,
            padding: const EdgeInsets.all(32),
            child: _selectedItemId == null
                ? const Center(child: Text('Tafsilotlarni ko\'rish uchun chapdan elementni tanlang'))
                : Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Loyiha #$_selectedItemId tafsilotlari', style: Theme.of(context).textTheme.headlineMedium),
                      const SizedBox(height: 16),
                      const Text('Ushbu qismda loyihaga oid barcha API ma\'lumotlari, jadvallar va boshqaruv tugmalari joylashadi.'),
                      const SizedBox(height: 24),
                      Wrap(
                        spacing: 12,
                        children: [
                          ElevatedButton.icon(onPressed: () {}, icon: const Icon(Icons.check), label: const Text('Tasdiqlash')),
                          OutlinedButton.icon(onPressed: () {}, icon: const Icon(Icons.download), label: const Text('Eksport PDF')),
                        ],
                      ),
                    ],
                  ),
          ),
        ),
      ],
    );
  }
}
```

---

## 10. Build va Distribution: macOS Sandbox va Windows Installer {#packaging}

Desktop ilovani tayyorlab foydalanuvchiga taqdim etishda ikki muhim qoida mavjud:

### 10.1 macOS App Sandbox & Tarmoq ruxsati
macOS da ilovangiz internetdan ma'lumot olishi (REST API bilan ishlashi) uchun `macos/Runner/DebugProfile.entitlements` va `Release.entitlements` fayllariga tarmoq ruxsatini qo'shish shart:

```xml
<key>com.apple.security.network.client</key>
<true/>
```
*Agar bu qo'shilmasa, ilova simulyatorda ishlashi mumkin, ammo release qilinganda barcha API so'rovlari `SocketException` bilan qulab tushadi!*

### 10.2 Windows EXE va Installer
Windows uchun yig'ish:
```bash
flutter build windows --release
```
Natija `build/windows/x64/runner/Release/` papkasida hosil bo'ladi. Uni foydalanuvchilarga tarqatish uchun **Inno Setup** yoki **MSIX Packaging Tool** vositasidan foydalaniladi:
```bash
dart run msix:create
```

---

## 11. Xulosa va Maslahatlar {#xulosa}

1. **Universal layout qoidasi:** Har doim kenglikni `MediaQuery.sizeOf(context).width` orqali tekshiring va 600px hamda 1024px nuqtalarida interfeysni avtomatik qayta quring.
2. **Kursorlarni unutmang:** Har qanday bosiladigan card yoki tugmaga `MouseRegion(cursor: SystemMouseCursors.click)` yoki `InkWell` bering.
3. **Klaviatura bilan do'st bo'ling:** Qidiruv, saqlash, yopish kabi eng ko'p bajariladigan amallarga `Shortcuts` bog'lang.

---

## 12. Rasmiy Manbalar {#manbalar}

- [Flutter Desktop Documentation](https://docs.flutter.dev/platform-integration/desktop)
- [Building Responsive and Adaptive Apps in Flutter](https://docs.flutter.dev/ui/adaptive-responsive)
- [Flutter window_manager paketi](https://pub.dev/packages/window_manager)
- [Flutter Desktop Sample App (Github)](https://github.com/flutter/samples/tree/main/desktop_photo_search)
