Workstation Logo
Продукты
AI LabsАгенты OpenAIАгенты ClaudeGrok BotWorkstation CRM (WSL CRM)МаркетингВсе продукты
Решения ИИ
Рабочие станции ИИAI SME PackagesЧастный ИИКластеры GPUПограничный ИИЛаборатория корпоративного ИИИИ по отраслям
Услуги
Platform ModernisationDigital EngineeringData Foundations & AIAutonomous OperationsИИ-консалтингАвтоматизация DevOpsКибербезопасностьРазработка ПОСоздание агентовНастройка MLOps
О нас
ПартнёрыИстории клиентов
Статьи
Документация
WSL ProxyRing PromoterWSL VaultJobshoutSysOps 24/7
Блог
Связаться с намиLogin
Workstation

AI-рабочие станции, мультиагентное AI-ПО, GPU-инфраструктура и решения на базе интеллектуальных агентов для современного бизнеса.

Связаться с нами

AI-решения

Рабочие станции ИИAI SME PackagesЧастный ИИКластеры GPUПограничный ИИЛаборатория корпоративного ИИИИ по отраслям

Продукты

Все продуктыWSL CRM и ERPМаркетингАгенты OpenAIWSL ProxyRing PromoterWSL VaultJobshoutSysOps 24/7

Компания

О насПочему WorkstationПартнёрыИстории клиентовЦеныКонтакты

Ресурсы

СтатьиДокументацияБлогПоискКарта сайта
Офис в Великобритании
77-79 Marlowes, Hemel Hempstead HP1 1LFКак добраться: съезд 20 с трассы M25, Внешний ЛондонРег. номер компании: 11641870Пн - Пт: 9:00 - 18:00 GMT
+44 7515 356 146
Офис в Бельгии
Workstation SRL, Rue Vanderkindere 34, 1180 Uccle, BrusselsBE 0751.518.683Пн - Пт: 9:00 - 18:00 CET
+32 492 45 67 46
Офис в Индии
#159 Sector 9, Pocket 1, DDA Flats, 110077 Dwarka, New Delhi
+91 98881 98841

© 2026 Workstation AI. Все права защищены.

КонфиденциальностьФайлы cookieУсловия использованияКарта сайта

Loading blog...

Home / Blog
MysqlNode.jsBackend

Освоение Flutter: создание готовых к использованию мобильных приложений

Полное руководство по созданию надежных, масштабируемых мобильных приложений с помощью Flutter и Dart.

Balinder Walia27 мая 2025 г.8 min read

Создать прототип Flutter несложно. Создание готового к использованию приложения Flutter, которое масштабируется, хорошо работает под нагрузкой и может обслуживаться в течение многих лет, требует гораздо более глубокого понимания архитектуры, управления состоянием, рабочих процессов тестирования и развертывания. Это руководство устраняет разрыв между учебными проектами и реальными приложениями, охватывая шаблоны и методы, которыми ежедневно пользуются профессиональные команды Flutter.

Чистая архитектура для Flutter

Чистая архитектура разделяет ваше приложение на отдельные уровни с четкими границами и правилами зависимостей. Такое разделение делает ваш код тестируемым, поддерживаемым и независимым от внешних платформ и инструментов.

Flutter Уровни архитектурыВаш дартс-кодБизнес-логика и amp; Состав пользовательского интерфейсаFlutter FrameworkВиджетыРендерингОснова (жесты, анимация)Engine (C++)Skia Graphics • Время выполнения Дарта • Каналы платформыiOS (UIKit)ARM / Apple SiliconAndroid (NDK)ARM / x86

Структура уровней

Рабочее приложение Flutter обычно имеет трехуровневую архитектуру:

  • Уровень представления— виджеты, страницы и управление состоянием. Этот уровень зависит от уровня предметной области, но никогда непосредственно от источников данных.
  • Уровень домена— бизнес-логика, сущности и варианты использования. Этот уровень не зависит от Flutter или каких-либо внешних пакетов. Он определяет интерфейсы репозитория (абстрактные классы), которые реализует уровень данных.
  • Уровень данных— реализации репозитория, клиенты API, доступ к локальной базе данных и модели данных (DTO). Этот уровень реализует интерфейсы, определенные на уровне домена.
lib/
  core/
    error/
      exceptions.dart
      failures.dart
    network/
      network_info.dart
    usecases/
      usecase.dart
  features/
    authentication/
      data/
        datasources/
          auth_remote_datasource.dart
          auth_local_datasource.dart
        models/
          user_model.dart
        repositories/
          auth_repository_impl.dart
      domain/
        entities/
          user.dart
        repositories/
          auth_repository.dart
        usecases/
          login.dart
          register.dart
          logout.dart
      presentation/
        bloc/
          auth_bloc.dart
          auth_event.dart
          auth_state.dart
        pages/
          login_page.dart
          register_page.dart
        widgets/
          login_form.dart

Правило зависимости строгое: внутренние уровни никогда не знают о внешних слоях. Уровень предметной области определяет абстрактные интерфейсы репозитория, а уровень данных обеспечивает конкретные реализации. Такая инверсия управления позволяет менять источники данных, не затрагивая бизнес-логику.

Управление состоянием: BLoC и Riverpod

Выбор правильного решения по управлению состоянием — одно из наиболее важных архитектурных решений в проекте Flutter.

Шаблон BLoC

BLoC (компонент бизнес-логики) использует потоки для управления состоянием. События приходят, состояния уходят. Этот однонаправленный поток данных делает изменения состояния предсказуемыми и упрощает отладку.

// Events
abstract class AuthEvent {}

class LoginRequested extends AuthEvent {
  final String email;
  final String password;
  LoginRequested({required this.email, required this.password});
}

class LogoutRequested extends AuthEvent {}

// States
abstract class AuthState {}

class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class AuthAuthenticated extends AuthState {
  final User user;
  AuthAuthenticated(this.user);
}
class AuthError extends AuthState {
  final String message;
  AuthError(this.message);
}

// BLoC
class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final LoginUseCase loginUseCase;
  final LogoutUseCase logoutUseCase;

  AuthBloc({
    required this.loginUseCase,
    required this.logoutUseCase,
  }) : super(AuthInitial()) {
    on<LoginRequested>(_onLoginRequested);
    on<LogoutRequested>(_onLogoutRequested);
  }

  Future<void> _onLoginRequested(
    LoginRequested event,
    Emitter<AuthState> emit,
  ) async {
    emit(AuthLoading());
    final result = await loginUseCase(
      LoginParams(email: event.email, password: event.password),
    );
    result.fold(
      (failure) => emit(AuthError(failure.message)),
      (user) => emit(AuthAuthenticated(user)),
    );
  }

  Future<void> _onLogoutRequested(
    LogoutRequested event,
    Emitter<AuthState> emit,
  ) async {
    await logoutUseCase();
    emit(AuthInitial());
  }
}

Riverpod

Riverpod предлагает более гибкий, безопасный для компиляции подход к управлению состоянием. В отличие от Provider, Riverpod не зависит от дерева виджетов, что упрощает тестирование и создание.

// Define providers
final authRepositoryProvider = Provider<AuthRepository>((ref) {
  return AuthRepositoryImpl(
    remoteDatasource: ref.read(authRemoteDatasourceProvider),
    localDatasource: ref.read(authLocalDatasourceProvider),
  );
});

final authStateProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
  return AuthNotifier(ref.read(authRepositoryProvider));
});

class AuthNotifier extends StateNotifier<AuthState> {
  final AuthRepository _repository;

  AuthNotifier(this._repository) : super(const AuthState.initial());

  Future<void> login(String email, String password) async {
    state = const AuthState.loading();
    final result = await _repository.login(email, password);
    state = result.fold(
      (failure) => AuthState.error(failure.message),
      (user) => AuthState.authenticated(user),
    );
  }
}

// Use in widgets
class LoginPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final authState = ref.watch(authStateProvider);
    return authState.when(
      initial: () => LoginForm(),
      loading: () => const CircularProgressIndicator(),
      authenticated: (user) => HomePage(user: user),
      error: (message) => ErrorDisplay(message: message),
    );
  }
}

Внедрение зависимостей

Правильное внедрение зависимостей важно для тестируемого кода. Пакетget_itпредоставляет простой локатор сервисов, который хорошо работает с чистой архитектурой.

Flutter Дерево виджетовMaterialAppКаркасСтолбецПанель приложенийListViewFABListTile 1ListTile 2
final sl = GetIt.instance;

void initDependencies() {
  // External
  sl.registerLazySingleton(() => Dio()..interceptors.add(AuthInterceptor()));
  sl.registerLazySingleton(() => InternetConnectionChecker());

  // Data sources
  sl.registerLazySingleton<AuthRemoteDatasource>(
    () => AuthRemoteDatasourceImpl(dio: sl()),
  );
  sl.registerLazySingleton<AuthLocalDatasource>(
    () => AuthLocalDatasourceImpl(secureStorage: sl()),
  );

  // Repositories
  sl.registerLazySingleton<AuthRepository>(
    () => AuthRepositoryImpl(
      remoteDatasource: sl(),
      localDatasource: sl(),
      networkInfo: sl(),
    ),
  );

  // Use cases
  sl.registerLazySingleton(() => LoginUseCase(sl()));
  sl.registerLazySingleton(() => RegisterUseCase(sl()));

  // BLoCs
  sl.registerFactory(() => AuthBloc(
    loginUseCase: sl(),
    logoutUseCase: sl(),
  ));
}

API Интеграция с Dio

Dio — самая популярная версия Клиент HTTP для Dart, предлагающий перехватчики, глобальную настройку и поддержку FormData. Структурируйте свой уровень API с помощью типобезопасной обработки запросов и ответов.

class ApiClient {
  final Dio _dio;

  ApiClient(this._dio) {
    _dio.options = BaseOptions(
      baseUrl: Environment.apiBaseUrl,
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 15),
      headers: {'Content-Type': 'application/json'},
    );

    _dio.interceptors.addAll([
      AuthInterceptor(),
      LogInterceptor(requestBody: true, responseBody: true),
      RetryInterceptor(dio: _dio, retries: 3),
    ]);
  }

  Future<T> get<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    required T Function(dynamic data) parser,
  }) async {
    try {
      final response = await _dio.get(path, queryParameters: queryParameters);
      return parser(response.data);
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  AppException _handleError(DioException error) {
    switch (error.type) {
      case DioExceptionType.connectionTimeout:
      case DioExceptionType.receiveTimeout:
        return NetworkException('Connection timed out');
      case DioExceptionType.badResponse:
        return ServerException(
          error.response?.statusCode ?? 500,
          error.response?.data?['message'] ?? 'Unknown error',
        );
      default:
        return NetworkException('Network error occurred');
    }
  }
}

Локальное хранилище с Hive и Sqflite

Большинству производственных приложений требуется локальное сохранение данных. Выберите правильный инструмент в зависимости от сложности ваших данных.

Hive для хранения ключей и объектов

Hive — это легкая и невероятно быстрая база данных NoSQL, написанная на чистом Dart. Он идеально подходит для кэширования, пользовательских настроек и хранения небольших и средних наборов данных.

@HiveType(typeId: 0)
class CachedArticle extends HiveObject {
  @HiveField(0)
  final String id;

  @HiveField(1)
  final String title;

  @HiveField(2)
  final String content;

  @HiveField(3)
  final DateTime cachedAt;

  CachedArticle({
    required this.id,
    required this.title,
    required this.content,
    required this.cachedAt,
  });
}

class ArticleCacheService {
  static const _boxName = 'articles_cache';

  Future<void> cacheArticles(List<Article> articles) async {
    final box = await Hive.openBox<CachedArticle>(_boxName);
    final cached = articles.map((a) => CachedArticle(
      id: a.id,
      title: a.title,
      content: a.content,
      cachedAt: DateTime.now(),
    ));
    await box.clear();
    await box.addAll(cached);
  }

  Future<List<CachedArticle>> getCachedArticles() async {
    final box = await Hive.openBox<CachedArticle>(_boxName);
    return box.values.toList();
  }
}

Sqflite для реляционных данных

Если ваши данные имеют сложные взаимосвязи и вам нужны запросы SQL, Sqflite предоставляет полную реализацию SQLite для Flutter. Используйте его для структурированных данных, которые выигрывают от объединений, индексов и транзакций.

Push-уведомления

Внедряйте push-уведомления с помощью Firebase Cloud Messaging (FCM) с правильной обработкой разрешений и фоновой обработкой сообщений.

class NotificationService {
  final FirebaseMessaging _messaging = FirebaseMessaging.instance;

  Future<void> initialize() async {
    // Request permission
    final settings = await _messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );

    if (settings.authorizationStatus == AuthorizationStatus.authorized) {
      // Get FCM token
      final token = await _messaging.getToken();
      await _sendTokenToServer(token);

      // Listen for token refresh
      _messaging.onTokenRefresh.listen(_sendTokenToServer);

      // Handle foreground messages
      FirebaseMessaging.onMessage.listen(_handleForegroundMessage);

      // Handle background/terminated message taps
      FirebaseMessaging.onMessageOpenedApp.listen(_handleMessageTap);
    }
  }

  void _handleForegroundMessage(RemoteMessage message) {
    // Show local notification using flutter_local_notifications
    FlutterLocalNotificationsPlugin().show(
      message.hashCode,
      message.notification?.title,
      message.notification?.body,
      const NotificationDetails(
        android: AndroidNotificationDetails(
          'default_channel',
          'Default',
          importance: Importance.high,
        ),
      ),
    );
  }
}

Глубокие ссылки

Глубокие ссылки позволяют пользователям напрямую переходить к определенному контенту в вашем приложении с внешних URL-адресов. Flutter поддерживает как глубокие ссылки на основе URI, так и динамические ссылки.

// Configure in MaterialApp
MaterialApp(
  onGenerateRoute: (settings) {
    final uri = Uri.parse(settings.name ?? '');

    if (uri.pathSegments.first == 'product') {
      final productId = uri.pathSegments[1];
      return MaterialPageRoute(
        builder: (_) => ProductDetailPage(id: productId),
      );
    }

    if (uri.pathSegments.first == 'order') {
      final orderId = uri.pathSegments[1];
      return MaterialPageRoute(
        builder: (_) => OrderTrackingPage(id: orderId),
      );
    }

    return MaterialPageRoute(builder: (_) => const HomePage());
  },
)

Для более надежной глубокой связи используйте пакетgo_router, который обеспечивает декларативную маршрутизацию с поддержкой глубоких ссылок, перенаправлением и вложенной навигацией.

CI/CD с Codemagic и Fastlane

Автоматизированные конвейеры сборки и развертывания необходимы для рабочих приложений. Codemagic предоставляет собственный сервис CI/CD, основанный на Flutter, а Fastlane предлагает более настраиваемую автоматизацию.

Конфигурация Codemagic

# codemagic.yaml
workflows:
  production-release:
    name: Production Release
    max_build_duration: 60
    environment:
      flutter: stable
      vars:
        APP_STORE_CONNECT_KEY_ID: Encrypted(...)
        GOOGLE_PLAY_SERVICE_ACCOUNT: Encrypted(...)
    scripts:
      - name: Install dependencies
        script: flutter pub get
      - name: Run tests
        script: flutter test --coverage
      - name: Build Android
        script: flutter build appbundle --release
      - name: Build iOS
        script: |
          flutter build ipa --release \
            --export-options-plist=/path/to/ExportOptions.plist
    artifacts:
      - build/**/outputs/**/*.aab
      - build/ios/ipa/*.ipa
    publishing:
      google_play:
        credentials: $GOOGLE_PLAY_SERVICE_ACCOUNT
        track: internal
      app_store_connect:
        api_key: $APP_STORE_CONNECT_KEY_ID

Интеграция Fastlane

Fastlane обеспечивает детальный контроль над процессом сборки и отправки. Определите полосы для разных этапов выпуска:

# fastlane/Fastfile
platform :ios do
  desc "Deploy to TestFlight"
  lane :beta do
    build_flutter_app(target: "lib/main.dart")
    upload_to_testflight(
      skip_waiting_for_build_processing: true
    )
  end

  desc "Deploy to App Store"
  lane :release do
    build_flutter_app(target: "lib/main.dart")
    upload_to_app_store(
      submit_for_review: true,
      automatic_release: false
    )
  end
end

Профилирование производительности

Производственные приложения требуют стабильной производительности. Flutter DevTools предоставляет комплексные возможности профилирования.

  • Отслеживание перестройки виджетов— используйте наложение «Производительность» и инструменты разработчика для выявления виджетов, которые перестраиваются чрезмерно. Примените конструкторыconstи выборочное управление состоянием, чтобы свести к минимуму перестроения.
  • Рендеринг кадров— отслеживайте представление временной шкалы, чтобы убедиться, что рендеринг кадров осуществляется в течение 16 мс (60 кадров в секунду) или 8 мс (120 кадров в секунду). Ищите дорогостоящие этапы сборки, планировки и покраски.
  • Профилирование памяти— отслеживание распределения памяти для обнаружения утечек. Распространенными причинами являются неотмененные подписки на потоки, неудаленные контроллеры и сохраненные ссылки в замыканиях.
  • Производительность при запуске— отложите тяжелую инициализацию с помощьюWidgetsBinding.instance.addPostFrameCallback. Используйте отложенную загрузку с импортомdeferred asдля функций, которые не нужны немедленно.
// Profile-mode build for accurate performance measurement
// flutter run --profile

// Add performance overlay in debug builds
MaterialApp(
  showPerformanceOverlay: true,
  // ...
)

Заключение

Для создания готовых к использованию приложений Flutter требуется нечто большее, чем просто знание каталога виджетов. Это требует продуманной архитектуры, надежного управления состоянием, комплексного тестирования и автоматизированных конвейеров развертывания. Приняв чистую архитектуру, инвестируя в правильное внедрение зависимостей, реализуя тщательную обработку ошибок и устанавливая рабочие процессы CI/CD, вы создаете приложения, которые не только функциональны, но и легко обслуживаются и масштабируются в долгосрочной перспективе.

Начните с ранней разработки архитектуры, напишите тесты с первого дня и автоматизируйте конвейер развертывания перед первым выпуском. Эти первоначальные инвестиции со временем увеличиваются, позволяя вашей команде быстрее выпускать функции с меньшим количеством регрессий и с большей уверенностью в каждом выпуске.