← Назад к вопросам
Как следить за изменниями компонента в BLoC?
2.0 Middle🔥 131 комментариев
#State Management
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как следить за изменениями компонента в BLoC
BLoC (Business Logic Component) — это паттерн управления состоянием в Flutter, который использует Streams и Events для отделения бизнес-логики от UI. Отслеживание изменений компонента — ключевая часть работы с BLoC.
Основные способы отслеживания изменений
1. BlocListener
BlocListener реагирует на изменения состояния и выполняет побочные эффекты (навигация, диалоги, снэки):
BlocListener<CounterBloc, CounterState>(
listener: (context, state) {
if (state is CounterError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.error)),
);
}
},
child: BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text('Счёт: ${state.count}');
},
),
)
2. BlocBuilder
BlocBuilder перестраивает UI при изменении состояния:
BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
if (state is CounterLoading) {
return CircularProgressIndicator();
} else if (state is CounterLoaded) {
return Text('Значение: ${state.count}');
} else if (state is CounterError) {
return Text('Ошибка: ${state.error}');
}
return SizedBox();
},
)
3. BlocConsumer
BlocConsumer объединяет слушатель и построитель в одном виджете:
BlocConsumer<AuthBloc, AuthState>(
listener: (context, state) {
if (state is AuthSuccess) {
Navigator.pushReplacementNamed(context, '/home');
}
},
builder: (context, state) {
if (state is AuthLoading) {
return Center(child: CircularProgressIndicator());
}
return LoginForm();
},
)
Селекторы состояния
ListenableSelector и BlocSelector позволяют слушать только определённые части состояния:
// Слушать только изменения имени пользователя
BlocSelector<UserBloc, UserState, String>(
selector: (state) => state.userName,
builder: (context, userName) {
return Text('Привет, $userName');
},
)
Расширенное отслеживание
Через stream.listen
@override
Widget build(BuildContext context) {
return BlocListener<DataBloc, DataState>(
listener: (context, state) {
// Выполнить действие при изменении
_handleStateChange(state);
},
child: Scaffold(
appBar: AppBar(title: Text('Данные')),
body: BlocBuilder<DataBloc, DataState>(
builder: (context, state) {
if (state is DataLoading) {
return Center(child: CircularProgressIndicator());
}
if (state is DataLoaded) {
return ListView.builder(
itemCount: state.items.length,
itemBuilder: (context, index) {
return ListTile(title: Text(state.items[index]));
},
);
}
return SizedBox();
},
),
),
);
}
Кастомные слушатели
class CustomBlocListener<B extends BlocBase, S> extends BlocListener<B, S> {
CustomBlocListener({
required B bloc,
required void Function(BuildContext, S) listener,
required Widget child,
}) : super(
bloc: bloc,
listener: listener,
child: child,
);
}
Управление подписками
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late StreamSubscription _subscription;
@override
void initState() {
super.initState();
final bloc = context.read<CounterBloc>();
_subscription = bloc.stream.listen((state) {
print('Новое состояние: $state');
// Обновить UI вручную если нужно
});
}
@override
void dispose() {
_subscription.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('Отслеживание изменений'),
),
);
}
}
Лучшие практики
- BlocListener для побочных эффектов (навигация, диалоги)
- BlocBuilder для перестройки UI
- BlocConsumer когда нужно оба
- BlocSelector для оптимизации — слушать только нужные поля
- Правильная иерархия — слушатели близко к листьям, стейт-менеджер вверху
- Отписываться от потоков в dispose, если используешь stream.listen() вручную
Правильное использование этих методов позволяет создавать реактивные, производительные приложения с чистой архитектурой.