Menu
Coddy logo textTech

Construtores Assíncronos

Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 79 de 110.

Construtores em Dart não podem ser marcados como async - eles devem retornar uma instância da classe imediatamente, não um Future. Então, como você cria objetos que precisam de inicialização assíncrona, como carregar dados de um banco de dados ou buscar configurações?

A solução é usar um método factory assíncrono estático. Mantenha o construtor privado (ou simples) e forneça um método estático que realize o trabalho assíncrono e retorne um Future da sua classe:

class UserProfile {
  final String name;
  final int age;
  
  UserProfile._(this.name, this.age);  // Construtor privado
  
  static Future<UserProfile> create(String userId) async {
    // Simula a busca de dados
    await Future.delayed(Duration(seconds: 1));
    return UserProfile._('Alice', 25);
  }
}

Future<void> main() async {
  var user = await UserProfile.create('123');
  print(user.name);  // Alice
}

O construtor privado UserProfile._ lida com a criação real do objeto, enquanto o método estático create() lida com todo o trabalho assíncrono. Os chamadores usam await para obter o objeto totalmente inicializado.

Este padrão garante que seu objeto esteja completamente pronto para uso assim que você o tiver — sem estados parcialmente inicializados ou dados ausentes. É comumente usado para classes que precisam carregar recursos, conectar-se a serviços ou realizar qualquer configuração que leve tempo.

challenge icon

Desafio

Fácil

Vamos construir um carregador de configuração que demonstra como lidar com a inicialização assíncrona em classes! Como os construtores não podem ser assíncronos, você usará o padrão de método de fábrica estático (static factory method) para criar objetos que precisam de tempo para "carregar" seus dados.

Você organizará seu código em dois arquivos:

  • config_loader.dart: Crie uma classe ConfigLoader que simula o carregamento de configurações de forma assíncrona. Sua classe deve ter:
    • Três campos finais: appName (String), version (String) e maxUsers (int)
    • Um construtor privado que inicializa todos os três campos
    • Um método de fábrica estático assíncrono chamado load que recebe um parâmetro String configId e retorna um Future<ConfigLoader>. Este método deve:
      • Usar Future.delayed() com 100 milissegundos para simular o tempo de carregamento
      • Gerar os valores de configuração baseados no configId:
        • appName: o configId com " App" anexado
        • version: "1.0." seguido pelo comprimento do configId
        • maxUsers: o comprimento do configId multiplicado por 10
      • Retornar uma nova instância de ConfigLoader com esses valores
    • Um método displayInfo() que imprime a configuração neste formato: Config: [appName] v[version] (max [maxUsers] users)
  • main.dart: Importe seu carregador de configuração e demonstre o padrão de inicialização assíncrona:
    • Imprima Loading configurations...
    • Carregue uma configuração com o configId "Production" e chame seu displayInfo()
    • Carregue uma configuração com o configId "Dev" e chame seu displayInfo()
    • Imprima All configs loaded!

Observe como o método estático load lida com todo o trabalho assíncrono, enquanto o construtor privado simplesmente atribui os valores. Os chamadores usam await para obter um objeto totalmente inicializado!

Saída esperada:

Loading configurations...
Config: Production App v1.0.10 (max 100 users)
Config: Dev App v1.0.3 (max 30 users)
All configs loaded!

Folha de consulta

Construtores em Dart não podem ser marcados como async porque eles devem retornar uma instância da classe imediatamente, não um Future.

Para criar objetos que precisam de inicialização assíncrona, use um método factory estático assíncrono:

class UserProfile {
  final String name;
  final int age;
  
  UserProfile._(this.name, this.age);  // Construtor privado
  
  static Future<UserProfile> create(String userId) async {
    // Realize o trabalho assíncrono aqui
    await Future.delayed(Duration(seconds: 1));
    return UserProfile._('Alice', 25);
  }
}

Uso:

Future<void> main() async {
  var user = await UserProfile.create('123');
  print(user.name);  // Alice
}

O construtor privado (marcado com _) lida com a criação do objeto, enquanto o método estático lida com o trabalho assíncrono. Isso garante que os objetos estejam totalmente inicializados antes do uso.

Experimente você mesmo

import 'config_loader.dart';

void main() async {
  // TODO: Imprima "Loading configurations..."
  
  // TODO: Carregue uma configuração com configId "Production"
  // e chame seu método displayInfo()
  
  // TODO: Carregue uma configuração com configId "Dev"
  // e chame seu método displayInfo()
  
  // TODO: Imprima "All configs loaded!"
}
quiz iconTeste seus conhecimentos

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