feat: mobile-like window size and always-visible stats
- Changed window size to mobile phone format (400x800) - Removed width condition for ActiveProxyFooter - now always visible - Added run-umbrix.sh launch script with icon copying - Stats cards now display on all screen sizes
This commit is contained in:
523
docs/BOTTOM_SHEET_BUTTONS.md
Normal file
523
docs/BOTTOM_SHEET_BUTTONS.md
Normal file
@@ -0,0 +1,523 @@
|
||||
# 🎨 Кнопки Bottom Sheet - Расположение и Иконки
|
||||
|
||||
## 📍 Где находятся эти кнопки?
|
||||
|
||||
**Файл:** [lib/features/profile/add/add_profile_modal.dart](../lib/features/profile/add/add_profile_modal.dart)
|
||||
|
||||
**Класс:** `AddProfileModal` (extends `HookConsumerWidget`)
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Структура кнопок
|
||||
|
||||
### 1. **"Добавить из буфера обмена"** (Clipboard)
|
||||
|
||||
```dart
|
||||
_Button(
|
||||
key: const ValueKey("add_from_clipboard_button"),
|
||||
label: t.profile.add.fromClipboard,
|
||||
icon: FluentIcons.clipboard_paste_24_regular, // ← ИКОНКА
|
||||
size: buttonWidth,
|
||||
onTap: () async {
|
||||
final captureResult = await Clipboard.getData(Clipboard.kTextPlain).then((value) => value?.text ?? '');
|
||||
if (addProfileState.isLoading) return;
|
||||
ref.read(addProfileProvider.notifier).add(captureResult);
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
**Иконка:** `FluentIcons.clipboard_paste_24_regular`
|
||||
**Перевод:** `t.profile.add.fromClipboard` → "Добавить из буфера обмена"
|
||||
**Цвет:** `theme.colorScheme.primary` (основной цвет темы)
|
||||
|
||||
---
|
||||
|
||||
### 2. **"Сканировать QR-код"** (QR Code)
|
||||
|
||||
```dart
|
||||
_Button(
|
||||
key: const ValueKey("add_by_qr_code_button"),
|
||||
label: t.profile.add.scanQr,
|
||||
icon: FluentIcons.qr_code_24_regular, // ← ИКОНКА
|
||||
size: buttonWidth,
|
||||
onTap: () async {
|
||||
final cr = await const QRCodeScannerScreen().open(context);
|
||||
if (cr == null) return;
|
||||
if (addProfileState.isLoading) return;
|
||||
ref.read(addProfileProvider.notifier).add(cr);
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
**Иконка:** `FluentIcons.qr_code_24_regular`
|
||||
**Перевод:** `t.profile.add.scanQr` → "Сканировать QR-код"
|
||||
**Цвет:** `theme.colorScheme.primary`
|
||||
**Условие:** Показывается только на мобильных (`!PlatformUtils.isDesktop`)
|
||||
|
||||
---
|
||||
|
||||
### 3. **"Добавить WARP"**
|
||||
|
||||
```dart
|
||||
Material(
|
||||
key: const ValueKey("add_warp_button"),
|
||||
elevation: 8,
|
||||
color: theme.colorScheme.surface,
|
||||
surfaceTintColor: theme.colorScheme.surfaceTint,
|
||||
shadowColor: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
await addProfileModal(context, ref);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
FluentIcons.add_24_regular, // ← ИКОНКА
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
t.profile.add.addWarp, // "Добавить WARP"
|
||||
style: theme.textTheme.labelLarge?.copyWith(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
**Иконка:** `FluentIcons.add_24_regular`
|
||||
**Перевод:** `t.profile.add.addWarp` → "Добавить WARP"
|
||||
**Стиль:** Полная ширина кнопка с текстом и иконкой
|
||||
|
||||
---
|
||||
|
||||
### 4. **"Ввести вручную"**
|
||||
|
||||
```dart
|
||||
_Button(
|
||||
key: const ValueKey("add_manually_button"),
|
||||
label: t.profile.add.manually,
|
||||
icon: FluentIcons.add_24_regular, // ← ИКОНКА
|
||||
size: buttonWidth,
|
||||
onTap: () async {
|
||||
context.pop();
|
||||
await const NewProfileRoute().push(context);
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
**Иконка:** `FluentIcons.add_24_regular`
|
||||
**Перевод:** `t.profile.add.manually` → "Ввести вручную"
|
||||
**Условие:**
|
||||
- На мобильных → показывается как отдельная кнопка внизу
|
||||
- На Desktop → показывается вместо кнопки "Сканировать QR-код"
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Откуда берутся иконки?
|
||||
|
||||
### Пакет: **fluentui_system_icons**
|
||||
|
||||
**pubspec.yaml:**
|
||||
```yaml
|
||||
dependencies:
|
||||
fluentui_system_icons: ^1.1.229
|
||||
```
|
||||
|
||||
**Импорт:**
|
||||
```dart
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
```
|
||||
|
||||
### Используемые иконки:
|
||||
|
||||
| Иконка | Код | Описание |
|
||||
|--------|-----|----------|
|
||||
| 📋 | `FluentIcons.clipboard_paste_24_regular` | Буфер обмена |
|
||||
| 📷 | `FluentIcons.qr_code_24_regular` | QR-код |
|
||||
| ➕ | `FluentIcons.add_24_regular` | Добавить/Плюс |
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Как иконки меняют цвет?
|
||||
|
||||
### Автоматическая смена цвета через Theme
|
||||
|
||||
```dart
|
||||
Icon(
|
||||
FluentIcons.clipboard_paste_24_regular,
|
||||
size: buttonWidth / 3,
|
||||
color: theme.colorScheme.primary, // ← ЦВЕТ ИЗ ТЕМЫ
|
||||
)
|
||||
```
|
||||
|
||||
**Откуда берётся цвет:**
|
||||
- `theme.colorScheme.primary` - основной цвет темы
|
||||
- Меняется автоматически при смене темы (светлая/тёмная)
|
||||
- Определяется в настройках темы приложения
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Где определяется тема?
|
||||
|
||||
**Путь к настройкам темы:**
|
||||
```dart
|
||||
// Тема определяется в:
|
||||
lib/core/preferences/
|
||||
lib/features/config_option/
|
||||
```
|
||||
|
||||
**Как работает смена цвета:**
|
||||
|
||||
1. **Пользователь выбирает тему** (светлую/тёмную/автоматическую)
|
||||
2. **Flutter Theme система** применяет `ColorScheme`
|
||||
3. **ColorScheme.primary** меняется на цвет из палитры темы
|
||||
4. **Icon виджеты** автоматически перерисовываются с новым цветом
|
||||
|
||||
**Пример:**
|
||||
```dart
|
||||
// Светлая тема
|
||||
colorScheme: ColorScheme.light(
|
||||
primary: Color(0xFF007AFF), // Синий
|
||||
)
|
||||
|
||||
// Тёмная тема
|
||||
colorScheme: ColorScheme.dark(
|
||||
primary: Color(0xFF0A84FF), // Светло-синий
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 Формат иконок
|
||||
|
||||
### FluentUI System Icons - это векторные иконки
|
||||
|
||||
**Технические детали:**
|
||||
- **Формат:** IconData (Flutter встроенный формат)
|
||||
- **Источник:** Microsoft Fluent Design System
|
||||
- **Размер:** Масштабируемые (векторные, не растровые)
|
||||
- **Цвет:** Одноцветные, цвет задаётся программно
|
||||
- **Вариации:** `_regular`, `_filled` (обычные и заполненные)
|
||||
|
||||
**НЕ PNG, НЕ SVG файлы!** Это встроенные векторные глифы в шрифте.
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Как изменить иконки?
|
||||
|
||||
### Вариант 1: Использовать другую иконку из того же пакета
|
||||
|
||||
```dart
|
||||
// Было:
|
||||
icon: FluentIcons.clipboard_paste_24_regular,
|
||||
|
||||
// Стало:
|
||||
icon: FluentIcons.copy_24_regular, // Другая иконка
|
||||
```
|
||||
|
||||
**Посмотреть все доступные иконки:**
|
||||
https://github.com/microsoft/fluentui-system-icons/tree/main/fonts
|
||||
|
||||
### Вариант 2: Использовать Material Icons
|
||||
|
||||
```dart
|
||||
// Изменить импорт
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// Использовать Material иконку
|
||||
icon: Icons.content_paste,
|
||||
```
|
||||
|
||||
### Вариант 3: Использовать свои SVG иконки
|
||||
|
||||
**1. Добавьте пакет flutter_svg:**
|
||||
```yaml
|
||||
dependencies:
|
||||
flutter_svg: ^2.0.10
|
||||
```
|
||||
|
||||
**2. Сохраните SVG в assets:**
|
||||
```
|
||||
assets/images/icons/clipboard.svg
|
||||
assets/images/icons/qr_code.svg
|
||||
```
|
||||
|
||||
**3. Используйте в коде:**
|
||||
```dart
|
||||
// Вместо Icon виджета
|
||||
SvgPicture.asset(
|
||||
'assets/images/icons/clipboard.svg',
|
||||
width: size / 3,
|
||||
height: size / 3,
|
||||
colorFilter: ColorFilter.mode(
|
||||
theme.colorScheme.primary,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Как изменить размер иконок?
|
||||
|
||||
```dart
|
||||
Icon(
|
||||
FluentIcons.clipboard_paste_24_regular,
|
||||
size: buttonWidth / 3, // ← РАЗМЕР
|
||||
color: theme.colorScheme.primary,
|
||||
)
|
||||
```
|
||||
|
||||
**Текущий размер:** `buttonWidth / 3`
|
||||
- buttonWidth рассчитывается из ширины экрана
|
||||
- Примерно 48-56 пикселей для иконки
|
||||
|
||||
**Чтобы изменить:**
|
||||
```dart
|
||||
// Фиксированный размер
|
||||
size: 64,
|
||||
|
||||
// Относительный размер
|
||||
size: buttonWidth / 2, // Больше
|
||||
size: buttonWidth / 4, // Меньше
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Как изменить цвет иконок?
|
||||
|
||||
### Вариант 1: Использовать другой цвет из темы
|
||||
|
||||
```dart
|
||||
Icon(
|
||||
FluentIcons.clipboard_paste_24_regular,
|
||||
color: theme.colorScheme.secondary, // Вторичный цвет
|
||||
// или:
|
||||
// color: theme.colorScheme.tertiary,
|
||||
// color: theme.colorScheme.error,
|
||||
// color: theme.colorScheme.onSurface,
|
||||
)
|
||||
```
|
||||
|
||||
### Вариант 2: Использовать кастомный цвет
|
||||
|
||||
```dart
|
||||
Icon(
|
||||
FluentIcons.clipboard_paste_24_regular,
|
||||
color: Color(0xFF00FF00), // Зелёный
|
||||
// или:
|
||||
// color: Colors.red,
|
||||
// color: Colors.amber,
|
||||
)
|
||||
```
|
||||
|
||||
### Вариант 3: Градиент (сложнее)
|
||||
|
||||
Для градиента нужно использовать `ShaderMask`:
|
||||
|
||||
```dart
|
||||
ShaderMask(
|
||||
shaderCallback: (Rect bounds) {
|
||||
return LinearGradient(
|
||||
colors: [Colors.blue, Colors.purple],
|
||||
).createShader(bounds);
|
||||
},
|
||||
child: Icon(
|
||||
FluentIcons.clipboard_paste_24_regular,
|
||||
size: buttonWidth / 3,
|
||||
color: Colors.white, // Базовый цвет (будет заменён градиентом)
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📱 Адаптивность кнопок
|
||||
|
||||
### Мобильные (Android/iOS):
|
||||
```dart
|
||||
if (!PlatformUtils.isDesktop)
|
||||
_Button(
|
||||
label: t.profile.add.scanQr,
|
||||
icon: FluentIcons.qr_code_24_regular,
|
||||
)
|
||||
```
|
||||
|
||||
**Показывается:**
|
||||
- ✅ Добавить из буфера обмена
|
||||
- ✅ Сканировать QR-код
|
||||
- ✅ Добавить WARP
|
||||
- ✅ Ввести вручную
|
||||
|
||||
### Desktop (Windows/macOS/Linux):
|
||||
```dart
|
||||
else
|
||||
_Button(
|
||||
label: t.profile.add.manually,
|
||||
icon: FluentIcons.add_24_regular,
|
||||
)
|
||||
```
|
||||
|
||||
**Показывается:**
|
||||
- ✅ Добавить из буфера обмена
|
||||
- ✅ Ввести вручную (вместо QR)
|
||||
- ✅ Добавить WARP
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Класс _Button
|
||||
|
||||
**Виджет для квадратных кнопок:**
|
||||
|
||||
```dart
|
||||
class _Button extends StatelessWidget {
|
||||
final String label; // Текст кнопки
|
||||
final IconData icon; // Иконка (FluentIcons.*)
|
||||
final double size; // Размер кнопки (квадрат)
|
||||
final VoidCallback onTap; // Действие при нажатии
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: Material(
|
||||
elevation: 8, // Тень
|
||||
color: theme.colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(icon, size: size / 3, color: color),
|
||||
const Gap(16),
|
||||
Text(label, style: labelStyle),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Свойства:**
|
||||
- Квадратная форма (`width = height = size`)
|
||||
- Иконка вверху (1/3 размера кнопки)
|
||||
- Текст внизу
|
||||
- Скругленные углы (8px)
|
||||
- Тень (elevation: 8)
|
||||
- Ripple эффект при нажатии (InkWell)
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Цветовая схема по умолчанию
|
||||
|
||||
**Где определяется primary цвет:**
|
||||
|
||||
```dart
|
||||
// Вероятно в:
|
||||
lib/core/preferences/general_preferences_provider.dart
|
||||
lib/core/theme/
|
||||
```
|
||||
|
||||
**Umbrix использует:**
|
||||
- **Primary color:** Бирюзовый/голубой (#00BCD4 или похожий)
|
||||
- **Surface:** Фон карточек
|
||||
- **OnSurface:** Текст на фоне
|
||||
|
||||
---
|
||||
|
||||
## 💡 Быстрые изменения
|
||||
|
||||
### Изменить цвет всех иконок на красный:
|
||||
|
||||
```dart
|
||||
Icon(
|
||||
icon,
|
||||
size: size / 3,
|
||||
color: Colors.red, // ← ВСЕ ИКОНКИ СТАНУТ КРАСНЫМИ
|
||||
)
|
||||
```
|
||||
|
||||
### Изменить размер иконок на больший:
|
||||
|
||||
```dart
|
||||
Icon(
|
||||
icon,
|
||||
size: size / 2, // ← БЫЛО /3, СТАЛО /2 (больше)
|
||||
color: color,
|
||||
)
|
||||
```
|
||||
|
||||
### Использовать заполненные (filled) иконки:
|
||||
|
||||
```dart
|
||||
// Было:
|
||||
icon: FluentIcons.clipboard_paste_24_regular,
|
||||
|
||||
// Стало:
|
||||
icon: FluentIcons.clipboard_paste_24_filled, // Заполненная версия
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Где ещё используются FluentIcons?
|
||||
|
||||
**Поиск по проекту:**
|
||||
```bash
|
||||
grep -r "FluentIcons\." lib/ | wc -l
|
||||
# Результат: Много! Используются по всему приложению
|
||||
```
|
||||
|
||||
**Примеры файлов:**
|
||||
- `lib/features/home/widget/home_page.dart`
|
||||
- `lib/features/proxy/overview/proxies_overview_page.dart`
|
||||
- `lib/features/common/adaptive_root_scaffold.dart`
|
||||
|
||||
---
|
||||
|
||||
## 📚 Полезные ссылки
|
||||
|
||||
1. **Fluent UI Icons Gallery:**
|
||||
https://aka.ms/fluentui-system-icons
|
||||
|
||||
2. **Flutter Icon класс:**
|
||||
https://api.flutter.dev/flutter/widgets/Icon-class.html
|
||||
|
||||
3. **ColorScheme документация:**
|
||||
https://api.flutter.dev/flutter/material/ColorScheme-class.html
|
||||
|
||||
4. **Пакет fluentui_system_icons:**
|
||||
https://pub.dev/packages/fluentui_system_icons
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Резюме
|
||||
|
||||
**Кнопки находятся:** `lib/features/profile/add/add_profile_modal.dart`
|
||||
|
||||
**Иконки:**
|
||||
- Формат: IconData из пакета `fluentui_system_icons`
|
||||
- НЕ файлы PNG/SVG, а векторные глифы в шрифте
|
||||
- Цвет: `theme.colorScheme.primary` (меняется с темой)
|
||||
- Размер: Масштабируемые, задаются программно
|
||||
|
||||
**Смена цвета:**
|
||||
- Автоматическая через Theme системы Flutter
|
||||
- `colorScheme.primary` берётся из настроек темы
|
||||
- Светлая/тёмная тема → разные оттенки
|
||||
|
||||
**Чтобы изменить:**
|
||||
1. Цвет → поменять `color: theme.colorScheme.primary` на другой
|
||||
2. Размер → изменить `size: buttonWidth / 3`
|
||||
3. Иконку → заменить `FluentIcons.clipboard_paste_24_regular` на другую
|
||||
4. Свою иконку → использовать SVG через flutter_svg пакет
|
||||
Reference in New Issue
Block a user