mirror of
https://github.com/smmarty/friflex_flutter_starter.git
synced 2025-12-21 17:10:45 +00:00
fix(linter): улучшение правил анализа и линтинга (#37)
* fix(linter): улучшение правил анализа и линтинга, добавление исключений и ошибок * fix(tasks): исправления по ревью --------- Co-authored-by: petrovyuri <petrovyuri@example.com>
This commit is contained in:
16
.vscode/tasks.json
vendored
16
.vscode/tasks.json
vendored
@@ -1,24 +1,12 @@
|
|||||||
{
|
{
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
|
||||||
"label": "fluttergen",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "/Users/yura/.pub-cache/bin/fluttergen",
|
|
||||||
"group": {
|
|
||||||
"kind": "build",
|
|
||||||
"isDefault": true
|
|
||||||
},
|
|
||||||
"presentation": {
|
|
||||||
"reveal": "always"
|
|
||||||
},
|
|
||||||
"problemMatcher": []
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "build_runner --delete-conflicting-outputs",
|
"label": "build_runner --delete-conflicting-outputs",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "dart",
|
"command": "flutter",
|
||||||
"args": [
|
"args": [
|
||||||
|
"pub",
|
||||||
"run",
|
"run",
|
||||||
"build_runner",
|
"build_runner",
|
||||||
"build",
|
"build",
|
||||||
|
|||||||
@@ -1,90 +1,135 @@
|
|||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
# Включает правила из:
|
# Персонализированные настройки анализатора и линтера.
|
||||||
# - package:lints/core.yaml: основные правила критических проблем
|
# Базовый набор flutter_lints + дополнительные ужесточения.
|
||||||
# - package:lints/recommended.yaml: рекомендуемые правила для чистого кода
|
|
||||||
# - package:flutter_lints/flutter.yaml: специфичные правила для Flutter
|
|
||||||
|
|
||||||
analyzer:
|
analyzer:
|
||||||
exclude:
|
exclude:
|
||||||
- "android/**"
|
- "build/**" # Исключаем артефакты сборки (генерируемый код, кэш, ресурсы)
|
||||||
- "assets/**"
|
- "android/**" # Платформенный код Android (Java/Kotlin, Gradle) не анализируем Dart линтером
|
||||||
- "build/**"
|
- "ios/**" # Платформенный код iOS (Swift/ObjC) вне области Dart анализа
|
||||||
- "config/**"
|
- "assets/**" # Статические ассеты (шрифты, изображения, json и др.)
|
||||||
- "core/**"
|
- "**/*.g.dart" # Сгенерированные файлы build_runner / json_serializable
|
||||||
- "res/**"
|
- "**/*.freezed.dart" # Сгенерированные модели пакетом freezed
|
||||||
- "ios/**"
|
- "**/*.gen.dart" # Общий паттерн для доп. генераторов
|
||||||
- "**/*.g.dart"
|
- "**/*.gr.dart" # Генерируемые маршруты (auto_route / другие)
|
||||||
- "**/*.config.dart"
|
- "**/*.config.dart" # Сгенерированные конфигурационные файлы
|
||||||
- "**/*.gen.dart"
|
- "**/generated/**" # Папки с автогенерируемыми исходниками
|
||||||
- "**/*.freezed.dart"
|
- "**/*.lock" # Временные/lock файлы генераторов (если создаются)
|
||||||
- "**/generated/*"
|
- "**/app_services/aurora/**" # Внутренний сервисный код (предположительно внешняя интеграция) исключён
|
||||||
- "**/*.gr.dart"
|
|
||||||
- "**/*.yaml"
|
|
||||||
- "app_services/aurora/**"
|
|
||||||
- "/app_services/aurora/**"
|
|
||||||
- "**/app_services/aurora/**"
|
|
||||||
- "**/*.lock.dart"
|
|
||||||
errors:
|
errors:
|
||||||
# Переопределения уровней ошибок (error/warning/info)
|
avoid_dynamic_calls: error # Запрет неявных dynamic вызовов (типобезопасность)
|
||||||
avoid_dynamic_calls: error # Запрещает использование dynamic для вызовов методов
|
avoid_returning_null_for_future: error # Future не должен завершаться null без явной модели
|
||||||
avoid_returning_null_for_future: error # Запрещает возврат null вместо Future
|
avoid_slow_async_io: warning # Подсказка о потенциально медленных IO операциях
|
||||||
avoid_slow_async_io: warning # Предупреждает о медленных асинхронных операциях ввода/вывода
|
avoid_type_to_string: warning # Предупреждение против использования Type.toString для логики
|
||||||
avoid_type_to_string: warning # Предупреждает о неправильном использовании toString() для типов
|
cancel_subscriptions: error # Требование отменять StreamSubscription для предотвращения утечек
|
||||||
cancel_subscriptions: error # Требует отмены подписок, предотвращает утечки памяти
|
close_sinks: error # Обязательное закрытие Sink (ресурсное управление)
|
||||||
close_sinks: error # Требует закрытия sink-ов, предотвращает утечки ресурсов
|
comment_references: warning # Проверка корректности ссылок в документационных комментариях
|
||||||
comment_references: warning # Проверяет корректность ссылок в комментариях
|
always_declare_return_types: error # Явный тип возвращаемого значения повышает читаемость
|
||||||
always_declare_return_types: error # Требует явного указания возвращаемых типов методов
|
avoid_bool_literals_in_conditional_expressions: warning # Избегать выражений вида condition ? true : false
|
||||||
always_require_non_null_named_parameters: warning # Требует использования @required для ненулевых параметров
|
avoid_return_types_on_setters: warning # Сеттеры не должны объявлять тип возврата
|
||||||
avoid_bool_literals_in_conditional_expressions: warning # Запрещает избыточные булевы литералы в условных выражениях
|
avoid_returning_null: warning # Предпочтительнее nullable типы / Option объекты вместо raw null
|
||||||
avoid_return_types_on_setters: warning # Запрещает возвращаемые типы для сеттеров
|
avoid_setters_without_getters: error # Сеттер без геттера может скрывать состояние
|
||||||
avoid_returning_null: error # Запрещает возврат null
|
avoid_void_async: error # async void нежелателен (трудно ловить ошибки)
|
||||||
avoid_setters_without_getters: error # Требует создания геттера при наличии сеттера
|
constant_identifier_names: error # Единый стиль именования констант (UPPER_CASE)
|
||||||
avoid_void_async: error # Запрещает использование void для асинхронных функций
|
unnecessary_new: warning # Снижение шума: new не нужен в современном Dart
|
||||||
constant_identifier_names: error # Проверяет правильность именования констант
|
use_decorated_box: warning # Оптимизация: DecoratedBox вместо контейнера без лишних виджетов
|
||||||
unnecessary_new: warning # Запрещает избыточное использование ключевого слова new
|
use_colored_box: warning # Оптимизация: ColoredBox для простого цвета фона
|
||||||
use_decorated_box: warning # Рекомендует использовать DecoratedBox вместо Container
|
|
||||||
use_colored_box: warning # Рекомендует использовать ColoredBox вместо Container с цветом
|
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
rules:
|
rules:
|
||||||
# Нестандартные правила или правила с измененными значениями
|
# === Именование и стиль ===
|
||||||
always_put_required_named_parameters_first: true # Требовать размещать обязательные именованные параметры первыми
|
- always_put_required_named_parameters_first # Обязательные именованные параметры первыми повышают читаемость
|
||||||
avoid_catches_without_on_clauses: true # Избегать catch без указания типа исключения
|
- always_use_package_imports # Единый стиль импортов через package: упрощает refactoring
|
||||||
avoid_catching_errors: true # Избегать перехвата ошибок типа Error
|
- curly_braces_in_flow_control_structures # Обязательные фигурные скобки в if/for/while для безопасности
|
||||||
avoid_equals_and_hash_code_on_mutable_classes: true # Избегать equals и hashCode в изменяемых классах
|
- directives_ordering # Упорядочивание импортов (dart → package → relative)
|
||||||
avoid_escaping_inner_quotes: true # Избегать экранирования внутренних кавычек
|
- eol_at_end_of_file # Пустая строка в конце файла (POSIX standard)
|
||||||
avoid_field_initializers_in_const_classes: true # Избегать инициализаторов полей в константных классах
|
- prefer_single_quotes # Единый стиль кавычек (одинарные быстрее печатать)
|
||||||
avoid_implementing_value_types: true # Избегать реализации интерфейсов значимых типов
|
- slash_for_doc_comments # Использовать /// вместо /** */ для документации
|
||||||
avoid_multiple_declarations_per_line: false # Разрешать несколько объявлений в одной строке
|
- sort_constructors_first # Конструкторы в начале класса — быстрый обзор API
|
||||||
avoid_positional_boolean_parameters: true # Избегать позиционных булевых параметров
|
|
||||||
avoid_private_typedef_functions: true # Избегать приватных typedef-функций
|
# === Обработка ошибок и типобезопасность ===
|
||||||
avoid_redundant_argument_values: true # Избегать избыточных значений аргументов
|
- avoid_catching_errors # Не перехватывать Error (только Exception) — ошибки системные
|
||||||
avoid_returning_this: true # Избегать возврата this
|
- await_only_futures # await только для Future (ловит ошибки типизации)
|
||||||
cascade_invocations: true # Использовать каскадные вызовы
|
- control_flow_in_finally # Запрет return/break/continue в finally блоках
|
||||||
deprecated_consistency: true # Поддерживать согласованность устаревших элементов
|
- empty_catches # Предупреждение о пустых catch (скрытые баги)
|
||||||
do_not_use_environment: false # Разрешить использование Environment
|
- hash_and_equals # Требует переопределять hashCode и == вместе
|
||||||
leading_newlines_in_multiline_strings: true # Начинать многострочные строки с новой строки
|
- only_throw_errors # Бросать Error/Exception типы, а не произвольные объекты
|
||||||
no_runtimeType_toString: true # Не использовать runtimeType.toString()
|
- test_types_in_equals # Проверка типа в equals для безопасности
|
||||||
one_member_abstracts: false # Разрешать абстрактные классы с одним методом
|
- unrelated_type_equality_checks # Запрет сравнения несовместимых типов
|
||||||
only_throw_errors: true # Выбрасывать только объекты Error
|
- use_build_context_synchronously # КРИТИЧНО: запрет BuildContext после async gap
|
||||||
parameter_assignments: true # Запрещать присваивание значений параметрам
|
- use_rethrow_when_possible # Использовать rethrow вместо throw e (сохраняет stacktrace)
|
||||||
prefer_asserts_with_message: true # Использовать сообщения с assert
|
- discarded_futures # Выявляет неожиданные Future без await
|
||||||
prefer_constructors_over_static_methods: true # Предпочитать конструкторы статическим методам
|
- unawaited_futures # Явно помечать намеренно не ожидаемые Future
|
||||||
prefer_final_in_for_each: true # Использовать final в for-each циклах
|
|
||||||
prefer_final_locals: true # Использовать final для локальных переменных
|
# === Иммутабельность и const ===
|
||||||
public_member_api_docs: false # Не требовать документацию для всех публичных членов
|
- avoid_equals_and_hash_code_on_mutable_classes # hashCode/== на изменяемых классах приводит к багам в коллекциях
|
||||||
require_trailing_commas: true # Требовать запятые в конце для улучшения форматирования
|
- prefer_const_constructors # Константные конструкторы для производительности
|
||||||
sort_constructors_first: true # Требовать размещать конструкторы первыми
|
- prefer_const_constructors_in_immutables # Обязательные const в неизменяемых классах
|
||||||
sort_pub_dependencies: false # Не требовать сортировки зависимостей в pubspec
|
- prefer_const_literals_to_create_immutables # Константные литералы коллекций
|
||||||
sort_unnamed_constructors_first: false # Не требовать размещать безымянные конструкторы первыми
|
- prefer_final_fields # final для полей где возможно
|
||||||
use_is_even_rather_than_modulo: true # Использовать isEven вместо % 2 == 0
|
- prefer_final_in_for_each # final в forEach предотвращает случайные изменения
|
||||||
use_late_for_private_fields_and_variables: false # Не требовать late для приватных полей
|
- prefer_final_locals # final локальные переменные — стремимся к иммутабельности
|
||||||
use_setters_to_change_properties: true # Использовать сеттеры для изменения свойств
|
- parameter_assignments # Не переназначать параметры — вводит путаницу
|
||||||
use_string_buffers: true # Использовать StringBuffer для сложной конкатенации
|
|
||||||
use_to_and_as_if_applicable: true # Использовать методы to и as при применимости
|
# === Flutter оптимизации ===
|
||||||
no_literal_bool_comparisons: true # Запрещать сравнения с литералами true/false
|
- avoid_unnecessary_containers # Убирает лишние Container виджеты
|
||||||
use_key_in_widget_constructors: true # Обязательное указание ключа для stateful/stateless widgets
|
- sized_box_for_whitespace # SizedBox вместо Container для отступов
|
||||||
always_use_package_imports: true # Всегда использовать package: импорты
|
- sized_box_shrink_expand # Специализированные конструкторы SizedBox.shrink/expand
|
||||||
unawaited_futures: true # Требовать использование unawaited для неожидаемых Future
|
- sort_child_properties_last # child/children последними в виджетах
|
||||||
comment_references: true # Использовать только видимые параметры в документации
|
- use_colored_box # ColoredBox для простого цвета фона
|
||||||
|
- use_decorated_box # DecoratedBox вместо контейнера без лишних виджетов
|
||||||
|
- use_key_in_widget_constructors # Ключ позволяет корректно применить дифф к дереву виджетов
|
||||||
|
|
||||||
|
# === Современный Dart (2.17+, 3.x) ===
|
||||||
|
- use_super_parameters # super параметры для краткости
|
||||||
|
- combinators_ordering # Сортировка show/hide в импортах
|
||||||
|
- implicit_call_tearoffs # Разрешить неявные tearoffs
|
||||||
|
- matching_super_parameters # Автоматическое соответствие параметрам супер-конструктора
|
||||||
|
- use_enums # Предпочитать enums вместо статических констант
|
||||||
|
|
||||||
|
# === Читаемость кода ===
|
||||||
|
- avoid_escaping_inner_quotes # Предпочтительно менять тип кавычек вместо экранирования
|
||||||
|
- avoid_field_initializers_in_const_classes # Инициализация в константных классах тяжеловесна/избыточна
|
||||||
|
- avoid_implementing_value_types # Не реализовывать value-type интерфейсы вручную (риск несовпадений)
|
||||||
|
- avoid_positional_boolean_parameters # Булевые позиционные параметры плохо читаются (использовать именованные)
|
||||||
|
- avoid_private_typedef_functions # Приватные typedef затрудняют повторное использование / читаемость
|
||||||
|
- avoid_redundant_argument_values # Удалять аргументы совпадающие с значениями по умолчанию
|
||||||
|
- avoid_returning_this # Возврат this усложняет fluent API и может скрывать ошибки
|
||||||
|
- cascade_invocations # Использовать каскады для последовательности операций над объектом
|
||||||
|
- deprecated_consistency # Единый стиль пометок @deprecated
|
||||||
|
- leading_newlines_in_multiline_strings # Многострочные строки начинают с новой строки — чище diff
|
||||||
|
- no_literal_bool_comparisons # Исключить сравнения вида flag == true
|
||||||
|
- no_runtimeType_toString # runtimeType.toString нестабилен для логики (только отладка)
|
||||||
|
- prefer_asserts_with_message # Сообщение в assert облегчает диагностику
|
||||||
|
- prefer_constructors_over_static_methods # Конструкторы лучше выражают создание экземпляра
|
||||||
|
- comment_references # Корректные ссылки в комментариях (небитые, видимые)
|
||||||
|
|
||||||
|
# === Сахар и идиомы ===
|
||||||
|
- avoid_init_to_null # Не писать = null явно (по умолчанию)
|
||||||
|
- prefer_if_null_operators # ?? оператор вместо тернарника с null
|
||||||
|
- prefer_interpolation_to_compose_strings # Интерполяция вместо конкатенации
|
||||||
|
- prefer_is_empty # .isEmpty вместо .length == 0
|
||||||
|
- prefer_is_not_empty # .isNotEmpty вместо .length > 0
|
||||||
|
- use_is_even_rather_than_modulo # isEven более явно и может быть эффективнее
|
||||||
|
- use_setters_to_change_properties # Изменение полей через сеттеры (инкапсуляция)
|
||||||
|
- use_string_buffers # StringBuffer эффективнее при конкатенации в циклах
|
||||||
|
- use_to_and_as_if_applicable # Использовать to*/as* если семантика преобразования доступна
|
||||||
|
|
||||||
|
# === Удаление избыточности ===
|
||||||
|
- avoid_return_types_on_setters # Сеттеры не должны объявлять тип возврата
|
||||||
|
- unnecessary_const # Убирает лишние const
|
||||||
|
- unnecessary_new # Убирает лишние new
|
||||||
|
- unnecessary_this # Убирает лишние this
|
||||||
|
- unnecessary_parenthesis # Лишние скобки
|
||||||
|
|
||||||
|
# === Продакшн / отладка ===
|
||||||
|
- avoid_print # Запрет print в продакшене (использовать logger)
|
||||||
|
|
||||||
|
bloc:
|
||||||
|
rules:
|
||||||
|
- avoid_flutter_imports # Логика блока должна быть платформенно независимой
|
||||||
|
- avoid_public_bloc_methods # Публичные методы могут нарушать чистоту паттерна (использовать события)
|
||||||
|
- avoid_public_fields # Публичные поля обходят инкапсуляцию; использовать состояние/события
|
||||||
|
- prefer_file_naming_conventions # Единообразие именования файлов блоков повышает навигацию
|
||||||
|
|
||||||
@@ -9,8 +9,8 @@ import 'package:friflex_starter/di/di_container.dart';
|
|||||||
import 'package:friflex_starter/features/error/error_screen.dart';
|
import 'package:friflex_starter/features/error/error_screen.dart';
|
||||||
import 'package:friflex_starter/features/splash/splash_screen.dart';
|
import 'package:friflex_starter/features/splash/splash_screen.dart';
|
||||||
import 'package:friflex_starter/features/update/domain/state/cubit/update_cubit.dart';
|
import 'package:friflex_starter/features/update/domain/state/cubit/update_cubit.dart';
|
||||||
import 'package:friflex_starter/features/update/update_type.dart';
|
|
||||||
import 'package:friflex_starter/features/update/update_routes.dart';
|
import 'package:friflex_starter/features/update/update_routes.dart';
|
||||||
|
import 'package:friflex_starter/features/update/update_type.dart';
|
||||||
import 'package:friflex_starter/l10n/gen/app_localizations.dart';
|
import 'package:friflex_starter/l10n/gen/app_localizations.dart';
|
||||||
import 'package:friflex_starter/l10n/localization_notifier.dart';
|
import 'package:friflex_starter/l10n/localization_notifier.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@@ -136,7 +136,7 @@ class _App extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
return ThemeConsumer(
|
return ThemeConsumer(
|
||||||
builder: () => MediaQuery(
|
builder: () => MediaQuery(
|
||||||
key: ValueKey('prevent_rebuild'),
|
key: const ValueKey('prevent_rebuild'),
|
||||||
data: MediaQuery.of(
|
data: MediaQuery.of(
|
||||||
context,
|
context,
|
||||||
).copyWith(textScaler: TextScaler.noScaling, boldText: false),
|
).copyWith(textScaler: TextScaler.noScaling, boldText: false),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:friflex_starter/di/di_container.dart';
|
import 'package:friflex_starter/di/di_container.dart';
|
||||||
@@ -22,12 +24,19 @@ final class DependsProviders extends StatelessWidget {
|
|||||||
// Сюда добавляем глобальные блоки, inherited и т.д.
|
// Сюда добавляем глобальные блоки, inherited и т.д.
|
||||||
Provider.value(value: diContainer), // Передаем контейнер зависимостей
|
Provider.value(value: diContainer), // Передаем контейнер зависимостей
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (_) => UpdateCubit(diContainer.repositories.updatesRepository)
|
create: (_) {
|
||||||
..checkForUpdates(
|
final updateCubit = UpdateCubit(
|
||||||
|
diContainer.repositories.updatesRepository,
|
||||||
|
);
|
||||||
|
unawaited(
|
||||||
|
updateCubit.checkForUpdates(
|
||||||
versionCode:
|
versionCode:
|
||||||
'1.0.0', // TODO(yura): заменить на получение из diContainer
|
'1.0.0', // TODO(yura): заменить на получение из diContainer
|
||||||
platform: 'android',
|
platform: 'android',
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
return updateCubit;
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: child,
|
child: child,
|
||||||
|
|||||||
@@ -46,20 +46,20 @@ class AppColors extends ThemeExtension<AppColors> with _$AppColorsTailorMixin {
|
|||||||
final Color infoSnackbarBackground;
|
final Color infoSnackbarBackground;
|
||||||
|
|
||||||
/// Цвета светлой темы
|
/// Цвета светлой темы
|
||||||
static final AppColors light = AppColors(
|
static const AppColors light = AppColors(
|
||||||
testColor: Colors.red,
|
testColor: Colors.red,
|
||||||
errorSnackbarBackground: const Color(0xFFD24720),
|
errorSnackbarBackground: Color(0xFFD24720),
|
||||||
successSnackbarBackground: const Color(0xFF6FB62C),
|
successSnackbarBackground: Color(0xFF6FB62C),
|
||||||
infoSnackbarBackground: const Color.fromARGB(255, 220, 108, 77),
|
infoSnackbarBackground: Color.fromARGB(255, 220, 108, 77),
|
||||||
itemTextColor: const Color(0xFFFAF3EB),
|
itemTextColor: Color(0xFFFAF3EB),
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Цвета тёмной темы
|
/// Цвета тёмной темы
|
||||||
static final AppColors dark = AppColors(
|
static const AppColors dark = AppColors(
|
||||||
testColor: Colors.green,
|
testColor: Colors.green,
|
||||||
errorSnackbarBackground: const Color(0xFF638B8B),
|
errorSnackbarBackground: Color(0xFF638B8B),
|
||||||
successSnackbarBackground: const Color(0xFF93C499),
|
successSnackbarBackground: Color(0xFF93C499),
|
||||||
infoSnackbarBackground: const Color.fromARGB(255, 35, 147, 178),
|
infoSnackbarBackground: Color.fromARGB(255, 35, 147, 178),
|
||||||
itemTextColor: Colors.white,
|
itemTextColor: Colors.white,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ class _AppSnackBarState extends State<AppSnackBar>
|
|||||||
CurvedAnimation(parent: _animationController, curve: Curves.easeOut),
|
CurvedAnimation(parent: _animationController, curve: Curves.easeOut),
|
||||||
);
|
);
|
||||||
|
|
||||||
_animationController.forward();
|
unawaited(_animationController.forward());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Запуск таймера для автоматического закрытия снекбара
|
/// Запуск таймера для автоматического закрытия снекбара
|
||||||
@@ -198,11 +198,13 @@ class _AppSnackBarState extends State<AppSnackBar>
|
|||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
_dismissTimer?.cancel();
|
_dismissTimer?.cancel();
|
||||||
|
unawaited(
|
||||||
_animationController.reverse().then((_) {
|
_animationController.reverse().then((_) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
widget.onDismiss?.call();
|
widget.onDismiss?.call();
|
||||||
}
|
}
|
||||||
});
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -243,7 +245,7 @@ class _AppSnackBarState extends State<AppSnackBar>
|
|||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.message,
|
widget.message,
|
||||||
style: TextStyle(color: Colors.white),
|
style: const TextStyle(color: Colors.white),
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -288,13 +290,21 @@ class _Icon extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return switch (type) {
|
return switch (type) {
|
||||||
TypeSnackBar.success => Icon(
|
TypeSnackBar.success => const Icon(
|
||||||
Icons.check_circle,
|
Icons.check_circle,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
size: 32,
|
size: 32,
|
||||||
),
|
),
|
||||||
TypeSnackBar.error => Icon(Icons.error, color: Colors.white, size: 32),
|
TypeSnackBar.error => const Icon(
|
||||||
TypeSnackBar.info => Icon(Icons.info, color: Colors.white, size: 32),
|
Icons.error,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
TypeSnackBar.info => const Icon(
|
||||||
|
Icons.info,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:friflex_starter/app/ui_kit/app_box.dart';
|
import 'package:friflex_starter/app/ui_kit/app_box.dart';
|
||||||
import 'package:friflex_starter/app/ui_kit/app_snackbar.dart';
|
import 'package:friflex_starter/app/ui_kit/app_snackbar.dart';
|
||||||
@@ -78,6 +80,7 @@ class _ComponentsScreenState extends State<ComponentsScreen> {
|
|||||||
final updateCubitState = context.read<UpdateCubit>().state;
|
final updateCubitState = context.read<UpdateCubit>().state;
|
||||||
if (updateCubitState is UpdateSuccessState &&
|
if (updateCubitState is UpdateSuccessState &&
|
||||||
updateCubitState.updateInfo.updateType == UpdateType.soft) {
|
updateCubitState.updateInfo.updateType == UpdateType.soft) {
|
||||||
|
unawaited(
|
||||||
SoftUpdateModal.show(
|
SoftUpdateModal.show(
|
||||||
context,
|
context,
|
||||||
updateEntity: updateCubitState.updateInfo,
|
updateEntity: updateCubitState.updateInfo,
|
||||||
@@ -87,6 +90,7 @@ class _ComponentsScreenState extends State<ComponentsScreen> {
|
|||||||
message: 'Начато обновление приложения',
|
message: 'Начато обновление приложения',
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -95,7 +99,7 @@ class _ComponentsScreenState extends State<ComponentsScreen> {
|
|||||||
const HBox(16),
|
const HBox(16),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pushNamed(UpdateRoutes.hardUpdateScreenName);
|
unawaited(context.pushNamed(UpdateRoutes.hardUpdateScreenName));
|
||||||
},
|
},
|
||||||
child: const Text('Переход на экран Hard Update обновления'),
|
child: const Text('Переход на экран Hard Update обновления'),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:friflex_starter/app/ui_kit/app_box.dart';
|
|
||||||
import 'package:friflex_starter/app/app_context_ext.dart';
|
import 'package:friflex_starter/app/app_context_ext.dart';
|
||||||
|
import 'package:friflex_starter/app/ui_kit/app_box.dart';
|
||||||
import 'package:friflex_starter/features/debug/debug_routes.dart';
|
import 'package:friflex_starter/features/debug/debug_routes.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
@@ -35,42 +37,42 @@ class DebugScreen extends StatelessWidget {
|
|||||||
const HBox(16),
|
const HBox(16),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pushNamed(DebugRoutes.iconsScreenName);
|
unawaited(context.pushNamed(DebugRoutes.iconsScreenName));
|
||||||
},
|
},
|
||||||
child: const Text('Экран с иконками'),
|
child: const Text('Экран с иконками'),
|
||||||
),
|
),
|
||||||
const HBox(16),
|
const HBox(16),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pushNamed(DebugRoutes.themeScreenName);
|
unawaited(context.pushNamed(DebugRoutes.themeScreenName));
|
||||||
},
|
},
|
||||||
child: const Text('Экран настроек темы'),
|
child: const Text('Экран настроек темы'),
|
||||||
),
|
),
|
||||||
const HBox(16),
|
const HBox(16),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pushNamed(DebugRoutes.tokensScreenName);
|
unawaited(context.pushNamed(DebugRoutes.tokensScreenName));
|
||||||
},
|
},
|
||||||
child: const Text('Экран с токенами'),
|
child: const Text('Экран с токенами'),
|
||||||
),
|
),
|
||||||
const HBox(16),
|
const HBox(16),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pushNamed(DebugRoutes.uiKitScreenName);
|
unawaited(context.pushNamed(DebugRoutes.uiKitScreenName));
|
||||||
},
|
},
|
||||||
child: const Text('Экран UI Kit'),
|
child: const Text('Экран UI Kit'),
|
||||||
),
|
),
|
||||||
const HBox(16),
|
const HBox(16),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pushNamed(DebugRoutes.langScreenName);
|
unawaited(context.pushNamed(DebugRoutes.langScreenName));
|
||||||
},
|
},
|
||||||
child: const Text('Экран локализации'),
|
child: const Text('Экран локализации'),
|
||||||
),
|
),
|
||||||
const HBox(16),
|
const HBox(16),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
context.pushNamed(DebugRoutes.componentsScreenName);
|
await context.pushNamed(DebugRoutes.componentsScreenName);
|
||||||
},
|
},
|
||||||
child: const Text('Экран компонентов'),
|
child: const Text('Экран компонентов'),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:friflex_starter/app/ui_kit/app_box.dart';
|
import 'package:friflex_starter/app/ui_kit/app_box.dart';
|
||||||
import 'package:friflex_starter/features/main/presentation/main_routes.dart';
|
import 'package:friflex_starter/features/main/presentation/main_routes.dart';
|
||||||
@@ -23,7 +25,7 @@ class MainScreen extends StatelessWidget {
|
|||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Переход на экран с деталями
|
// Переход на экран с деталями
|
||||||
context.pushNamed(MainRoutes.mainDetailScreenName);
|
unawaited(context.pushNamed(MainRoutes.mainDetailScreenName));
|
||||||
},
|
},
|
||||||
child: const Text('Переход на экран с деталями'),
|
child: const Text('Переход на экран с деталями'),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:friflex_starter/app/app_context_ext.dart';
|
import 'package:friflex_starter/app/app_context_ext.dart';
|
||||||
@@ -46,12 +48,14 @@ class _RootScreenState extends State<RootScreen> {
|
|||||||
// Проверяем только состояние успеха с доступной информацией об обновлении
|
// Проверяем только состояние успеха с доступной информацией об обновлении
|
||||||
if (updateState is UpdateSuccessState &&
|
if (updateState is UpdateSuccessState &&
|
||||||
updateState.updateInfo.updateType == UpdateType.soft) {
|
updateState.updateInfo.updateType == UpdateType.soft) {
|
||||||
|
unawaited(
|
||||||
SoftUpdateModal.show(
|
SoftUpdateModal.show(
|
||||||
context,
|
context,
|
||||||
updateEntity: updateState.updateInfo,
|
updateEntity: updateState.updateInfo,
|
||||||
onUpdate: () {
|
onUpdate: () {
|
||||||
// TODO(yura): реализовать логику обновления приложения
|
// TODO(yura): реализовать логику обновления приложения
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -64,7 +68,7 @@ class _RootScreenState extends State<RootScreen> {
|
|||||||
? FloatingActionButton(
|
? FloatingActionButton(
|
||||||
child: const Icon(Icons.bug_report),
|
child: const Icon(Icons.bug_report),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pushNamed(DebugRoutes.debugScreenName);
|
unawaited(context.pushNamed(DebugRoutes.debugScreenName));
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ part 'update_state.dart';
|
|||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
class UpdateCubit extends Cubit<UpdateState> {
|
class UpdateCubit extends Cubit<UpdateState> {
|
||||||
/// {@macro UpdateCubit}
|
/// {@macro UpdateCubit}
|
||||||
UpdateCubit(this._updatesRepository) : super(UpdateInitialState());
|
UpdateCubit(this._updatesRepository) : super(const UpdateInitialState());
|
||||||
|
|
||||||
/// Репозиторий для проверки обновлений
|
/// Репозиторий для проверки обновлений
|
||||||
final IUpdateRepository _updatesRepository;
|
final IUpdateRepository _updatesRepository;
|
||||||
@@ -23,7 +23,7 @@ class UpdateCubit extends Cubit<UpdateState> {
|
|||||||
required String platform,
|
required String platform,
|
||||||
}) async {
|
}) async {
|
||||||
if (state is UpdateLoadingState) return;
|
if (state is UpdateLoadingState) return;
|
||||||
emit(UpdateLoadingState());
|
emit(const UpdateLoadingState());
|
||||||
try {
|
try {
|
||||||
final updateInfo = await _updatesRepository.checkForUpdates(
|
final updateInfo = await _updatesRepository.checkForUpdates(
|
||||||
versionCode: versionCode,
|
versionCode: versionCode,
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ void _initErrorHandlers(IDebugService debugService) {
|
|||||||
|
|
||||||
/// Метод для показа экрана ошибки
|
/// Метод для показа экрана ошибки
|
||||||
void _showErrorScreen(Object error, StackTrace? stackTrace) {
|
void _showErrorScreen(Object error, StackTrace? stackTrace) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
AppRouter.rootNavigatorKey.currentState?.push(
|
await AppRouter.rootNavigatorKey.currentState?.push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => ErrorScreen(error: error, stackTrace: stackTrace),
|
builder: (_) => ErrorScreen(error: error, stackTrace: stackTrace),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -695,10 +695,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.0"
|
version: "1.17.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1084,10 +1084,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
|
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.6"
|
version: "0.7.7"
|
||||||
theme_tailor:
|
theme_tailor:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -631,12 +631,12 @@ void main() {
|
|||||||
|
|
||||||
// Создаем приложение с кастомными отступами
|
// Создаем приложение с кастомными отступами
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
const MaterialApp(
|
||||||
home: MediaQuery(
|
home: MediaQuery(
|
||||||
data: const MediaQueryData(
|
data: MediaQueryData(
|
||||||
padding: EdgeInsets.only(top: 50), // Симулируем статус бар
|
padding: EdgeInsets.only(top: 50), // Симулируем статус бар
|
||||||
),
|
),
|
||||||
child: const Scaffold(body: Center(child: Text('Test'))),
|
child: Scaffold(body: Center(child: Text('Test'))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user