← Назад к вопросам

Как следить за изменниями компонента в MVVM?

1.0 Junior🔥 11 комментариев
#Архитектура Flutter

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как следить за изменениями компонента в MVVM?

MVVM (Model-View-ViewModel) в Flutter строится на принципе реактивности, где View (UI) автоматически обновляется при изменении данных в ViewModel. Существует несколько подходов для отслеживания изменений.

1. StreamBuilder (Stream + ViewModel)

Один из самых распространённых подходов — использование Stream для отслеживания изменений:

class CounterViewModel {
  int _count = 0;
  final _countController = StreamController<int>();
  
  Stream<int> get countStream => _countController.stream;
  
  void increment() {
    _count++;
    _countController.sink.add(_count);
  }
  
  void dispose() {
    _countController.close();
  }
}

class CounterView extends StatefulWidget {
  @override
  State<CounterView> createState() => _CounterViewState();
}

class _CounterViewState extends State<CounterView> {
  late CounterViewModel viewModel = CounterViewModel();
  
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<int>(
      stream: viewModel.countStream,
      initialData: 0,
      builder: (context, snapshot) {
        return Column(
          children: [
            Text("Счёт: ${snapshot.data}"),
            ElevatedButton(
              onPressed: viewModel.increment,
              child: Text("Увеличить"),
            ),
          ],
        );
      },
    );
  }
  
  @override
  void dispose() {
    viewModel.dispose();
    super.dispose();
  }
}

2. ValueNotifier + ValueListenableBuilder

Для простых случаев можно использовать ValueNotifier:

class UserViewModel {
  final userName = ValueNotifier<String>("Иван");
  final userAge = ValueNotifier<int>(25);
  
  void updateName(String newName) {
    userName.value = newName;
  }
  
  void updateAge(int newAge) {
    userAge.value = newAge;
  }
  
  void dispose() {
    userName.dispose();
    userAge.dispose();
  }
}

class UserView extends StatefulWidget {
  @override
  State<UserView> createState() => _UserViewState();
}

class _UserViewState extends State<UserView> {
  late UserViewModel viewModel = UserViewModel();
  
  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<String>(
      valueListenable: viewModel.userName,
      builder: (context, name, _) {
        return ValueListenableBuilder<int>(
          valueListenable: viewModel.userAge,
          builder: (context, age, _) {
            return Column(
              children: [
                Text("Имя: $name"),
                Text("Возраст: $age"),
              ],
            );
          },
        );
      },
    );
  }
  
  @override
  void dispose() {
    viewModel.dispose();
    super.dispose();
  }
}

3. BLoC паттерн (современный стандарт)

BLoC (Business Logic Component) — это паттерн, специально разработанный для управления состоянием:

class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}

class CounterState {
  final int count;
  CounterState({required this.count});
}

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterState(count: 0)) {
    on<IncrementEvent>((event, emit) {
      emit(CounterState(count: state.count + 1));
    });
    
    on<DecrementEvent>((event, emit) {
      emit(CounterState(count: state.count - 1));
    });
  }
}

class CounterView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CounterBloc, CounterState>(
      builder: (context, state) {
        return Column(
          children: [
            Text("Счёт: ${state.count}"),
            ElevatedButton(
              onPressed: () => context.read<CounterBloc>().add(IncrementEvent()),
              child: Text("Увеличить"),
            ),
          ],
        );
      },
    );
  }
}

4. Provider (фреймворк для управления состоянием)

Provider — это популярная библиотека для MVVM в Flutter:

class CounterViewModel extends ChangeNotifier {
  int _count = 0;
  
  int get count => _count;
  
  void increment() {
    _count++;
    notifyListeners();  // Уведомляет об изменении
  }
}

class CounterView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<CounterViewModel>(
      builder: (context, viewModel, _) {
        return Column(
          children: [
            Text("Счёт: ${viewModel.count}"),
            ElevatedButton(
              onPressed: viewModel.increment,
              child: Text("Увеличить"),
            ),
          ],
        );
      },
    );
  }
}

5. Getx (простой и мощный)

Getx упрощает реактивность:

class CounterViewModel extends GetxController {
  var count = 0.obs;  // Observable переменная
  
  void increment() {
    count++;
  }
}

class CounterView extends StatelessWidget {
  final viewModel = Get.put(CounterViewModel());
  
  @override
  Widget build(BuildContext context) {
    return Obx(() => Column(
      children: [
        Text("Счёт: ${viewModel.count}"),
        ElevatedButton(
          onPressed: viewModel.increment,
          child: Text("Увеличить"),
        ),
      ],
    ));
  }
}

Сравнение подходов

ПодходСложностьПроизводительностьПодходит для
StreamBuilderСредняяХорошаяПростые приложения
ValueNotifierНизкаяОтличнаяОдного значения
BLoCВысокаяОтличнаяБольших приложений
ProviderСредняяХорошаяСреднего размера приложений
GetXНизкаяХорошаяБыстрой разработки

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

  • Всегда вызывайте dispose() для очистки ресурсов
  • Используйте const конструкторы для Widget, чтобы избежать лишних перестроек
  • Разделяйте логику и представление — ViewModel содержит логику, View содержит только UI
  • Тестируйте ViewModel отдельно от UI

Выбор подхода зависит от сложности приложения: от простого StreamBuilder для учебных проектов до BLoC для production-приложений.

Как следить за изменниями компонента в MVVM? | PrepBro