Приведи пример использования паттернов программирования в Dart/Flutter.

«Приведи пример использования паттернов программирования в Dart/Flutter.» — вопрос из категории Паттерны, который задают на 29% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

1. State Management как реализация Observer (Наблюдатель): Пакет provider или riverpod — это классическая реализация паттерна Наблюдатель. Виджеты (наблюдатели) слушают изменения в объектах-состояниях (наблюдаемых).

// Пример с ChangeNotifier (наблюдаемый объект)
import 'package:flutter/material.dart';

class CounterModel with ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // Уведомляем всех слушателей (виджеты)
  }
}

// Виджет-слушатель (Consumer)
class MyCounterWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // Этот Text слушает изменения в CounterModel
        Consumer<CounterModel>(
          builder: (context, counter, child) => Text('Count: ${counter.count}'),
        ),
        ElevatedButton(
          onPressed: () => context.read<CounterModel>().increment(),
          child: Text('Increment'),
        ),
      ],
    );
  }
}

2. Factory Method для создания платформо-зависимых виджетов:

abstract class DialogButton {
  Widget build(BuildContext context);
}

class MaterialDialogButton implements DialogButton {
  @override
  Widget build(BuildContext context) {
    return TextButton(
      onPressed: () {},
      child: Text('OK'),
    );
  }
}

class CupertinoDialogButton implements DialogButton {
  @override
  Widget build(BuildContext context) {
    return CupertinoButton(
      onPressed: () {},
      child: Text('OK'),
    );
  }
}

// Фабричный метод
DialogButton createDialogButton(TargetPlatform platform) {
  switch (platform) {
    case TargetPlatform.android:
    case TargetPlatform.iOS:
      return CupertinoDialogButton();
    default:
      return MaterialDialogButton();
  }
}

3. Singleton для глобального доступа к сервисам:

class AnalyticsService {
  // Приватный конструктор
  AnalyticsService._privateConstructor();
  // Единственный экземпляр
  static final AnalyticsService _instance = AnalyticsService._privateConstructor();
  // Фабричный конструктор для доступа
  factory AnalyticsService() => _instance;

  void logEvent(String event) {
    // Отправка события в Firebase Analytics и т.д.
    print('Logged: $event');
  }
}

// Использование в любом месте приложения
AnalyticsService().logEvent('screen_opened');