Apps foundation

Migração de duas décadas do app Google Play para o Jetpack Compose

Alto-falantes

  • Kirill Grouchnikov

    Engenheiro de software

  • Jon Boekenoogen

    Engenheiro de software

Sobre

Sobre a palestra

Este ano, o Android se tornou oficialmente focado no Compose para criar todas as interfaces, colocando as visualizações legadas no modo de manutenção. Para o Google Play, essa jornada começou em 2020. Com uma presença de engenharia que abrange quase duas décadas e bilhões de usuários, a migração para o Jetpack Compose foi realizada com práticas recomendadas comprovadas, colaboração estreita com a equipe principal do Compose e evolução contínua das ferramentas.

Descubra como a modernização do Google Play com o Compose resultou em melhorias mensuráveis na velocidade e na estabilidade de longo prazo. A sessão mostra como conectar fluxos de trabalho de design do Figma com a implementação do Compose, automatizando a qualidade para evitar recomposições desnecessárias e criando interfaces expressivas com cores dinâmicas, tipografia avançada e formas em camadas. Você vai sair com estratégias testadas para triar bugs mais rapidamente e escalonar sua própria migração do Compose com confiança.

Resumo

  • O Google Play migrou 97% das impressões de usuários para telas exclusivas do Compose ao longo de seis anos. As páginas principais também tiveram uma melhoria de 30% na taxa de instabilidade nos últimos 14 meses.
  • A adoção incremental reduz os riscos comerciais em grandes bases de código. As equipes podem se familiarizar primeiro com telas de baixo tráfego e, em seguida, avançar para hospedar ComposeView diretamente na raiz da atividade.
  • Os temas dinâmicos funcionam inteiramente em código usando sobreposições de MaterialTheme e a biblioteca Material Color Utilities. Isso evita as remoções de atividades, mas gerar esquemas de cores no momento da criação ajuda a limitar a latência do ambiente de execução.
  • O fluxo de dados unidirecional e a interface declarativa simplificam a arquitetura e removem vários tipos de disputas. A execução de fontes de dados e transformações de interface em paralelo melhorou a latência de carregamento do Google Play em dezenas de milissegundos.
  • As verificações automatizadas do Lint aplicam regras de qualidade de código e do sistema de design. O Google Play as usa para bloquear cores fixadas no código e sinalizar possíveis problemas de desempenho, como BoxWithConstraints.

Gemini Notebook

Tópicos

  • jetpack-compose
  • migração
  • desempenho
  • aplicação de temas
  • unidirectional-data-flow
  • lint-rules
  • developer-velocity

Transcrição

Introdução e adoção da Google Play Store

  • Olá, pessoal!

  • Meu nome é Jon.

  • Faço parte da equipe da Google Play Store.

  • Este é meu colega Kirill.

  • Hoje vamos falar sobre como migramos muito código do sistema de visualização e fragmento para o Compose.

  • Vamos começar.

  • Como foi essa adoção e qual foi a escala dela?

  • A base de código do nosso app cresceu de forma orgânica desde 2007.

  • Temos mais de alguns milhões de linhas de código de produção, 200 telas e 500 componentes de interface exclusivos que são usados em muitas dessas telas.

  • Em 2020, percebemos que a velocidade, a confiabilidade e o desempenho dos desenvolvedores não estavam atingindo as metas que queríamos.

  • Por isso, decidimos adotar uma pilha de tecnologia de interface moderna.

  • Avaliamos várias bibliotecas diferentes.

  • Também tínhamos alguns amigos no AndroidX que nos falaram sobre o Compose na época, e decidimos adotar o Compose e nos tornar o primeiro grande parceiro dele.

  • Como foi?

  • Seis anos depois, 97% das impressões de usuários no app Google Play Store estão em telas exclusivas do Compose.

  • Todo o desenvolvimento para smartphones e telas grandes foi migrado para o Compose em 2020.

  • Até mesmo nossos formatos estendidos, como XR, TV, Auto e Wear, migraram para o Compose desde 2025.

  • O Compose foi um grande defensor desse sucesso e o motivo de continuarmos a adotá-lo cada vez mais.

  • Eles têm um pacote de testes muito forte e abrangente que nos ajuda a adotar novas versões do Compose duas vezes por semana.

  • Ao usar as versões Alfa e as novas APIs, podemos aproveitar as correções de bugs, as melhorias de desempenho e explorar algumas dessas novas APIs para oferecer novas experiências aos usuários.

  • Falando de desempenho em particular, o Compose realmente melhorou o desempenho de instabilidade do nosso app em todas as telas principais.

  • Isso inclui a página de resultados da pesquisa, a página inicial e a página de detalhes.

  • Essas são métricas muito importantes para nossos negócios e também para a experiência do usuário: desempenho, instabilidade e latência de carregamento.

  • Portanto, tivemos uma melhoria de 30% nos últimos 14 meses.

Plano de migração

  • Mas como chegamos até aqui?

  • É sobre isso que vamos falar.

  • Vamos falar sobre o plano de migração.

  • Por isso, o Google Play adotou uma abordagem em várias etapas.

  • Não queríamos apostar todas as nossas fichas em uma única estratégia.

  • Tomamos algumas medidas para entender os riscos, como isso afeta nossos negócios e realmente criar confiança antes de apostar tudo.

  • Vou nomeá-las: engatinhar, andar, trotar e correr.

  • Então, para nós, o engatinhar foi realmente sobre criar familiaridade com o Compose.

  • Começamos com uma página de baixo tráfego.

  • Pode ser a página de configurações, licenças de terceiros, esse tipo de coisa.

  • Seu principal objetivo é se familiarizar com a biblioteca do Compose.

  • Isso pode ser usado para linhas, colunas, texto, imagens e todos esses tipos de coisas.

  • Depois, você precisa começar a trazer toda a infraestrutura do app e entender como ela funciona no Compose.

  • Isso pode ser registro de impressões, registro de cliques, temas, estávamos reformulando o carregamento de dados ao mesmo tempo.

  • Então, é um pouco de tudo isso.

  • E uma coisa que achamos útil para garantir que todas as nossas telas tivessem uma aparência consistente foi criar alguns testes de unidade e testes funcionais para verificar se os objetos de tema no Compose eram mapeados diretamente para os estilos XML, tipografia, cores, tudo isso.

  • Depois que nos familiarizamos e nos sentimos confortáveis, decidimos passar para a caminhada.

  • Para nós, isso significou pegar um componente menor em uma tela de tráfego muito alto e avaliar como seria essa migração.

  • Recomendamos usar um item que esteja em uma RecyclerView para entender as preocupações de compatibilidade entre o sistema de visualização e o Compose, além de medir o desempenho e as instabilidades ao longo do processo.

  • O componente em si não precisa ser muito complexo, mas é importante ter algo que exercite layouts, textos, imagens, esse tipo de coisa, algumas instâncias diferentes do que você normalmente veria.

  • Uma coisa que você deve observar e provavelmente esperar nessa área é um aumento na instabilidade e na latência. Ele será leve, mas isso ocorre porque agora você está executando duas pilhas.

  • Você tem uma pilha de renderização que está toda no Compose e outra que está em Views.

  • Portanto, haverá alguma sobrecarga de memória, algum carregamento de classe extra, todas essas coisas.

  • Mas, à medida que você adotar mais o Compose, isso vai desaparecer.

  • Se você realmente precisar dele nesse meio tempo, faça um leve pré-aquecimento da tela para ajudar a alinhar e reduzir esse custo no curto prazo.

  • Depois de caminhar, passamos para o trote.

  • Isso é para quando já estivermos muito, muito confortáveis.

  • Em vez de fazer um único componente em uma página de alto tráfego, faça a página inteira.

  • Nesse caso, mantivemos a biblioteca de navegação e o uso de fragmentos, mas cada tela foi implementada totalmente no Compose.

  • Fizemos o fragmento retornar a própria ComposeView.

  • O que você pode esperar é uma melhora na latência e no jank.

  • Você não tem tanta sobrecarga de memória e carregamento de classes como eu estava falando.

  • O próprio Compose também faz alguns níveis de armazenamento em cache no nível do ComposeView.

  • Portanto, se você tiver uma RecyclerView com vários cards que parecem consistentes e podem ter os mesmos ícones, toda essa análise de ícones e uso de recursos será compartilhada entre tudo na ComposeView.

  • Depois disso, passamos para a corrida.

  • É quando você realmente se dedica ao máximo.

  • Depois que a maior parte do tráfego do app estiver no Compose, passamos para o próximo nível.

  • Acima das telas, temos a Atividade.

  • Portanto, mova o ComposeView para a raiz da atividade.

  • E grande parte do seu código vai ser relativamente semelhante.

  • Você ainda está tentando fazer uma instrução de navegação em que quer dizer, por exemplo, "mudar de uma tela para outra".

  • Mas, em vez de fazer transações de fragmentos, você está chamando uma função combinável.

  • E você pode fazer isso com a biblioteca Nav2 ou Nav3, o que preferir.

  • No nosso caso, nem todas as telas foram migradas.

  • Eu disse que temos muitos.

  • Por isso, criamos a compatibilidade com fragmentos, em que é possível ter uma tela baseada em fragmentos e usar os wrappers AndroidFragment do Compose para fazer isso.

  • O que você pode esperar, mais uma vez, é que o jank e a latência são importantes para nossos negócios. Por isso, continuamos nos esforçando para melhorar, porque agora temos armazenamento em cache em todas as telas e muito menos problemas de thrashing de memória.

  • Mas você também tem novos recursos.

  • Temos um exemplo disso à direita neste GIF, em que é possível fazer transições de elementos compartilhados entre telas.

  • Temos uma imagem que é ampliada conforme você passa de uma tela para outra, o que dá essa continuidade.

  • Também é possível aproveitar toda a flexibilidade de animação do Compose durante essa transição.

  • Agora, vou passar a palavra para o Kirill, que vai falar sobre mais recursos.

Temas e desempenho

  • Obrigado, Jon.

  • Quero falar um pouco sobre um tema que é muito importante para mim: temas e estilos para apps Android, algo que faço há algum tempo.

  • No mundo das visualizações, havia código Java e, um pouco mais tarde, código Kotlin. Depois, havia recursos XML, como cores, strings, dimensões, layouts, temas e estilos.

  • Todos esses recursos XML eram compilados em uma tabela de recursos incluída no APK, que era praticamente estática.

  • Havia uma API Resources.Theme.applyStyle que podia ser usada dinamicamente no ambiente de execução para aplicar um estilo personalizado à hierarquia da interface.

  • A principal limitação era ou é que você precisa fazer isso antes de inflar o conteúdo. Isso significa que, se você quiser aplicar esse estilo dinamicamente a uma parte da interface, será necessário remover e recriar toda a atividade, o que não é bom para a experiência do usuário.

  • Além disso, como ele aplicava esses blocos XML pré-compilados, estava ficando difícil combinar várias sobreposições dinâmicas condicionais com base nas flags do experimento ou em outras necessidades de negócios.

  • Era um pouco difícil criar um modelo mental adequado da hierarquia de estilos no mundo XML.

  • E, por fim, não havia uma única visualização unificada no Android Studio ou em qualquer outra ferramenta para desenvolvedores que permitisse preencher a lacuna entre o mundo do Java, o mundo do código e o mundo do XML.

  • E era meio difícil conciliar e criar o modelo mental adequado e depurar por que meu estilo não estava sendo aplicado, por que eu estava recebendo a cor errada e assim por diante.

  • No novo mundo, no mundo do Compose, especificamente para o sistema do Material Design e a biblioteca Compose Material, mas também para outros sistemas de design, o que temos para o sistema do Material Design é um elemento combinável MaterialTheme que você aplica ao topo da hierarquia da IU na tela. Você também pode aplicar esse MaterialTheme como uma sobreposição em qualquer parte menor da IU.

  • Na tela à direita, você pode ver o Play Points Ouro na parte de cima, que é um pequeno card com o indicador de progresso e alguns textos.

  • Aplicamos essa sobreposição de tema do Material Design com um esquema de cores personalizado gerado pela biblioteca Material Color Utilities, disponível no GitHub para você usar.

  • Passamos uma cor marrom, amarela, dourada como semente, e ela gera todo o espectro de tokens de cor do Material, primária sobre primária, secundária sobre secundária, que usamos na implementação desse card.

  • Isso coloca o recurso de temas dinâmicos nas mãos dos desenvolvedores Android.

  • Tudo é feito em código, em Kotlin, o que significa que é muito mais fácil criar o modelo mental adequado de como tudo funciona e muito mais fácil de depurar.

  • Internamente, essas sobreposições são anexadas como locais de composição aos nós de interface específicos na sua hierarquia, onde você escolhe aplicá-las.

  • Considerações de desempenho: gerar um esquema de cores completo com a biblioteca Material Color Utilities não é isento de custos de processamento.

  • Isso gera uma certa sobrecarga.

  • Se você tiver algo complexo o suficiente que precise de vários desses, como uma espécie de bento box ou algo assim, dependendo das necessidades do produto, e tiver vários esquemas de cores gerados no momento da execução, isso vai começar a se acumular, aumentando a latência e a instabilidade.

  • Portanto, você precisa começar a medir como isso afeta o desempenho e pode transferir parte dessa sobrecarga do ambiente de execução para o tempo de build.

  • Você pode usar a mesma biblioteca de utilitários de cores do Material Design no momento da criação para gerar esses esquemas de cores com base nas necessidades da sua marca ou do seu produto e transferir essa sobrecarga para o momento da criação.

  • Os locais de composição que mencionei, que monitoram exatamente onde as sobreposições estão na árvore de hierarquia da IU, são muito mais baratos, mas ainda assim têm um custo.

  • Portanto, dependendo de quantos você usa, dezenas, centenas, o custo começa a aumentar.

  • Em algum momento, você vai precisar analisar o custo de todos esses CompositionLocals combinados.

  • Este é um exemplo do que eu estava mencionando, como os recursos de temas dinâmicos de ambiente de execução do Compose.

  • Aqui temos quatro quadrantes, os mesmos recursos visuais, o mesmo, vamos chamar de card de evento.

  • Temos essa imagem principal que ocupa todo o card, um rótulo no canto superior esquerdo, um botão no canto inferior direito e alguns textos.

  • No lado direito de cada quadrante, você vê os tokens de cor usados como especificação de design para cada elemento.

  • A implementação do cartão é exatamente igual para os quatro.

  • A única diferença é que a sobreposição do tema do Material Design que envolve o card é gerada ou configurada com um esquema de cores criado dinamicamente com base na cor dominante da imagem: azul, roxo, amarelo e vermelho.

  • Todo o resto permanece igual.

  • Não é necessário remover e recriar a atividade.

  • É possível aplicá-lo condicionalmente com base nas flags do experimento ou em qualquer outra necessidade de negócios.

  • E é um mundo muito mais dinâmico, como já disse, é um mundo muito mais dinâmico, verdadeiramente dinâmico, de aplicação de personalizações de cores ao seu app.

  • Agora, Jon vai apresentar a próxima seção.

Como escalonar a confiabilidade e a velocidade

  • Sim.

  • Essa é mais uma das flexibilidades que o Kirill mencionou.

  • Vamos falar sobre confiabilidade e velocidade.

  • Por isso, criar códigos de alta qualidade rapidamente é algo que todos os desenvolvedores e empresas buscam.

  • Vamos ver como o Compose ajudou o Google Play a fazer isso.

  • Então, antes de mais nada, sei que o diagrama é grande, mas a simplicidade da arquitetura tem enormes benefícios para todos os autores e mantenedores de código, sejam humanos ou agentes.

  • Não vou me aprofundar muito nisso, mas um dos modelos mentais que o Compose tenta apresentar é o fluxo de dados unidirecional, que realmente teve um grande impacto na nossa equipe.

  • Para quem não sabe como funciona, o básico é que você tem uma fonte de dados.

  • Uma fonte de dados fornece dados.

  • Esses dados podem ser transformados em uma interface específica, que é mostrada como um modelo de interface.

  • Em seguida, o Compose pode observar isso facilmente chamando uma propriedade de valor nesse estado.

  • Isso é representado pelas setas na parte de cima do diagrama.

  • E os eventos fluem na direção oposta.

  • Portanto, se um usuário clicar em um botão, isso vai voltar para o transformador de IU, atualizar a fonte de dados, emitir um novo valor, executar as transformações e você vai ter esse loop.

  • Então, tudo passa por esse loop relativamente simples.

  • Os benefícios que realmente vimos com isso é que ajuda a reduzir o tempo de otimização.

  • Não há mais como saber onde todas essas chamadas estão acontecendo na linha de execução principal ou fora dessas outras linhas.

  • Ele segue um loop geral.

  • O Compose facilita muito a observação de dados, e os dados têm garantias de consistência forte em todas as camadas arquitetônicas.

  • O modelo mental reduziu muitas conversas sobre fontes de verdade na nossa equipe e removeu completamente vários tipos de disputas.

  • Outro grande benefício para nós, em comparação com a arquitetura de modelo-visualização-controlador que tínhamos, é que ficou muito, muito simples ter a execução paralela de fontes de dados e transformações de interface.

  • Isso melhorou muito nossa latência de carregamento na Play Store, em dezenas de milissegundos, cerca de 50 ms.

  • Foi bem impressionante para nós.

  • Além do fluxo de dados unidirecional, o outro grande defensor do Compose é a interface declarativa.

  • Kirill já falou um pouco sobre isso.

  • Os desenvolvedores do Google Play também gostaram muito disso.

  • O fluxo de controle da interface agora está em um único arquivo.

  • Temos um exemplo à direita que é bem simples.

    @Composable
    fun PurchaseRow(onClick: () -> Unit, modifier: Modifier) {
      Row(modifier) {
        Text("20% off sale")
        if (LocalExpts.current.useButtonV2) {
          ButtonV2("Buy", onClick = onClick)
        } else {
          ButtonV1("Buy", onClick = onClick)
        }
      }
    }
  • Estamos alternando entre duas implementações de botão com base em uma flag de experimento.

  • Em vez de precisar ler vários arquivos de visualização ou, você sabe, arquivos de fragmento e arquivos XML, basta ler de cima para baixo, e isso simplificou muito as coisas.

  • O custo de aninhar elementos que podem ser compostos também é muito menor do que as hierarquias de visualização aninhadas, o que nos permitiu organizar o código de forma natural para torná-lo mais legível, em vez de precisar extrair cada detalhe para melhorar o desempenho.

  • Outro benefício que observamos é que os callbacks de ciclo de vida ficaram muito mais simples e foram reduzidos em quantidade, o que diminui as preocupações.

  • Então, em vez de ter o ciclo de vida da visualização, do fragmento e da atividade, você tem o efeito descartável, o efeito colateral e, dependendo de onde você o coloca, ele age de maneira muito consistente.

  • Isso tem sido um grande benefício para nós.

Preparação para os LLMs

  • Na minha opinião, essas duas mudanças arquitetônicas prepararam o terreno para os LLMs e permitiram que eles gerassem códigos de alta qualidade.

  • E tenho certeza de que muitos de vocês já tentaram ou fizeram isso com seu LLM favorito.

  • Você pode fazer uma captura de tela com anotações de cores e tipografia e inseri-la para gerar linhas, colunas, texto e botões, e ela faz um trabalho razoável.

  • No entanto, ele tem limitações.

  • E o que o Play realmente percebeu é que ele não entende sua base de código, certo?

  • Ele não sabe quais componentes você já criou e que devem ser reutilizados, nem os nomes dos tokens de design.

  • Ele só fornece cores e strings codificadas.

  • Qual conteúdo deve ser do dispositivo, qual deve vir de um servidor ou como garantir que o desempenho seja bom ao usar o LazyList e não apenas criar uma coluna enorme?

  • Para resolver alguns desses problemas, adicionamos um pouco mais de metadados às capturas de tela.

  • Portanto, nomes de interface, caixas delimitadoras para sabermos onde os componentes começam e terminam, tokens de design para os nomes semânticos de cores e tipografia, habilidades e também algumas instruções para dizer onde isso será colocado, para que ele possa considerar essas questões de desempenho.

  • E também uma assinatura de função de nível muito alto, para que você possa informar qual conteúdo é dinâmico e qual é estático.

  • Assim, você acaba com algo como o que tínhamos antes, passando para o depois.

  • Não incluí as caixas delimitadoras aqui porque elas adicionariam muito ruído ao diagrama.

  • Mas você pode ver que essas cores e nomes de tipografia codificados foram substituídos por "title large" e "on surface".

  • Os nomes dos componentes, como cabeçalho do card e selo do ícone, estão claramente marcados.

  • Assim, eles podem ser encontrados na sua base de código.

  • E, para nós, muitos deles também vêm de bibliotecas de design comuns, sobre as quais o Kirill vai falar em um minuto.

  • Portanto, você não precisa rotular todos esses dados o tempo todo.

  • Você combina isso com uma assinatura de método como a que temos na parte de baixo, OfferCard, e então ele sabe que o título, o endDate e a imagem são todos dinâmicos.

  • Não devemos codificar esses valores.

  • E, junto com esses nomes semânticos, agora podemos trocar todas as diferentes aplicações de cores que o Kirill mencionou.

  • Com isso, estamos gerando muito mais código.

  • Vou passar a palavra para o Kirill, que vai falar sobre como estamos mantendo a qualidade.

Automação de qualidade

  • Certo.

  • Obrigado, Jon.

  • Continuando com o tema de LLMs e desenvolvimento assistido por agentes, vamos começar com o design de UX.

  • O que você precisa fazer é, seja qual for sua ferramenta de design, como o Figma ou outra, ter um modelo compartilhado que seja usado por todos os designers e que tenha tokens de cor, tipografia e forma do sistema de design escolhido.

  • Talvez seja o Material.

  • Talvez seja o sistema de design personalizado.

  • E, especificamente para os tokens de cor, você quer que esses modelos tenham suporte integrado para alternar entre os modos claro e escuro e entre os modos de contraste normal, médio e alto, que foram introduzidos no Android há alguns anos.

  • Assim, os designers podem alternar rapidamente entre essas diferentes combinações para ver como as especificações de design respondem a isso.

  • Depois, quando o design estiver sendo finalizado e entregue para a implementação, no pipeline de exportação, você precisa ter algum tipo de recurso para garantir que cada cor, cada elemento tipográfico e cada forma use um token correspondente, em vez de um valor RGB, uma família de fontes ou um tamanho de fonte fixados no código.

  • Por fim, o que achamos muito útil na escala do Google Play é que temos não apenas dezenas, mas algumas centenas de componentes, clusters, cards e contêineres que são usados em várias telas.

  • O que achamos útil é acompanhar tudo isso em um único lugar, o que chamamos de catálogo de componentes, e depois treinar um agente personalizado nesse catálogo.

  • Isso é útil quando você começa uma nova tela, talvez uma nova jornada do usuário, talvez uma nova área de produto. Com base nessas explorações iniciais de design, talvez slides, talvez especificações do Figma, você pode alimentar esse agente, que ajuda a identificar rapidamente quais componentes já existem para que não precisem ser redesenhados e reespecificados, para que não precisem ser reimplementados do zero.

  • Isso reduz significativamente os custos de design e implementação.

  • Na implementação, dependemos muito das regras principais e personalizadas do Lint.

  • Algumas regras principais do Lint que quero destacar são o FrequentlyChangingValueDetector, que ajuda a identificar fontes de recomposições desnecessárias, que podem levar à latência e instabilidade, e o ScaffoldPaddingDetector, para sinalizar casos em que usamos o elemento combinável Scaffold do Material Compose, mas ignoramos os paddings recebidos.

  • Não é uma fonte de latência e instabilidade, mas, em alguns casos, pode levar a desalinhamentos de layout muito difíceis de depurar, como em casos de tela cheia ou de dobra e desdobra.

  • Por fim, o BoxWithConstraints tem algumas considerações de desempenho.

  • Pode levar dois frames para que tudo seja disposto corretamente.

  • Não bloqueamos isso, mas sinalizamos como um aviso do Lint para que o desenvolvedor que está trabalhando nessa tela específica saiba que está fazendo uma escolha específica ao usar o BoxWithConstraints ou talvez possa escolher usar outra coisa para esse layout específico.

  • Para a automação de temas, novamente, tokens de cor, tipografia e forma.

  • Temos uma regra de lint que bloqueia o uso de cores e tipografia fixadas no código.

  • Temos uma regra do Lint que sinaliza o uso de componentes do Material 2.

  • Queremos usar apenas o Material 3, o mais recente e melhor, incluindo os novos componentes expressivos que foram introduzidos no ano passado e neste ano.

  • Temos uma regra personalizada do Lint que impede as pessoas de ler cores de, digamos, arquivos XML legados.

  • Queremos usar apenas o Compose nas novas telas.

  • E os elementos de texto combináveis, especificamente no sistema de design do Material Design, têm substitutos para estilo e cor.

  • Eles leem isso da cor do conteúdo local e da tipografia local, mas não queremos depender disso.

  • Queremos que cada texto use intencionalmente o estilo específico, o estilo de tipografia e a cor específica da especificação de design.

  • Essa é uma regra de lint que temos.

  • E, por último, na mesma linha do ScaffoldPaddingDetector, temos uma regra de lint personalizada que garante que todas as funções combináveis usem o modificador de entrada.

  • Isso evita que novos usos sejam colocados incorretamente ou não respeitem o plano de fundo recebido, por exemplo.

Conclusão

  • E esses são os três pontos principais que gostaríamos que você levasse desta apresentação.

  • A primeira é que hoje, no final de 2026, na nossa opinião, o Compose é um kit de ferramentas de IU maduro, comprovado em escala.

  • O próprio Android prioriza o Compose.

  • No início deste ano, no Google I/O, anunciamos que todas as bibliotecas de interface e todos os fluxos de agentes priorizam o Compose.

  • E, como o Jon mencionou antes, é preciso engatinhar, andar, correr e voar. Sugerimos que você migre de forma incremental, especialmente bases de código grandes, para acelerar o fluxo de desenvolvedores e de agentes.

  • E, com isso, agradecemos por compartilhar seu tempo conosco hoje. Em alguns minutos, eu e o Jon estaremos do lado de fora para responder às suas perguntas.

  • Agradeço novamente sua atenção.

Código

Snippets de código

Snippet do Kotlin aos 16:09 em "Como escalonar a confiabilidade e a velocidade"
@Composable
fun PurchaseRow(onClick: () -> Unit, modifier: Modifier) {
  Row(modifier) {
    Text("20% off sale")
    if (LocalExpts.current.useButtonV2) {
      ButtonV2("Buy", onClick = onClick)
    } else {
      ButtonV1("Buy", onClick = onClick)
    }
  }
}