
Como o Flutter realmente funciona?
O que são Widgets, Elementos, BuildContext, RenderOject, Bindings? ..
Dificuldade: Iniciante
Entrada
No ano passado ( nota: em 2018 ), quando iniciei minha jornada no fabuloso mundo de Flutter, havia muito pouca informação na Internet em comparação com o que é hoje. Agora, apesar de muitos materiais já terem sido escritos, apenas uma pequena parte deles fala sobre como o Flutter realmente funciona.
O que são Widgets ( widgets ), Elementos ( elementos ), BuildContext? Por que o Flutter é rápido? Por que às vezes não funciona como esperado? O que são árvores e por que são necessárias?
Em 95% dos casos, ao escrever um aplicativo, você só lida com widgets para exibir algo ou interagir com ele. Mas você nunca se perguntou como toda essa mágica funciona por dentro? Como o sistema sabe quando atualizar a tela e quais partes devem ser atualizadas?
Conteúdo:
Parte 1: Antecedentes
A primeira parte do artigo apresenta alguns conceitos-chave que serão usados na segunda parte do material e ajudam a entender melhor o Flutter.
Um pouco sobre o dispositivo
Vamos começar do final e voltar ao básico.
Quando você olha para o seu dispositivo ou, mais precisamente, para o aplicativo em execução no seu dispositivo, você vê apenas a tela.
Na verdade, tudo o que você vê são os pixels, que juntos formam uma imagem bidimensional e, quando você toca a tela com o dedo, o dispositivo reconhece apenas a posição do dedo no vidro.
Toda a mágica do aplicativo (do ponto de vista visual) na maioria dos casos é atualizar essa imagem com base nas seguintes interações:
- com a tela do dispositivo ( por exemplo, um dedo no vidro )
- com a rede ( por exemplo, comunicação com o servidor )
- ao longo do tempo ( por exemplo, animação )
- com outros sensores externos
A visualização da imagem na tela é fornecida pelo hardware (tela), que regularmente (normalmente 60 vezes por segundo) atualiza a tela. Isso é chamado de "taxa de atualização" e é expresso em Hz (Hertz).
O monitor recebe informações para exibição da GPU (Unidade de processamento gráfico), que é um circuito eletrônico especializado otimizado e projetado para formar rapidamente imagens de alguns dados (polígonos e texturas). O número de vezes por segundo que o processador gráfico pode gerar uma "imagem" (= buffer de quadro) a ser exibida e enviá-la ao hardware é chamada de taxa de quadros ( nota: taxa de quadros ). Isso é medido usando um bloco de quadros por segundo ( por exemplo, 60 quadros por segundo ou 60 fps ).
Você pode me perguntar por que comecei este artigo com os conceitos de uma imagem bidimensional exibida por uma GPU / hardware e um sensor físico de vidro, e qual é a conexão com os widgets regulares do Flutter?
Acho que será mais fácil entender como o Flutter realmente funciona se olharmos para esse ponto de vista, pois um dos principais objetivos do aplicativo Flutter é criar essa imagem bidimensional e permitir que ela interaja com ela. Também porque no Flutter, acredite ou não, quase tudo se deve à necessidade de atualizar a tela rapidamente e no momento certo!
Interface entre código e dispositivo
De qualquer forma, todos os interessados em Flutter já viram a seguinte imagem que descreve a arquitetura de alto nível de Flutter.

Quando escrevemos um aplicativo Flutter usando o Dart, permanecemos no nível do Flutter Framework (destacado em verde).
O Flutter Framework interage com o Flutter Engine (em azul) através de uma camada de abstração chamada Window . Esse nível de abstração fornece várias APIs para interação indireta com o dispositivo.
Também através desse nível de abstração, o Flutter Engine notifica o Flutter Framework quando:
- um evento de interesse ocorre no nível do dispositivo (alteração de orientação, alteração de configurações, problema de memória, estado operacional do aplicativo ...)
- algum evento ocorre no nível do vidro (= gesto)
- canal de plataforma envia alguns dados
- mas também principalmente quando o Flutter Engine está pronto para renderizar um novo quadro
Gerenciar a renderização do Flutter Framework Flutter Engine
É difícil de acreditar, mas é verdade. Exceto em alguns casos ( veja abaixo ), nenhum código do Flutter Framework é executado sem iniciar a renderização do Flutter Engine .
Exceções:
- Gesto / Gesto (= evento no copo)
- Mensagens da plataforma (= mensagens geradas por um dispositivo, como GPS)
- Mensagens do dispositivo (= mensagens relacionadas a uma alteração no status do dispositivo, por exemplo, orientação, aplicativo enviado em segundo plano, alertas de memória, configurações do dispositivo ...)
- Respostas futuras ou http
(Entre nós, é possível aplicar uma alteração visual sem chamar o Flutter Engine, mas isso não é recomendado )
Você me pergunta: "Se algum tipo de código relacionado ao gesto é executado e causa uma alteração visual, ou se eu uso um cronômetro para definir a frequência da tarefa que leva a alterações visuais (por exemplo, animação), como funciona?"
Se você deseja que uma alteração visual ocorra ou que algum código seja executado com base em um cronômetro, é necessário informar ao Flutter Engine que algo precisa ser desenhado.
Geralmente, na próxima vez em que o Flutter Engine é atualizado, ele solicita que o Flutter Framework execute algum código e, finalmente, forneça uma nova cena para renderização.
Portanto, uma pergunta importante é como o mecanismo Flutter organiza todo o comportamento do aplicativo com base na renderização.
Para ter uma idéia dos mecanismos internos, observe a seguinte animação:

Uma breve explicação (mais detalhes virão mais tarde):
- Alguns eventos externos (gesto, respostas http, etc.) ou até futuros podem disparar tarefas que tornam necessário atualizar a exibição. A mensagem correspondente é enviada ao Flutter Engine (= Quadro de programação )
- Quando o Flutter Engine está pronto para começar a atualizar a renderização, ele cria uma solicitação Begin Frame
- Essa solicitação Begin Frame é interceptada pelo Flutter Framework , que executa tarefas relacionadas principalmente a Tickers (por exemplo, animação)
- Essas tarefas podem recriar a solicitação para renderização posterior (exemplo: a animação não concluiu sua execução e, para concluí-la, será necessário obter outro quadro inicial mais tarde)
- Em seguida, o Flutter Engine envia um quadro de desenho , que é interceptado pelo Flutter Framework , que procurará todas as tarefas relacionadas à atualização do layout em termos de estrutura e tamanho
- Depois que todas essas tarefas são concluídas, ele prossegue para as tarefas associadas à atualização do layout em termos de renderização
- Se houver algo na tela que precise ser desenhado, uma nova cena ( Cena ) para visualização será enviada ao Flutter Engine , que atualizará a tela.
- O Flutter Framework, em seguida, executa todas as tarefas que serão executadas após a renderização (= retornos de chamada PostFrame) e quaisquer outras tarefas subseqüentes que não estejam relacionadas à renderização
- ... e esse processo começa tudo de novo
RenderView e RenderObject
Antes de mergulhar nos detalhes do fluxo de trabalho, é hora de apresentar o conceito da Árvore de renderização .
Como mencionado anteriormente, tudo será convertido em pixels que serão exibidos na tela, e o Flutter Framework converterá os Widgets que usamos para desenvolver o aplicativo em blocos visuais que serão exibidos na tela.
Essas partes visuais correspondem a objetos chamados RenderObject , que são usados para:
- definindo uma determinada área da tela em termos de tamanho, posição, geometria e também em termos de "conteúdo renderizado"
- identificação de áreas da tela que podem ser afetadas por gestos (= toque do dedo)
Um conjunto de todos os RenderObjects forma uma árvore chamada Árvore de Renderização . No topo desta árvore (= raiz ), encontramos um RenderView .
RenderView fornece uma superfície comum para objetos Render Tree e é uma versão especial do RenderObject .
Visualmente, poderíamos representar tudo isso da seguinte maneira:

A relação entre Widget e RenderObject será discutida mais adiante. Enquanto isso, é hora de ir um pouco mais fundo ...
Ligações de Inicialização
Quando o aplicativo Flutter é iniciado, a função main()
é chamada primeiro, que finalmente chama o runApp(Widget app)
.
Quando o método runApp()
é runApp()
Flutter Framework inicializa as interfaces entre ele e o Mecanismo de Flutter . Essas interfaces são chamadas de ligações ( nota: ligações ).
Introdução às ligações
As ligações foram projetadas para serem o elo entre a estrutura e o mecanismo Flutter. Somente através de ligações os dados podem ser trocados entre o Flutter Framework e o Flutter Engine .
(Há apenas uma exceção a essa regra - RenderView , mas discutiremos isso mais tarde).
Cada ligação é responsável pelo processamento de um conjunto de tarefas, ações, eventos específicos, agrupados por área de atividade.
No momento da redação deste artigo, o Flutter Framework tinha 8 ligações.
Abaixo estão 4 deles que serão considerados neste artigo:
- SchedulerBinding
- Ligação por gestos
- Ligação do renderizador
- Vinculação de widgets
Para completar, vou mencionar os 4 restantes:
- ServicesBinding : responsável pelo processamento de mensagens enviadas pelo canal da plataforma
- PaintingBinding : responsável pelo processamento do cache de imagens
- SemanticsBinding : reservado para a implementação subsequente de tudo relacionado à semântica
- TestWidgetsFlutterBinding : usado pela biblioteca de teste do widget
Você também pode mencionar WidgetsFlutterBinding , mas isso não é realmente uma ligação, mas um tipo de "inicializador de ligações" .
O diagrama a seguir mostra a interação entre as ligações, que vou considerar a seguir, e o Flutter Engine .

Vejamos cada uma dessas ligações "principais".
SchedulerBinding
Essa ligação tem duas responsabilidades principais:
- Diga o Flutter Engine : "Ei! Da próxima vez que não estiver ocupado, acorde-me para que eu possa trabalhar um pouco e lhe dizer o que renderizar, ou se eu precisar que você me ligue mais tarde ..."
- Ouça e responda a esses "despertares perturbadores" (veja abaixo)
Quando o SchedulerBinding solicita uma chamada de ativação ?
Quando o Ticker deve elaborar um novo tick
Por exemplo, você tem uma animação, você a inicia. A animação é cortada usando o Ticker , que é chamado em intervalos regulares (= tick ) para executar um retorno de chamada . Para iniciar um retorno de chamada , precisamos informar ao Flutter Engine para que ele nos acorde durante a próxima atualização (= Begin Frame ). Isso iniciará o retorno de chamada do ticker para concluir sua tarefa. Se o ticker ainda precisar continuar a execução, no final de sua tarefa, ele chamará SchedulerBinding para agendar outro quadro.
Quando atualizar a exibição
Por exemplo, precisamos elaborar um evento que leve a uma alteração visual (exemplo: atualizar a cor de uma parte da tela, rolar, adicionar / remover algo da tela); para isso, precisamos tomar as medidas necessárias para finalmente exibir a imagem atualizada na tela. Nesse caso, quando ocorre essa alteração, o Flutter Framework chama SchedulerBinding para agendar outro quadro usando o Flutter Engine . (Mais tarde, veremos como isso realmente funciona)
Ligação por gestos
Essa ligação ouve a interação com o mecanismo em termos do "dedo" (= gesto ).
Em particular, ele é responsável por receber dados relacionados aos dedos e por determinar com que parte da tela os gestos trabalham. Ele então notifica adequadamente / dessas partes.
Ligação do renderizador
Essa ligação é o link entre o Flutter Engine e a Render Tree . Ela é responsável por:
- ouvindo eventos gerados pelo mecanismo para informar sobre alterações aplicadas pelo usuário por meio de configurações do dispositivo que afetam efeitos visuais e / ou semânticas
- mensagem ao mecanismo sobre alterações que serão aplicadas à exibição
Para fornecer as alterações que serão exibidas na tela, o RendererBinding é responsável por gerenciar o PipelineOwner e inicializar o RenderView .
PipelineOwner é um tipo de orquestra que sabe o que precisa ser feito com o RenderObject de acordo com o componente e coordena essas ações.
Essa ligação escuta as alterações aplicadas pelo usuário por meio das configurações do dispositivo que afetam o idioma (= localidade ) e a semântica .
Pequena nota
Suponho que, numa fase posterior do desenvolvimento do Flutter, todos os eventos relacionados à semântica sejam transferidos para o SemanticsBinding , mas no momento da redação deste artigo, esse não é o caso.
Além disso, WidgetsBinding é o link entre os widgets e o Flutter Engine . Ela é responsável por:
- gerenciamento do processo de processamento de alterações na estrutura de widgets
- renderizar chamada
O processamento de alterações na estrutura dos widgets é realizado usando o BuildOwner .
O BuildOwner controla quais widgets precisam ser reconstruídos e lida com outras tarefas que se aplicam à estrutura do widget como um todo.
Parte 2. De widgets para pixels
Agora que aprendemos o básico do trabalho interno de Flutter , é hora de falar sobre widgets.
Em toda a documentação do Flutter, você lerá todos os widgets (widgets).
Isso está quase correto. Mas, para ser um pouco mais preciso, prefiro dizer:
Do lado do desenvolvedor, tudo relacionado à interface do usuário em termos de layout e interação é feito usando widgets.
Por que tanta precisão? Além do fato de o Widget permitir que o desenvolvedor determine parte da tela em termos de tamanho, conteúdo, layout e interação, MAS há muito mais. Então, o que é realmente o Widget ?
Configuração imutável
Se você olhar o código fonte do Flutter , notará a seguinte definição da classe Widget .
@immutable abstract class Widget extends DiagnosticableTree { const Widget({ this.key }); final Key key; ... }
O que isso significa?
A anotação "@ imutável" é muito importante e nos diz que qualquer variável na classe Widget deve ser FINAL , ou seja, "definida e designada UMA VEZ PARA TODOS ". Assim, depois de criar uma instância, o Widget não poderá mais alterar suas variáveis internas.
Como o Widget é imutável, ele pode ser considerado uma configuração estática.
A estrutura hierárquica dos widgets
Ao projetar com o Flutter, você define a estrutura de suas telas usando widgets como este:
Widget build(BuildContext context){ return SafeArea( child: Scaffold( appBar: AppBar( title: Text('My title'), ), body: Container( child: Center( child: Text('Centered Text'), ), ), ), ); }
Este exemplo usa 7 widgets que juntos formam uma estrutura hierárquica. Um esquema muito simplificado baseado neste código é o seguinte:

Como você pode ver, o diagrama apresentado se parece com uma árvore, onde SafeArea é sua raiz.
Floresta atrás das árvores
Como você já sabe, um widget em si pode ser uma agregação de outros widgets. Como exemplo, você pode modificar o código anterior da seguinte maneira:
Widget build(BuildContext context){ return MyOwnWidget(); }
Esta opção assume que o próprio widget "MyOwnWidget" exibirá SafeArea , Scaffold . Mas a coisa mais importante neste exemplo é que
Um widget pode representar uma folha, um nó em uma árvore, até a própria árvore ou, por que não, uma floresta de árvores ...
Entendendo o elemento em uma árvore
O que isso tem a ver com isso?
Como será mostrado mais adiante, para poder gerar pixels que compõem a imagem exibida no dispositivo, o Flutter deve conhecer em detalhes todas as pequenas partes que compõem a tela e, para determinar todas as partes, precisa conhecer a expansão de todos os widgets.
Para ilustrar esse ponto, considere o princípio de uma boneca aninhada: quando fechada, você vê apenas 1 boneca, mas ela contém outra, que por sua vez contém outra e assim por diante ...

Quando o Flutter expande todos os widgets (parte da tela) , será como obter todos os bonecos (parte do todo) .
A figura abaixo mostra parte da estrutura hierárquica final dos widgets correspondentes ao código anterior. Em amarelo, destaquei os widgets mencionados no código anteriormente, para que você possa defini-los na árvore final.

Esclarecimentos importantes
A linguagem "Árvore de widgets" existe apenas para facilitar o entendimento, pois os programadores usam widgets, mas NÃO existe nenhuma árvore de widgets no Flutter!
De fato, seria mais correto dizer "árvore dos elementos"
É hora de introduzir o conceito de um elemento .
Cada widget possui um elemento. Os elementos são conectados um ao outro e formam uma árvore. Portanto, um elemento é uma referência a algo na árvore.
Para começar, pense em um elemento como um nó que possui um pai e possivelmente um filho. Ao vinculá-los através de um relacionamento pai-filho , obtemos uma estrutura em árvore.

Como você pode ver, o elemento aponta para um widget e também pode apontar para um RenderObject .
Melhor ainda ... O elemento aponta para o Widget que criou este elemento!
Vamos resumir:
- Não existe uma árvore de widgets, mas há uma árvore de elementos
- Os elementos são criados por widgets.
- O item refere-se ao widget que o criou.
- Elementos vinculados aos relacionamentos pai
- Um item pode ter um "bebê".
- Os elementos também podem apontar para um RenderObject.
Os elementos determinam como as partes dos blocos exibidos são relacionadas entre si.
Para melhor imaginar onde o conceito de um elemento se encaixa, vejamos a seguinte representação visual:

Como você pode ver, a árvore de elementos é o relacionamento real entre widgets e RenderObjects .
Mas por que o Widget cria um elemento ?
3 categorias de widgets
No Flutter, os widgets são divididos em 3 categorias, pessoalmente os chamo da seguinte forma (mas essa é apenas a minha maneira de classificá-los) :
Proxy
O principal objetivo desses widgets é armazenar algumas informações (que devem estar acessíveis aos widgets), parte da estrutura da árvore com base no Proxy. Um exemplo desses widgets é InheritedWidget ou LayoutId .
Esses widgets não participam diretamente da formação da interface do usuário, mas são usados para obter as informações que eles podem fornecer.
Renderer
Esses widgets estão diretamente relacionados ao layout da tela, porque determinam (ou são usados para determinar) o tamanho , posição , renderização . Exemplos típicos são: Linha , Coluna , Pilha e Preenchimento , Alinhamento , Opacidade , RawImage ...
Componente
Esses são outros widgets que fornecem diretamente não as informações finais relacionadas aos tamanhos, posições, aparência, mas os dados (ou dicas) que serão usados para obter as informações finais. Esses widgets são comumente referidos como componentes.
Exemplos: RaisedButton , Andaime , Texto , GestureDetector , Container ...

Este arquivo PDF lista a maioria dos widgets agrupados por categoria.
Por que essa separação é importante? Porque, dependendo da categoria do widget, o tipo de elemento correspondente está associado a ...
Tipos de itens
Existem vários tipos de elementos:

Como você pode ver na figura acima, os elementos são divididos em 2 tipos principais:
Ótimo! Tanta informação, mas como tudo isso está relacionado um ao outro e por que é interessante falar sobre isso?
Como widgets e elementos funcionam juntos
No Flutter, todas as mecânicas são baseadas na invalidação de um elemento ou renderObject.
A invalidação do elemento pode ser feita das seguintes maneiras:
- usando
setState
, que invalida todo o StatefulElement (observe que eu não intencionalmente digo StatefulWidget ) - por meio de notificações processadas por proxyElement (por exemplo, InheritedWidget), que invalida qualquer elemento que dependa desse proxyElement
O resultado da invalidação é que um link para o elemento correspondente aparece na lista de elementos sujos .
A invalidação de renderObject significa que a estrutura dos elementos não muda, mas há uma alteração no nível de renderObject , por exemplo:
- alterando seu tamanho, posição, geometria ...
- algo precisa ser repintado, por exemplo, quando você altera a cor do plano de fundo, o estilo da fonte ...
O resultado dessa invalidação é um link para o renderObject correspondente na lista de objetos de renderização (renderObjects) que precisam ser reconstruídos ou redesenhados.
Independentemente do tipo de invalidação, SchedulerBinding é chamado (lembra-se disso?) Para solicitar ao Flutter Engine que agende um novo quadro.
Este é exatamente o momento em que o Flutter Engine "acorda" o SchedulerBinding e toda a mágica acontece ...
onDrawFrame ()
No início deste artigo, observamos que o SchedulerBinding tem duas responsabilidades principais, uma das quais é a disposição de lidar com solicitações feitas pelo Flutter Engine relacionadas à reconstrução de quadros. Este é o momento perfeito para focar nisso.
O diagrama de sequência parcial abaixo mostra o que acontece quando o SchedulerBinding recebe uma solicitação onDrawFrame () do Flutter Engine .

Etapa 1. Elementos
WidgetsBinding é chamado e essa ligação primeiro considera as alterações associadas aos elementos. WidgetsBinding chama o método buildScope do objeto buildOwner , pois o BuildOwner é responsável pelo processamento da árvore de itens. Este método percorre a lista de elementos sujos e solicita sua reconstrução .
Os principais princípios desse método de rebuild()
) são:
- Há uma solicitação para reconstruir o elemento (isso levará a maior parte do tempo), chamando o método
build()
do widget ao qual esse elemento se refere (método = Widget build (BuildContext context) {...}
). Este método build()
retornará um novo widget - Se o elemento não tiver "filhos", um elemento será criado para o novo widget (veja abaixo) ( nota: inflateWidget ), caso contrário
- o novo widget é comparado ao referenciado pelo filho do elemento
- Se eles forem intercambiáveis (= o mesmo tipo e chave de widget ), a atualização ocorrerá e o filho será salvo.
- Se eles não forem intercambiáveis, o filho será descartado ( ~ descartado ) e um elemento será criado para o novo widget
- Este novo item é montado como filho do item. ( montado) = inserido na árvore de elementos)
A animação a seguir tentará tornar essa explicação um pouco mais clara.

Nota sobre widgets e elementos
Para um novo widget, é criado um elemento de um tipo específico que corresponde à categoria do widget, a saber:
- InheritedWidget -> InheritedElement
- StatefulWidget -> StatefulElement
- StatelessWidget -> StatelessElement
- InheritedModel -> InheritedModelElement
- InheritedNotifier -> InheritedNotifierElement
- LeafRenderObjectWidget -> LeafRenderObjectElement
- SingleChildRenderObjectWidget -> SingleChildRenderObjectElement
- MultiChildRenderObjectWidget -> MultiChildRenderObjectElement
- ParentDataWidget -> ParentDataElement
Cada um desses tipos de elementos tem seu próprio comportamento. Por exemplo:
- StatefulElement chamará o método
widget.createState()
na inicialização, que criará um State e o associará ao elemento - Quando um elemento do tipo RenderObjectElement é montado, ele cria um RenderObject . Esse renderObject será adicionado à Render Tree e associado ao elemento.
Etapa 2. renderObjects
Agora, depois de concluir todas as ações associadas aos elementos sujos , a Árvore de elementos fica estável. Portanto, é hora de considerar o processo de visualização.
Como RendererBinding é responsável por renderizar a Render Tree , o WidgetsBinding chama o método drawFrame
RendererBinding .
O diagrama parcial abaixo mostra a sequência de ações executadas durante a solicitação drawFrame () .

Nesta etapa, as seguintes ações são executadas:
- Cada renderObject marcado como sujo é solicitado para compor (ou seja, calcular seu tamanho e geometria)
- Cada renderObject marcado como "necessitando de redesenho" é redesenhado usando seu próprio método de camada
- A cena resultante é formada e enviada ao Flutter Engine , para que este último a transfira para a tela do dispositivo
- Por fim, a semântica também é atualizada e enviada ao Flutter Engine
No final deste fluxo de trabalho, a tela do dispositivo é atualizada.
Parte 3: Manuseando gestos
Gestos (= eventos relacionados a ações dos dedos no vidro ) são processados usando GestureBinding .
Quando o Mecanismo de Flutter envia informações sobre um evento de gesto pela API window.onPointerDataPacket , o GestureBinding o intercepta, executa alguns buffers e:
- converte as coordenadas fornecidas pelo Flutter Engine para corresponder à proporção de pixels do dispositivo e, em seguida,
- recupera do renderView uma lista de todos os RenderObjects que estão na parte da tela relacionada às coordenadas do evento
- itera pela lista resultante de renderObjects e envia um evento relacionado a cada um deles
- se renderObject "escuta" eventos desse tipo, ele o processa
Espero que agora eu entenda a importância do renderObjects .
Parte 4: Animações
Esta parte do artigo é sobre o conceito de animação e uma profunda compreensão do Ticker .
Quando você trabalha com animações, geralmente usa um AnimationController ou qualquer widget para animações ( nota: AnimatedCrossFade ).
No Flutter, tudo relacionado às animações refere-se ao Ticker . O ticker , quando está ativo, tem apenas uma tarefa: "solicita ao SchedulerBinding que registre um retorno de chamada e diga ao Flutter Engine para ativá-lo quando um novo retorno de chamada aparecer". Quando o Flutter Engine está pronto, ele chama SchedulerBinding por meio de uma solicitação: " onBeginFrame ". SchedulerBinding acessa a lista de retorno de chamada e executa cada uma delas.
Cada tick é interceptado por um controlador "interessado" para processá-lo. Se a animação estiver concluída, o ticker será "desativado"; caso contrário, o ticker solicitará um SchedulerBinding para agendar um novo retorno de chamada. E assim por diante ...
Imagem completa
Agora aprendemos como o Flutter funciona:

Buildcontext
Por fim, retorne ao diagrama que mostra os diferentes tipos de elementos e considere a assinatura do elemento raiz:
abstract class Element extends DiagnosticableTree implements BuildContext { ... }
Vemos o muito famoso BuildContext ! Mas o que é isso?
BuildContext é uma interface que define vários getters e métodos que podem ser implementados por um elemento. BuildContext é usado principalmente no método build()
de StatelessWidget ou State for StatefulWidget .
BuildContext nada mais é do que o próprio elemento , que corresponde
- widget sendo atualizado (dentro dos métodos de
build
ou builder
) - StatefulWidget associado a State em que você faz referência à variável de contexto.
Isso significa que a maioria dos desenvolvedores trabalha constantemente com elementos sem sequer saber sobre isso.
Qual a utilidade de um BuildContext?
BuildContext , , , BuildContext , :
- RenderObject , (, Renderer , -)
- RenderObject
- . ,
of
(, MediaQuery.of(context)
, Theme.of(context)
…)
, , BuildContext – , . StatelessWidget , StatefulWidget , setState()
, BuildContext .
, !
– , StatelessWidget .
, , StatefulWidget .
void main(){ runApp(MaterialApp(home: TestPage(),)); } class TestPage extends StatelessWidget {
, setState()
, : _element.markNeedsBuild()
.
Conclusão
: " ". , , Flutter , , , , . , , Widget , Element , BuildContext , RenderObject , . , .
. .
PS , () .
PSS Flutter internals Didier Boelens, )