Stream Controllers
Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 78 de 110.
Embora os geradores async* sejam ótimos para criar streams com dados predefinidos, às vezes você precisa de mais controle - como adicionar dados a uma stream de diferentes partes do seu código. É aí que o StreamController entra.
Um StreamController atua como um gerenciador para uma stream. Ele fornece um "sink" para adicionar dados e uma "stream" na qual os ouvintes podem se inscrever:
import 'dart:async';
void main() {
var controller = StreamController<String>();
// Escuta o stream
controller.stream.listen((data) {
print('Received: $data');
});
// Adiciona dados através do sink
controller.add('Hello');
controller.add('World');
controller.close(); // Sempre feche quando terminar
}O método add() insere novos valores no stream, e quaisquer ouvintes os recebem imediatamente. Você deve sempre chamar close() quando terminar para liberar recursos.
Por padrão, uma stream pode ter apenas um listener. Para múltiplos listeners, use um broadcast controller:
import 'dart:async';
void main() {
var controller = StreamController<int>.broadcast();
controller.stream.listen((n) => print('Listener 1: $n'));
controller.stream.listen((n) => print('Listener 2: $n'));
controller.add(42);
controller.close();
}StreamControllers são essenciais em OOP para a construção de classes orientadas a eventos - como notificar várias partes do seu aplicativo quando os dados mudam, lidar com ações do usuário ou gerenciar atualizações em tempo real dentro dos seus objetos.
Desafio
FácilVamos construir um sistema de hub de mensagens que permite que múltiplos assinantes recebam mensagens em tempo real! Você usará um StreamController para gerenciar o fluxo de mensagens e demonstrar como as streams de transmissão (broadcast) permitem múltiplos ouvintes.
Você organizará seu código em dois arquivos:
message_hub.dart: Crie uma classeMessageHubque gerencia a distribuição de mensagens usando um StreamController. Seu hub deve:- Usar um
StreamController<String>de broadcast internamente para que múltiplos ouvintes possam se inscrever - Fornecer um getter chamado
messagesque retorna a stream para os ouvintes se inscreverem - Ter um método
broadcast(String message)que adiciona uma mensagem à stream - Ter um método
shutdown()que fecha o controller
- Usar um
main.dart: Importe seu hub de mensagens e configure um cenário com múltiplos assinantes ouvindo a mesma stream:- Crie uma instância de
MessageHub - Adicione um primeiro ouvinte que imprime mensagens no formato:
Subscriber A: [message] - Adicione um segundo ouvinte que imprime mensagens no formato:
Subscriber B: [message] - Transmita (broadcast) três mensagens:
Hello everyone,Welcome to the hub, eGoodbye - Desligue o hub
- Crie uma instância de
Lembre-se de importar dart:async no seu arquivo do hub de mensagens para acessar o StreamController. Observe como ambos os assinantes recebem cada mensagem que é transmitida - esse é o poder das streams de broadcast!
Saída esperada:
Subscriber A: Hello everyone
Subscriber B: Hello everyone
Subscriber A: Welcome to the hub
Subscriber B: Welcome to the hub
Subscriber A: Goodbye
Subscriber B: GoodbyeFolha de consulta
Um StreamController gerencia uma stream, fornecendo controle sobre quando e como os dados são adicionados. Ele fornece um "sink" para adicionar dados e uma "stream" na qual os ouvintes podem se inscrever.
Uso básico:
import 'dart:async';
var controller = StreamController<String>();
// Escuta a stream
controller.stream.listen((data) {
print('Received: $data');
});
// Adiciona dados através do sink
controller.add('Hello');
controller.add('World');
controller.close(); // Sempre feche quando terminar
O método add() envia novos valores para a stream. Os ouvintes os recebem imediatamente. Sempre chame close() ao terminar para liberar recursos.
Por padrão, uma stream pode ter apenas um ouvinte. Para múltiplos ouvintes, use um controlador de broadcast:
var controller = StreamController<int>.broadcast();
controller.stream.listen((n) => print('Listener 1: $n'));
controller.stream.listen((n) => print('Listener 2: $n'));
controller.add(42);
controller.close();
StreamControllers são essenciais para construir classes orientadas a eventos, notificar várias partes do seu aplicativo quando os dados mudam, lidar com ações do usuário ou gerenciar atualizações em tempo real.
Experimente você mesmo
import 'message_hub.dart';
void main() {
// TODO: Criar uma instância de MessageHub
// TODO: Adicionar o primeiro listener que imprime: Subscriber A: [message]
// TODO: Adicionar o segundo listener que imprime: Subscriber B: [message]
// TODO: Transmitir três mensagens:
// - "Hello everyone"
// - "Welcome to the hub"
// - "Goodbye"
// TODO: Desligar o hub
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosBibliotecas e ImportaçõesIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosVariáveis de InstânciaFundamentos de ConstrutoresRecapitulação - Calculadora Simples4Null Safety
Introdução ao Null SafetyNullable vs Non-NullableOs Operadores ? e !Palavra-chave Late e Null SafetyOperadores Null-AwareNull Safety em ClassesRecapitulação - Sistema de Perfil de Usuário7Classes Abstratas e Interfaces
Classes AbstratasMétodos AbstratosInterfaces em DartInterfaces ImplícitasImplementando vs EstendendoMúltiplas InterfacesRecapitulação - Calculadora de Formas10Coleções e Generics
Visão Geral de List, Set e MapColeções Type-SafeClasses GenéricasMétodos GenéricosRestrições de GenericsIterable e IteratorRecapitulação - Armazenamento Genérico13Conceitos Avançados de POO
Composição vs HerançaMétodos de ExtensãoClasses ChamáveisClasses Seladas (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums com Métodos16Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e Usuário2Construtores em Dart
Construtor PadrãoConstrutores NomeadosListas de InicializaçãoConstrutores ConstantesConstrutores FactoryConstrutores de RedirecionamentoRecapitulação - Shape Builder5Encapsulamento
Membros Públicos vs PrivadosA Convenção do Prefixo _Privacidade em Nível de BibliotecaAprofundamento em Getters & SettersOcultação de InformaçãoRecapitulação - Registros de Estudantes8Mixins
Introdução aos MixinsCriando MixinsUsando Múltiplos MixinsPalavra-chave on em MixinsMixin vs HerançaMixin vs InterfaceRecapitulação - Sistema Animal11Métodos Especiais
Sobrescrita de toString()Sobrescrita de hashCode & ==Interface ComparableMétodo call()Sobrescrita de noSuchMethodRecapitulação - Coleção Personalizada14Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy3Propriedades de Classe
Membros de Instância vs EstáticosCampos Final & ConstVariáveis LateMétodos e Campos EstáticosGetters e SettersRecapitulação - Gerenciador de Conta Bancária6Herança
Herança BásicaA Palavra-chave superSobrescrita de MétodoA Anotação @overrideA Palavra-chave final para ClassesConstrutores e HerançaRecapitulação - Hierarquia de Funcionários9Polimorfismo
Fundamentos do PolimorfismoPolimorfismo via InterfacesVerificação de Tipos em Tempo de ExecuçãoOs Operadores is & asPalavra-chave CovariantRecapitulação - Processador de Pagamentos12POO Assíncrona
Futures e async/awaitFundamentos de StreamsStream ControllersConstrutores AssíncronosAsync em Métodos de ClasseRecapitulação - Data Fetcher15Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Repository