Start Debugging

Migre as importações de Material e Cupertino do Flutter para os pacotes material_ui e cupertino_ui

A migração completa de package:flutter/material.dart e package:flutter/cupertino.dart para material_ui 1.1.1 e cupertino_ui 1.0.2: o que dart fix --code=migrate_design_widgets reescreve, por que widgets de terceiros passam a lançar erros de busca de ancestral, o que MaterialUiCompatibilityBridge realmente resolve e como a dependência de flutter_localizations muda.

Para um app cuja única superfície Material é o próprio código, esta é uma migração de um comando e uma tarde: flutter pub add material_ui, depois dart fix --apply --code=migrate_design_widgets, e então rode os testes. As APIs dos widgets são uma cópia idêntica do que havia no SDK, então nada renderiza diferente e nenhum golden deveria se mover. O que custa tempo de verdade é o grafo de dependências. Cada pacote que ainda importa package:flutter/material.dart arrasta para o seu programa uma segunda cópia, incompatível em nível de tipos, de Theme, Material e MaterialLocalizations, e os widgets dele vão falhar na busca de ancestral dentro da sua árvore migrada até você envolver o app em MaterialUiCompatibilityBridge. Este guia tem como alvo o canal stable atual, Flutter 3.47.2 com Dart 3.13.2, mais material_ui 1.1.1 e cupertino_ui 1.0.2.

O relógio importa aqui. As bibliotecas dentro do SDK já estão congeladas, e a depreciação formal está agendada para a versão stable de novembro de 2026.

Por que isso não é uma limpeza opcional

O que quebra

ÁreaMudançaSeveridade
Importaçõespackage:flutter/material.dart passa a ser package:material_ui/material_ui.dart; package:flutter/cupertino.dart passa a ser package:cupertino_ui/cupertino_ui.dartalta, totalmente automatizável
Identidade de tiposO Material do SDK e o Material do material_ui são tipos diferentes em runtime, então buscas de ancestral não cruzam a fronteiraalta, exige a ponte
Delegates de localizaçãoGlobalMaterialLocalizations e GlobalCupertinoLocalizations vêm dos pacotes, não de flutter_localizationsmédia
pubspec.yamlDuas novas dependências diretas; flutter_localizations não é mais uma dependência direta necessáriamédia
Código geradoTudo que emite package:flutter/material.dart em um arquivo .g.dart ou .freezed.dart precisa ser regerado após a passada no código-fontemédia
Pacotes publicadosMigrar seu próprio pacote é uma mudança incompatível para quem o consome, então exige um incremento de versão maiormédia
APIs dos widgetsNenhuma. Construtores, parâmetros e renderização seguem iguaisnenhuma

Essa última linha é toda a razão de esta migração ser viável. O material_ui 1.0.0 é uma cópia da biblioteca embutida como ela estava no congelamento de abril de 2026, não um redesenho.

Checklist de pré-voo

Passos da migração

  1. Adicione os pacotes antes de mexer em uma única importação. A regra do dart fix reescreve strings de importação; ela não edita pubspec.yaml. Faça na ordem errada e você fica com um arquivo cheio de importações não resolvidas.

    # Flutter 3.47.2, Dart 3.13.2
    flutter pub add material_ui
    flutter pub add cupertino_ui

    Isso resolve hoje para material_ui: ^1.1.1 e cupertino_ui: ^1.0.2. Se o seu app é só Material, você ainda recebe cupertino_ui transitivamente, porque o material_ui depende de cupertino_ui: ^1.0.0 desde a versão 1.0.1, mas declare-o explicitamente se você o importa diretamente. Verifique com flutter pub deps --style=compact | grep -E 'material_ui|cupertino_ui' e confirme que os dois resolvem.

  2. Reescreva as importações com a correção que já vem nos pacotes. Ambos registram a mesma correção do analisador, então um comando cobre Material e Cupertino de uma vez.

    dart fix --dry-run --code=migrate_design_widgets   # review first
    dart fix --apply  --code=migrate_design_widgets

    O resultado é um diff de uma linha por arquivo:

    // Before: Flutter 3.43 and earlier
    import 'package:flutter/material.dart';
    
    // After: material_ui 1.1.1
    import 'package:material_ui/material_ui.dart';

    Nada abaixo da linha de importação muda. MaterialApp, Scaffold, ThemeData, Colors, showDialog e todos os outros nomes são exportados com o mesmo identificador. Verifique com grep -rn "package:flutter/material.dart\|package:flutter/cupertino.dart" lib test retornando nada, e depois flutter analyze.

  3. Aponte os delegates de localização para os pacotes. Os delegates e as strings traduzidas se mudaram para material_ui e cupertino_ui, e os pacotes expõem um getter agregado que evita listar três delegates na mão.

    // Before: flutter_localizations, Flutter 3.43
    import 'package:flutter_localizations/flutter_localizations.dart';
    
    localizationsDelegates: const <LocalizationsDelegate<Object>>[
      GlobalMaterialLocalizations.delegate,
      GlobalCupertinoLocalizations.delegate,
      GlobalWidgetsLocalizations.delegate,
    ],
    // After: material_ui 1.1.1
    import 'package:material_ui/material_ui.dart';
    
    localizationsDelegates: GlobalMaterialLocalizations.delegates,

    GlobalMaterialLocalizations.delegates já inclui os delegates de Cupertino e de Widgets. Se você também usa gen-l10n, seu AppLocalizations.delegate gerado não é afetado e entra nessa lista como antes. Agora você pode remover flutter_localizations das suas próprias dependencies, embora ele permaneça no pubspec.lock: o cupertino_ui 1.0.2 ainda depende dele, junto de collection: ^1.19.1 e intl: ^0.20.2. Verifique iniciando com um locale diferente de inglês e checando uma string embutida, por exemplo pressione e segure um TextField e confirme que a opção de colar está traduzida.

  4. Faça a ponte para as dependências que não migraram. Este é o passo que as pessoas pulam e depois depuram por uma hora. Envolva no nível do app com MaterialApp.builder:

    // material_ui 1.1.1
    MaterialApp(
      theme: ThemeData(useMaterial3: true),
      builder: (BuildContext context, Widget? child) {
        return MaterialUiCompatibilityBridge(child: child!);
      },
      home: const HomeScreen(),
    )

    O lado Cupertino é simétrico:

    // cupertino_ui 1.0.2
    CupertinoApp(
      builder: (BuildContext context, Widget? child) {
        return CupertinoUiCompatibilityBridge(child: child!);
      },
      home: const HomeScreen(),
    )

    Você também pode envolver uma subárvore mais estreita se apenas uma tela embute widgets legados, o que mantém os inherited widgets extras fora do resto da árvore. Verifique navegando por todas as telas que hospedam um widget de terceiros. A ponte é um andaime temporário: apague-a quando flutter pub outdated não mostrar mais nada usando as importações antigas.

  5. Regenere tudo que um gerador de código escreveu. O dart fix vê o seu código-fonte, não os templates que o produziram. Rode o gerador de novo depois do passo 2 para que os arquivos emitidos parem de importar a biblioteca do SDK:

    dart run build_runner build --delete-conflicting-outputs

    Depois confira os restos que o dart fix não alcança: arquivos barrel com export que reexportam Material para quem consome, importações condicionais que escolhem uma implementação de Material por plataforma, e qualquer template de gerador seu com o caminho de importação escrito à mão como string. Verifique com o mesmo grep do passo 2, ampliado para o repositório inteiro em vez de apenas lib e test.

  6. Se você publica um pacote, incremente a versão maior. Trocar um pacote publicado para material_ui muda o que quem consome precisa ter no próprio pubspec.yaml. Publicar isso como versão menor quebra apps em silêncio: a árvore de widgets deles acaba misturando origens sem nenhum erro de compilação para apontar. Suba para a próxima versão maior, registre no changelog a restrição de material_ui necessária, e mantenha a versão maior anterior em um branch de manutenção se você dá suporte a versões antigas do Flutter. Verifique com dart pub publish --dry-run.

Verificação

Plano de rollback

Totalmente reversível hoje. As mudanças são um diff de pubspec.yaml, uma linha de importação por arquivo, uma lista de delegates e um widget de ponte opcional, então um git revert do commit de migração te devolve às bibliotecas do SDK sem nenhum dado ou artefato de build para desfazer. Duas ressalvas: não existe dart fix reverso, então um rollback manual significa editar cada importação de volta na mão, e é por isso que o passo zero é um branch. E depois da stable de novembro de 2026, reverter te estaciona em APIs formalmente depreciadas que serão removidas, então trate o rollback como uma forma de desbloquear um release, não como uma decisão.

Detalhes que pegam

“Could not find an ancestor of type MaterialLocalizations” em código que você não escreveu. É o problema de identidade de tipos aparecendo em runtime. Um widget compilado contra a biblioteca do SDK chama MaterialLocalizations.of(context), que percorre a árvore procurando o inherited widget do seu tipo MaterialLocalizations. Seu MaterialApp do material_ui inseriu um tipo diferente com o mesmo nome, a busca não acha, e o assert dispara. Theme.of(context) falha da mesma forma, com “Could not find an ancestor of type Theme”. A ponte do passo 4 existe exatamente para inserir os inherited widgets legados ao lado dos novos, de modo que as duas buscas resolvam. Ela não é remendo para um Scaffold ausente: se o erro vem do seu próprio código migrado, você tem o problema comum descrito em no Material widget found no Flutter, e a ponte não vai ajudar.

Importação não resolvida logo depois de rodar a correção. Você rodou dart fix antes de flutter pub add. Adicione o pacote e rode dart fix --apply --code=migrate_design_widgets de novo; a regra é idempotente.

Não deixe as duas importações no mesmo arquivo. package:flutter/material.dart e package:material_ui/material_ui.dart exportam os mesmos identificadores, então qualquer arquivo com as duas recebe erros de importação ambígua em Material, Theme, Colors e companhia. Prefixar uma delas compila, mas te dá dois design systems em um arquivo, o que é pior que o erro. Escolha um por arquivo.

A data do congelamento e a da depreciação não são a mesma. O anúncio do congelamento de código dizia que as bibliotecas do SDK seriam depreciadas na versão stable seguinte à 3.44. Isso escorregou: a 3.47 saiu em 2026-08-12 sem a depreciação, e as notas da versão 3.47 agora colocam a depreciação formal na stable de novembro. Congeladas desde abril, depreciadas em novembro, removidas depois. Planeje contra novembro, não contra aquilo sobre o que o seu analisador está calado hoje.

Manifestos de assets podem mudar mesmo que os widgets não. O material_ui 1.1.0 expôs o asset do shader ink_sparkle pelo próprio pubspec.yaml e descartou o shader stretch_effect. Se você faz asserções sobre o manifesto de assets ou remove assets não usados em um passo de build, esse é um diff real para revisar.

Migre importações e versões do Flutter em commits separados. Se você pular versões do SDK na mesma passada, qualquer regressão visual terá duas causas candidatas. Faça a atualização do SDK primeiro, confirme que o app está limpo, e só então migre as importações.

Relacionado

Fontes

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Voltar