Se você utiliza o Bloc como gerenciador de estado já se deparou com a situação na qual você precisa invocá-lo para realizar uma chamada específica. Uma das formas para isso é utilizarmos as extensões de contexto.

Por que isso importa

Entender como utilizar as extensões de contexto nos permitirá obter a instância mais próxima do Bloc naquele contexto sem termos a necessidade de instanciá-lo.

No entanto, é importante ter cuidado com algumas armadilhas.

Cuidado para não utilizá-lo para observar estados

O context.read só captura o estado atual no momento no qual ele é chamado e não recebe as novas atualizações. Isso significa que, se estivermos utilizando-o para obter novas informações do estado teremos um problema de consistência de dados em tela.

Cuidado para não pedí-lo fora do contexto correto

Outro problema comum é tentar acessar o Bloc por meio da extensão de contexto fora do contexto no qual ele foi provisionado. Isso resultará em uma exceção, pois o Bloc não estará disponível naquele contexto específico.

Como funciona

Quando utilizamos o context.read o que estamos fazendo é percorrer a árvore de widgets em busca de um BlocProvider que tenha sido responsável por provisionar o Bloc que estamos buscando.

Exemplo prático

Definição do cubit

import 'package:flutter_bloc/flutter_bloc.dart';
 
class CounterCubit extends Cubit<int> {
    CounterCubit() : super(0);
 
    void increment() => emit(state + 1);
    void decrement() => emit(state - 1);
}
 
class UserProfileState {
    UserProfileState({required this.name});
 
    final String name;
}
 
class UserProfileCubit extends Cubit<UserProfileState> {
    UserProfileCubit() : super(UserProfileState(name: 'John Doe'));
 
    void changeName(String newName) => emit(UserProfileState(name: newName));
}

Construção da UI

class MyApp extends StatelessWidget {
    @override Widget build(BuildContext context) {
        return MultiBlocProvider(
            providers: [
                BlocProvider(create: (_) => CounterCubit()),
                BlocProvider(create: (_) => UserProfileCubit()),
            ],
            child: MaterialApp( home: HomeScreen(), ),
        );
    }
}
 
class HomeScreen extends StatelessWidget {
 
    @override Widget build(BuildContext context) {
        final count = context.watch<CounterCubit>().state;
        final userName = context.select<UserProfileCubit, String>((cubit) => cubit.state.name);
 
        return Scaffold(
            appBar: AppBar(title: Text('Exemplo de Context Watch e Select')),
            body: Center(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                        Text(
                            'Valor do Contador: $count',
                            style: TextStyle(fontSize: 20)
                        ),
                        SizedBox(height: 20),
                        Text(
                            'Nome do Usuário: $userName',
                            style: TextStyle(fontSize: 20)
                        ),
                        SizedBox(height: 20),
                        ElevatedButton(
                            onPressed: () => context.read<CounterCubit>().increment(),
                            child: Text('Incrementar Contador'),
                        ),
                        ElevatedButton(
                            onPressed: () => context.read<UserProfileCubit>().changeName('Jane Doe'),
                            child: Text('Mudar Nome do Usuário'),
                        ),
                    ],
                ),
            ),
        );
    }
}

Conclusão

No exemplo anterior, demonstramos na prática o uso das extensões de contexto. Saber aplicar o context.read tornará sua vida um pouco mais fácil para acessar funções do Bloc desejado.

Nos próximos artigos iremos abordar o uso das outras extensões de contexto como context.select e context.watch, as quais trarão mais reatividade na construção dos nossos Widgets.

Até lá!