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

Что такое BlocProvider и BlocBuilder?

2.0 Middle🔥 222 комментариев
#State Management#Архитектура Flutter

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

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

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

BlocProvider и BlocBuilder

BLoC (Business Logic Component) — это паттерн управления состоянием, основанный на Streams. Это альтернатива Provider и GetX.

Основная концепция BLoC

Визуально:
┌─────────────────────────┐
│      BLoC              │
│  (Business Logic)      │
│                         │
│  Input: Sink (Events)  │ ← Пользователь действует
│  Output: Stream (State)│ → UI обновляется
└─────────────────────────┘
      ↑              ↓
   Widget Input → BlocBuilder

Структура BLoC

// 1. События (что может произойти)
class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}

// 2. Состояния (текущее состояние UI)
class CounterState {
  final int count;
  CounterState({required this.count});
}

// 3. BLoC (обработка событий и генерация состояний)
import 'package:flutter_bloc/flutter_bloc.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterState(count: 0)) {
    // Регистрируем обработчики событий
    on<IncrementEvent>(_onIncrement);
    on<DecrementEvent>(_onDecrement);
  }
  
  // Обработчик события Increment
  Future<void> _onIncrement(
    IncrementEvent event,
    Emitter<CounterState> emit,
  ) async {
    emit(CounterState(count: state.count + 1));
  }
  
  // Обработчик события Decrement
  Future<void> _onDecrement(
    DecrementEvent event,
    Emitter<CounterState> emit,
  ) async {
    emit(CounterState(count: state.count - 1));
  }
}

BlocProvider — провайдер BLoC

// Создание BLoC один раз
BlocProvider(
  create: (context) => CounterBloc(),
  child: MyWidget(),
)

// Multi — если несколько BLoCs
MultiBlocProvider(
  providers: [
    BlocProvider(create: (context) => CounterBloc()),
    BlocProvider(create: (context) => AuthBloc()),
    BlocProvider(create: (context) => ThemeBloc()),
  ],
  child: MyApp(),
)

BlocBuilder — слушатель состояния

BlocBuilder<CounterBloc, CounterState>(
  builder: (context, state) {
    return Text('Count: ${state.count}');
  },
)

Полный пример счётчика

// События
abstract class CounterEvent {}

class IncrementPressed extends CounterEvent {}
class DecrementPressed extends CounterEvent {}

// Состояния
class CounterState {
  final int count;
  CounterState({required this.count});
}

// BLoC
class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterState(count: 0)) {
    on<IncrementPressed>(_onIncrement);
    on<DecrementPressed>(_onDecrement);
  }
  
  Future<void> _onIncrement(
    IncrementPressed event,
    Emitter<CounterState> emit,
  ) async {
    emit(CounterState(count: state.count + 1));
  }
  
  Future<void> _onDecrement(
    DecrementPressed event,
    Emitter<CounterState> emit,
  ) async {
    emit(CounterState(count: state.count - 1));
  }
}

// UI
class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => CounterBloc(),
      child: Scaffold(
        appBar: AppBar(title: Text('BLoC Counter')),
        body: Center(
          child: BlocBuilder<CounterBloc, CounterState>(
            builder: (context, state) {
              return Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text('Count: ${state.count}', style: TextStyle(fontSize: 48)),
                  SizedBox(height: 32),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      FloatingActionButton(
                        onPressed: () => context.read<CounterBloc>().add(DecrementPressed()),
                        child: Icon(Icons.remove),
                      ),
                      SizedBox(width: 16),
                      FloatingActionButton(
                        onPressed: () => context.read<CounterBloc>().add(IncrementPressed()),
                        child: Icon(Icons.add),
                      ),
                    ],
                  ),
                ],
              );
            },
          ),
        ),
      ),
    );
  }
}

BlocListener — реагирование на события

Используется для побочных эффектов (навигация, диалоги, снэки).

BlocListener<CounterBloc, CounterState>(
  listener: (context, state) {
    if (state.count == 10) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Достигли 10!')),
      );
    }
  },
  child: MyWidget(),
)

Практический пример: Получение данных

// События
abstract class UserEvent {}
class FetchUserEvent extends UserEvent {
  final String userId;
  FetchUserEvent({required this.userId});
}

// Состояния
abstract class UserState {}
class UserInitial extends UserState {}
class UserLoading extends UserState {}
class UserSuccess extends UserState {
  final User user;
  UserSuccess({required this.user});
}
class UserFailure extends UserState {
  final String error;
  UserFailure({required this.error});
}

// BLoC
class UserBloc extends Bloc<UserEvent, UserState> {
  final UserRepository repository;
  
  UserBloc({required this.repository}) : super(UserInitial()) {
    on<FetchUserEvent>(_onFetchUser);
  }
  
  Future<void> _onFetchUser(
    FetchUserEvent event,
    Emitter<UserState> emit,
  ) async {
    emit(UserLoading());
    try {
      final user = await repository.getUser(event.userId);
      emit(UserSuccess(user: user));
    } catch (e) {
      emit(UserFailure(error: e.toString()));
    }
  }
}

// UI
class UserScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocBuilder<UserBloc, UserState>(
        builder: (context, state) {
          if (state is UserInitial) {
            return Center(child: Text('Нажмите кнопку'));
          } else if (state is UserLoading) {
            return Center(child: CircularProgressIndicator());
          } else if (state is UserSuccess) {
            return Center(child: Text(state.user.name));
          } else if (state is UserFailure) {
            return Center(child: Text('Ошибка: ${state.error}'));
          }
          return SizedBox();
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          context.read<UserBloc>().add(FetchUserEvent(userId: '123'));
        },
        child: Icon(Icons.download),
      ),
    );
  }
}

BlocBuilder vs BlocListener vs BlocConsumer

// BlocBuilder — только UI обновление
BlocBuilder<MyBloc, MyState>(
  builder: (context, state) => ...,
)

// BlocListener — побочные эффекты
BlocListener<MyBloc, MyState>(
  listener: (context, state) => ...,
  child: ...,
)

// BlocConsumer — комбинация обоих
BlocConsumer<MyBloc, MyState>(
  listener: (context, state) => ...,  // Побочные эффекты
  builder: (context, state) => ...,    // UI обновление
)

Преимущества BLoC

  • Полная разделение логики и UI — легко тестировать
  • Event-driven — явная история всех действий
  • Масштабируемость — подходит для больших проектов
  • TypeSafe — типобезопасные события и состояния
  • DevTools — отличная поддержка в Flutter DevTools

Недостатки

  • Boilerplate код — много классов для простых случаев
  • Кривая обучения — сложнее чем Provider
  • Оверинжиниринг — для простых приложений

Когда использовать BLoC

  • Большие приложения с сложной логикой
  • Нужна полная история всех действий (для аналитики, логирования)
  • Требуется максимальная тестируемость
  • Много разработчиков в команде

Альтернативы

  • Provider — проще, минимальный boilerplate
  • Riverpod — современнее, тоже хороший выбор
  • GetX — быстрее в разработке, но меньше контроля
Что такое BlocProvider и BlocBuilder? | PrepBro