The stats feature displays statistics about the active and completed todos.
StatsState
StatsState keeps track of summary information and the current StatsStatus.
part of 'stats_bloc.dart';enum StatsStatus { initial, loading, success, failure }final classStatsStateextendsEquatable{ const StatsState({this.status = StatsStatus.initial,this.completedTodos = 0,this.activeTodos = 0, }); final StatsStatus status; final int completedTodos; final int activeTodos; @override List<Object> get props => [status, completedTodos, activeTodos]; StatsState copyWith({ StatsStatus? status,int? completedTodos,int? activeTodos, }) {return StatsState( status: status ?? this.status, completedTodos: completedTodos ?? this.completedTodos, activeTodos: activeTodos ?? this.activeTodos, ); }}StatsEvent
StatsEvent has only one event called StatsSubscriptionRequested:
part of 'stats_bloc.dart';sealed classStatsEventextendsEquatable{ const StatsEvent(); @override List<Object> get props => [];}final classStatsSubscriptionRequestedextendsStatsEvent{ const StatsSubscriptionRequested();}StatsBloc
StatsBloc depends on the TodosRepository just like TodosOverviewBloc. It subscribes to the todos stream via _todosRepository.getTodos.
part'stats_event.dart';part 'stats_state.dart';classStatsBlocextendsBloc<StatsEvent, StatsState> { StatsBloc({required TodosRepository todosRepository, }) : _todosRepository = todosRepository,super(const StatsState()) {on<StatsSubscriptionRequested>(_onSubscriptionRequested); } final TodosRepository _todosRepository; Future<void> _onSubscriptionRequested( StatsSubscriptionRequested event, Emitter<StatsState> emit, ) async { emit(state.copyWith(status: StatsStatus.loading));await emit.forEach<List<Todo>>( _todosRepository.getTodos(), onData: (todos) => state.copyWith( status: StatsStatus.success, completedTodos: todos.where((todo) => todo.isCompleted).length, activeTodos: todos.where((todo) => !todo.isCompleted).length, ), onError: (_, _) => state.copyWith(status: StatsStatus.failure), ); }}StatsView
view.dart is the barrel file for the stats_page.
export'stats_page.dart';stats_page.dart contains the UI for the page that displays the todos statistics.
classStatsPageextendsStatelessWidget{ const StatsPage({super.key}); @override Widget build(BuildContext context) {return BlocProvider( create: (context) => StatsBloc( todosRepository: context.read<TodosRepository>(), )..add(const StatsSubscriptionRequested()), child: const StatsView(), ); }}classStatsViewextendsStatelessWidget{ const StatsView({super.key}); @override Widget build(BuildContext context) {final l10n = context.l10n;final state = context.watch<StatsBloc>().state;final textTheme = Theme.of(context).textTheme;return Scaffold( appBar: AppBar( title: Text(l10n.statsAppBarTitle), ), body: Column( children: [ ListTile( key: const Key('statsView_completedTodos_listTile'), leading: const Icon(Icons.check_rounded), title: Text(l10n.statsCompletedTodoCountLabel), trailing: Text('${state.completedTodos}', style: textTheme.headlineSmall, ), ), ListTile( key: const Key('statsView_activeTodos_listTile'), leading: const Icon(Icons.radio_button_unchecked_rounded), title: Text(l10n.statsActiveTodoCountLabel), trailing: Text('${state.activeTodos}', style: textTheme.headlineSmall, ), ), ], ), ); }}A simplified representation of the widget tree for the StatsPage is:
├── StatsPage│ └── BlocProvider<StatsBloc>│ └── StatsView│ ├── context.watch<StatsBloc>│ └── ColumnThe TodosOverviewBloc and StatsBloc both communicate with the TodosRepository, but it is important to note there is no direct communication between the blocs.
夜雨聆风