Рубрики
Flutter

Flutter Web в 2025: Готов к продакшену?

Статья Flutter Web в 2025: Готов к продакшену? будет добавлена позже.

Рубрики
Flutter

Clean Architecture во Flutter: Полное руководство на 2025 год

Рассмотрим каждый слой подробно.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

Clean Architecture — это концепция, предложенная Robert C. Martin (Uncle Bob), которая разделяет приложение на слои с чёткими обязанностями. Основная идея — зависимости направлены внутрь, а внешние слои ничего не знают о внутренних.

Ключевые принципы:

  • Независимость от фреймворков — бизнес-логика не зависит от Flutter
  • Тестируемость — каждый слой тестируется отдельно
  • Независимость от UI — бизнес-логика работает без виджетов
  • Независимость от базы данных — можно легко заменить источник данных
  • Внешние зависимости идут внутрь — внутренние слои ничего не знают о внешних

Слои Clean Architecture

Классическая Clean Architecture состоит из трёх основных слоев:

  1. Domain (Доменный слой) — бизнес-логика, сущности, use cases
  2. Data (Данный слой) — репозитории, DTO, источники данных
  3. Presentation (Презентационный слой) — UI, state management

Рассмотрим каждый слой подробно.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

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

Что такое Clean Architecture?

Clean Architecture — это концепция, предложенная Robert C. Martin (Uncle Bob), которая разделяет приложение на слои с чёткими обязанностями. Основная идея — зависимости направлены внутрь, а внешние слои ничего не знают о внутренних.

Ключевые принципы:

  • Независимость от фреймворков — бизнес-логика не зависит от Flutter
  • Тестируемость — каждый слой тестируется отдельно
  • Независимость от UI — бизнес-логика работает без виджетов
  • Независимость от базы данных — можно легко заменить источник данных
  • Внешние зависимости идут внутрь — внутренние слои ничего не знают о внешних

Слои Clean Architecture

Классическая Clean Architecture состоит из трёх основных слоев:

  1. Domain (Доменный слой) — бизнес-логика, сущности, use cases
  2. Data (Данный слой) — репозитории, DTO, источники данных
  3. Presentation (Презентационный слой) — UI, state management

Рассмотрим каждый слой подробно.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

Рассмотрим каждый слой подробно.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

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

Что такое Clean Architecture?

Clean Architecture — это концепция, предложенная Robert C. Martin (Uncle Bob), которая разделяет приложение на слои с чёткими обязанностями. Основная идея — зависимости направлены внутрь, а внешние слои ничего не знают о внутренних.

Ключевые принципы:

  • Независимость от фреймворков — бизнес-логика не зависит от Flutter
  • Тестируемость — каждый слой тестируется отдельно
  • Независимость от UI — бизнес-логика работает без виджетов
  • Независимость от базы данных — можно легко заменить источник данных
  • Внешние зависимости идут внутрь — внутренние слои ничего не знают о внешних

Слои Clean Architecture

Классическая Clean Architecture состоит из трёх основных слоев:

  1. Domain (Доменный слой) — бизнес-логика, сущности, use cases
  2. Data (Данный слой) — репозитории, DTO, источники данных
  3. Presentation (Презентационный слой) — UI, state management

Рассмотрим каждый слой подробно.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

Clean Architecture — это концепция, предложенная Robert C. Martin (Uncle Bob), которая разделяет приложение на слои с чёткими обязанностями. Основная идея — зависимости направлены внутрь, а внешние слои ничего не знают о внутренних.

Ключевые принципы:

  • Независимость от фреймворков — бизнес-логика не зависит от Flutter
  • Тестируемость — каждый слой тестируется отдельно
  • Независимость от UI — бизнес-логика работает без виджетов
  • Независимость от базы данных — можно легко заменить источник данных
  • Внешние зависимости идут внутрь — внутренние слои ничего не знают о внешних

Слои Clean Architecture

Классическая Clean Architecture состоит из трёх основных слоев:

  1. Domain (Доменный слой) — бизнес-логика, сущности, use cases
  2. Data (Данный слой) — репозитории, DTO, источники данных
  3. Presentation (Презентационный слой) — UI, state management

Рассмотрим каждый слой подробно.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

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

Что такое Clean Architecture?

Clean Architecture — это концепция, предложенная Robert C. Martin (Uncle Bob), которая разделяет приложение на слои с чёткими обязанностями. Основная идея — зависимости направлены внутрь, а внешние слои ничего не знают о внутренних.

Ключевые принципы:

  • Независимость от фреймворков — бизнес-логика не зависит от Flutter
  • Тестируемость — каждый слой тестируется отдельно
  • Независимость от UI — бизнес-логика работает без виджетов
  • Независимость от базы данных — можно легко заменить источник данных
  • Внешние зависимости идут внутрь — внутренние слои ничего не знают о внешних

Слои Clean Architecture

Классическая Clean Architecture состоит из трёх основных слоев:

  1. Domain (Доменный слой) — бизнес-логика, сущности, use cases
  2. Data (Данный слой) — репозитории, DTO, источники данных
  3. Presentation (Презентационный слой) — UI, state management

Рассмотрим каждый слой подробно.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

Рассмотрим каждый слой подробно.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

Clean Architecture — это концепция, предложенная Robert C. Martin (Uncle Bob), которая разделяет приложение на слои с чёткими обязанностями. Основная идея — зависимости направлены внутрь, а внешние слои ничего не знают о внутренних.

Ключевые принципы:

  • Независимость от фреймворков — бизнес-логика не зависит от Flutter
  • Тестируемость — каждый слой тестируется отдельно
  • Независимость от UI — бизнес-логика работает без виджетов
  • Независимость от базы данных — можно легко заменить источник данных
  • Внешние зависимости идут внутрь — внутренние слои ничего не знают о внешних

Слои Clean Architecture

Классическая Clean Architecture состоит из трёх основных слоев:

  1. Domain (Доменный слой) — бизнес-логика, сущности, use cases
  2. Data (Данный слой) — репозитории, DTO, источники данных
  3. Presentation (Презентационный слой) — UI, state management

Рассмотрим каждый слой подробно.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

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

Что такое Clean Architecture?

Clean Architecture — это концепция, предложенная Robert C. Martin (Uncle Bob), которая разделяет приложение на слои с чёткими обязанностями. Основная идея — зависимости направлены внутрь, а внешние слои ничего не знают о внутренних.

Ключевые принципы:

  • Независимость от фреймворков — бизнес-логика не зависит от Flutter
  • Тестируемость — каждый слой тестируется отдельно
  • Независимость от UI — бизнес-логика работает без виджетов
  • Независимость от базы данных — можно легко заменить источник данных
  • Внешние зависимости идут внутрь — внутренние слои ничего не знают о внешних

Слои Clean Architecture

Классическая Clean Architecture состоит из трёх основных слоев:

  1. Domain (Доменный слой) — бизнес-логика, сущности, use cases
  2. Data (Данный слой) — репозитории, DTO, источники данных
  3. Presentation (Презентационный слой) — UI, state management

Рассмотрим каждый слой подробно.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

Рассмотрим каждый слой подробно.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

Clean Architecture — это концепция, предложенная Robert C. Martin (Uncle Bob), которая разделяет приложение на слои с чёткими обязанностями. Основная идея — зависимости направлены внутрь, а внешние слои ничего не знают о внутренних.

Ключевые принципы:

  • Независимость от фреймворков — бизнес-логика не зависит от Flutter
  • Тестируемость — каждый слой тестируется отдельно
  • Независимость от UI — бизнес-логика работает без виджетов
  • Независимость от базы данных — можно легко заменить источник данных
  • Внешние зависимости идут внутрь — внутренние слои ничего не знают о внешних

Слои Clean Architecture

Классическая Clean Architecture состоит из трёх основных слоев:

  1. Domain (Доменный слой) — бизнес-логика, сущности, use cases
  2. Data (Данный слой) — репозитории, DTO, источники данных
  3. Presentation (Презентационный слой) — UI, state management

Рассмотрим каждый слой подробно.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

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

Что такое Clean Architecture?

Clean Architecture — это концепция, предложенная Robert C. Martin (Uncle Bob), которая разделяет приложение на слои с чёткими обязанностями. Основная идея — зависимости направлены внутрь, а внешние слои ничего не знают о внутренних.

Ключевые принципы:

  • Независимость от фреймворков — бизнес-логика не зависит от Flutter
  • Тестируемость — каждый слой тестируется отдельно
  • Независимость от UI — бизнес-логика работает без виджетов
  • Независимость от базы данных — можно легко заменить источник данных
  • Внешние зависимости идут внутрь — внутренние слои ничего не знают о внешних

Слои Clean Architecture

Классическая Clean Architecture состоит из трёх основных слоев:

  1. Domain (Доменный слой) — бизнес-логика, сущности, use cases
  2. Data (Данный слой) — репозитории, DTO, источники данных
  3. Presentation (Презентационный слой) — UI, state management

Рассмотрим каждый слой подробно.

Заключение

Clean Architecture во Flutter — это инвестиция в будущее вашего приложения. Да, требуется больше кода и времени на разработку, но код становится легко тестируемым и масштабируемым.

Рубрики
Flutter

Dart 3.5: Новые возможности языка в 2025 году

Dart 3.5 принес значительные улучшения в язык, которые делают разработку на Flutter более продуктивной и код более безопасным. В этой статье разберём ключевые нововведения.

Что нового в Dart 3.5?

  • Pattern matching — мощная система сопоставления с образцом
  • Records — легковесные неизменяемые данные
  • Class modifiers — новый контроль над классами
  • Улучшенные generics
  • Enhanced switch expressions
  • И многое другое

Pattern Matching

Pattern matching — это способ деструктуризации и проверки значений:

// Простой pattern
switch (value) {
  case int x when x > 0:
    print('Positive: $x');
  case int x:
    print('Non-positive: $x');
  case String s:
    print('String: $s');
  default:
    print('Other');
}

// Pattern в переменных
final (name, age) = getUser();
print('$name is $age years old');

// Pattern в циклах
final map = {'a': 1, 'b': 2};
for (final (key, value) in map.entries) {
  print('$key: $value');
}

Records

Records — это неизменяемые данные-классы:

// Объявление record
record User(String name, int age) {
  String get description => '$name is $age years old';
}

// Использование
final user = User('Alice', 30);
print(user.name);      // Alice
print(user.age);       // 30
print(user.description); // Alice is 30 years old

// Деструктуризация
final (name, age) = user;

Заключение

Dart 3.5 делает язык более современным и выразительным. Начните использовать новые возможности в своих проектах уже сегодня!

Рубрики
Flutter

CI/CD для Flutter с Codemagic: Полное руководство

CI/CD (Continuous Integration/Continuous Delivery) автоматизирует процесс сборки, тестирования и публикации Flutter приложений. Codemagic — это специализированный CI/CD сервис для Flutter. В этом руководстве разберём полную настройку.

Что такое Codemagic?

Codemagic — это cloud-based CI/CD платформа, созданная специально для Flutter и React Native.

Преимущества: — Специализируется на Flutter — Простая настройка за 5 минут — Бесплатный план для Open Source — Test distribution — Code signing из коробки — Fastlane integration

Начало работы

Регистрация

  1. Зарегистрируйтесь на codemagic.io
  2. Подключите GitHub/GitLab/Bitbucket репозиторий
  3. Выберите Flutter проект

Первая сборка

Codemagic автоматически определит Flutter проект и предложит конфигурацию:

workflows:
  sample-workflow:
    name: Codemagic Sample
    triggering:
      events:
        - push
      branch_patterns:
        - pattern: main
          include: false
    environment:
      vars:
        XCODE_WORKSPACE: codemagicFlutter.xcworkspace
        XCODE_SCHEME: codemagicFlutter
      flutter: stable
    scripts:
      - name: Build Android app
        script: flutter build apk --release
    artifacts:
      - build/app/outputs/flutter-apk/*.apk

Настройка тестов

Unit тесты

scripts:
  - name: Run Flutter tests
    script: flutter test

Integration тесты

scripts:
  - name: Run integration tests
    script: flutter test integration_test --no-sound-null-safety

Code coverage

scripts:
  - name: Generate coverage
    script: flutter test --coverage
  - name: Upload coverage
    script: bash <(curl -s https://codecov.io/bash)

Code Signing

Android

scripts:
  - name: Set up key properties
    script: |
      echo $CM_KEYSTORE_PROPERTIES | base64 --decode > keystore.properties
  - name: Build release APK
    script: |
      flutter build apk --release \
        --keystore $CM_KEYSTORE \
        --store-password $CM_KEYSTORE_PASSWORD \
        --key-password $CM_KEY_PASSWORD \
        --key-alias $CM_KEY_ALIAS

iOS

scripts:
  - name: Set up code signing
    script: |
      keychain initialize
      keychain set-defaults
    - name: Fetch signing certificates
    script: |
      app-store-connect fetch-signing-files \
        $CM_BUNDLE_ID \
        --platform IOS \
        --type IOS_APP_DEVELOPMENT \
        --certificate-key $CM_CERTIFICATE_PRIVATE_KEY \
        --certificate-issuer $CM_CERTIFICATE_ISSUER
    - name: Build IPA
    script: flutter build ipa --release --export-options-plist exportOptions.plist

Публикация в сторы

Google Play

scripts:
  - name: Upload to Google Play
    script: |
      cd android
      fastlane supply \
        --apk build/app/outputs/flutter-apk/app-release.apk \
        --track internal \
        --json_key_data $CM_SERVICE_ACCOUNT_JSON

App Store

scripts:
  - name: Upload to TestFlight
    script: |
      app-store-connect publish \
        --path build/ios/ipa/*.ipa \
        --skip_screenshots \
        --skip_waiting_for_build_processing

Заключение

Codemagic — отличный выбор для CI/CD Flutter приложений. Начните с простого workflow и усложняйте по мере необходимости.

Рубрики
Flutter

Riverpod 2.0: Полное руководство для начинающих

Riverpod 2.0 — это major update популярной библиотеки управления состоянием для Flutter. В этом руководстве разберём основные концепции, провайдеры и кодогенерацию с примерами.

Что такое Riverpod?

Riverpod (Provider наоборот) — это современная система управления состоянием от Remi Rousselet, автора пакета provider. Это следующая эволюция, которая решает многие ограничения оригинального Provider.

Ключевые преимущества: — Compile-time безопасность — ошибки обнаруживаются при компиляции — Не требует BuildContext — доступ к состоянию из любого места — Auto-dispose — автоматическое освобождение ресурсов — Code generation — типобезопасные провайдеры — Тестирование — простое мокирование зависимостей

Установка

Добавьте зависимости в pubspec.yaml:

dependencies:
  flutter_riverpod: ^2.5.0

dev_dependencies:
  riverpod_generator: ^2.4.0
  riverpod_lint: ^2.3.0
  build_runner: ^2.4.0

Основные концепции

Provider

Provider — это источник данных. Он может быть: — Значением (число, строка, объект) — Функцией, которая вычисляет значение — Async-функцией для загрузки данных из API — Stream для real-time данных

// Простой провайдер значения
final counterProvider = Provider<int>((ref) => 42);

// Провайдер с вычислением
final doubleProvider = Provider<int>((ref) {
  final value = ref.watch(counterProvider);
  return value * 2;
});

StateProvider

StateProvider — для простого изменяемого состояния:

final countProvider = StateProvider<int>((ref) => 0);

// В виджете
class MyWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(countProvider);

    return ElevatedButton(
      onPressed: () => ref.read(countProvider.notifier).state++,
      child: Text('Count: $count'),
    );
  }
}

StateNotifierProvider

Для более сложной логики используйте StateNotifier:

class Counter extends StateNotifier<int> {
  Counter() : super(0);

  void increment() => state++;
  void decrement() => state--;
  void reset() => state = 0;
}

final counterNotifierProvider = StateNotifierProvider<Counter, int>((ref) {
  return Counter();
});

AsyncNotifierProvider

Для работы с asynchronous данными (API, базы данных):

@riverpod
class UserRepository extends _$UserRepository {
  @override
  Future<User> build(String userId) async {
    final response = await http.get('/api/users/$userId');
    return User.fromJson(jsonDecode(response.body));
  }
}

// Использование
class UserWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userAsync = ref.watch(userRepositoryProvider('123'));

    return userAsync.when(
      data: (user) => Text(user.name),
      loading: () => CircularProgressIndicator(),
      error: (err, stack) => Text('Error: $err'),
    );
  }
}

Code Generation в Riverpod 2.0

Riverpod 2.0 поддерживает кодогенерацию для большей типобезопасности:

// Объявление провайдера с аннотацией
@riverpod
String greeting(GreetingRef ref) {
  return 'Hello, World!';
}

// Параметризованный провайдер
@riverpod
Future<User> fetchUser(FetchUserRef ref, int id) async {
  final response = await http.get('/api/users/$id');
  return User.fromJson(jsonDecode(response.body));
}

// Class-based провайдер с состоянием
@riverpod
class Counter extends _$Counter {
  @override
  int build() => 0;

  void increment() => state++;
  void decrement() => state--;
}

После добавления аннотации запустите генератор:

flutter pub run build_runner build

Чтение и обновление состояния

ref.watch — подписаться на изменения (в build методе):

final value = ref.watch(myProvider);

ref.read — получить значение без подписки (в callback):

onPressed: () {
  final value = ref.read(myProvider);
  print(value);
  // Или для StateProvider:
  ref.read(myProvider.notifier).state++;
}

ref.listen — слушать изменения и реагировать:

ref.listen<int>(countProvider, (previous, next) {
  print('Count changed from $previous to $next');
  if (next > 10) {
    showNotification('Limit reached!');
  }
});

ConsumerWidget и ConsumerStatefulWidget

Для использования провайдеров в виджетах:

// Stateless виджет с провайдерами
class MyScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(countProvider);
    return Text('Count: $count');
  }
}

// Stateful виджет
class MyScreen extends ConsumerStatefulWidget {
  @override
  ConsumerState<MyScreen> createState() => _MyScreenState();
}

class _MyScreenState extends ConsumerState<MyScreen> {
  @override
  Widget build(BuildContext context) {
    final count = ref.watch(countProvider);
    return ElevatedButton(
      onPressed: () => ref.read(countProvider.notifier).state++,
      child: Text('Count: $count'),
    );
  }
}

ProviderScope и ProviderContainer

Оберните приложение в ProviderScope:

void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}

Для тестов используйте ProviderContainer:

test('counter increments', () {
  final container = ProviderContainer();

  // Чтение значения
  expect(container.read(countProvider), 0);

  // Изменение значения
  container.read(countProvider.notifier).state++;

  expect(container.read(countProvider), 1);

  // Не забудьте удалить контейнер
  container.dispose();
});

Модификаторы провайдеров

.family

Параметризованные провайдеры:

final userProvider = FutureProvider.family<User, int>((ref, id) async {
  final response = await http.get('/api/users/$id');
  return User.fromJson(jsonDecode(response.body));
});

// Использование
final userAsync = ref.watch(userProvider(123));

.autoDispose

Автоматическое освобождение ресурсов когда никто не слушает:

final socketProvider = StreamProvider.autoDispose<WebSocket>((ref) {
  final socket = WebSocket.connect('ws://example.com');

  ref.onDispose(() => socket.close());

  return socket;
});

Полный пример приложения

Создадим простое ToDo приложение:

// Модель задачи
class Todo {
  final String id;
  final String title;
  final bool completed;

  Todo({required this.id, required this.title, this.completed = false});

  Todo copyWith({String? title, bool? completed}) {
    return Todo(
      id: id,
      title: title ?? this.title,
      completed: completed ?? this.completed,
    );
  }
}

// Провайдер списка задач
@riverpod
class TodoList extends _$TodoList {
  @override
  List<Todo> build() {
    return [];
  }

  void add(String title) {
    state = [...state, Todo(id: DateTime.now().toString(), title: title)];
  }

  void toggle(String id) {
    state = [
      for (final todo in state)
        if (todo.id == id) todo.copyWith(completed: !todo.completed) else todo
    ];
  }

  void remove(String id) {
    state = state.where((todo) => todo.id != id).toList();
  }
}

// UI
class TodoApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final todos = ref.watch(todoListProvider);

    return Scaffold(
      appBar: AppBar(title: Text('Todo Riverpod')),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          final todo = todos[index];
          return ListTile(
            leading: Checkbox(
              value: todo.completed,
              onChanged: (_) => ref.read(todoListProvider.notifier).toggle(todo.id),
            ),
            title: Text(todo.title),
            trailing: IconButton(
              icon: Icon(Icons.delete),
              onPressed: () => ref.read(todoListProvider.notifier).remove(todo.id),
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _showAddDialog(context, ref),
        child: Icon(Icons.add),
      ),
    );
  }
}

Тестирование

Riverpod упрощает тестирование:

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  test('adds todo', () {
    final container = ProviderContainer();

    // Добавляем задачу
    container.read(todoListProvider.notifier).add('Test task');

    // Проверяем
    expect(container.read(todoListProvider).length, 1);
    expect(container.read(todoListProvider).first.title, 'Test task');

    container.dispose();
  });

  testWidgets('toggles todo', (tester) async {
    await tester.pumpWidget(
      ProviderScope(
        child: MaterialApp(home: TodoApp()),
      ),
    );

    // Добавляем задачу через UI
    await tester.tap(find.byType(FloatingActionButton));
    await tester.enterText(find.byType(TextField), 'New task');
    await tester.tap(find.text('Add'));
    await tester.pump();

    // Переключаем
    await tester.tap(find.byType(Checkbox));
    await tester.pump();

    // Проверяем
    expect(find.text('New task'), findsOneWidget);
  });
}

Лучшие практики

  1. Используйте code generation для большей типобезопасности
  2. Разделяйте провайдеры по файлам по domain
  3. Применяйте autoDispose для ресурсов которые нужно освобождать
  4. Тестируйте провайдеры изолированно от UI
  5. Используйте family для параметризованных данных
  6. Избегайте watch в методах кроме build (используйте read)

Заключение

Riverpod 2.0 — это мощная и типобезопасная система управления состоянием для Flutter. Кодогенерация делает код чище, а compile-time проверки помогают избегать runtime ошибок.

Начните с простого: — Для простых счетчиков используйте StateProvider — Для сложной логики — StateNotifier или @riverpod class — Для API запросов — FutureProvider или AsyncNotifierProvider — Для streams — StreamProvider

Документация: https://riverpod.dev