
¿Cómo funciona realmente Flutter?
¿Qué son los widgets, elementos, BuildContext, RenderOject, Bindings? ..
Dificultad: principiante
Entrada
El año pasado ( nota: en 2018 ), cuando comencé mi viaje al fabuloso mundo de Flutter, había muy poca información en Internet en comparación con lo que es hoy. Ahora, a pesar del hecho de que muchos materiales ya se han escrito, solo una pequeña parte de ellos habla sobre cómo funciona realmente Flutter.
¿Qué son los widgets ( widgets ), elementos ( elementos ), BuildContext? ¿Por qué es rápido Flutter? ¿Por qué a veces no funciona como se esperaba? ¿Qué son los árboles y por qué se necesitan?
En el 95% de los casos al escribir una aplicación, solo tratará con widgets para mostrar algo o interactuar con él. Pero, ¿nunca te has preguntado cómo funciona toda esta magia por dentro? ¿Cómo sabe el sistema cuándo actualizar la pantalla y qué partes deben actualizarse?
Contenido:
Parte 1: antecedentes
La primera parte del artículo presenta algunos conceptos clave que se utilizarán en la segunda parte del material y ayudan a comprender mejor Flutter.
Un poco sobre el dispositivo
Comencemos desde el final y volvamos a lo básico.
Cuando mira su dispositivo o, más precisamente, la aplicación que se ejecuta en su dispositivo, solo ve la pantalla.
De hecho, todo lo que ves son los píxeles, que juntos forman una imagen bidimensional, y cuando tocas la pantalla con el dedo, el dispositivo solo reconoce la posición de tu dedo en el cristal.
Toda la magia de la aplicación (desde un punto de vista visual) en la mayoría de los casos es actualizar esta imagen en función de las siguientes interacciones:
- con la pantalla del dispositivo ( por ejemplo, un dedo en el cristal )
- con la red ( por ejemplo, comunicación con el servidor )
- con el tiempo ( por ejemplo, animación )
- con otros sensores externos
La visualización de la imagen en la pantalla es proporcionada por hardware (pantalla), que regularmente (generalmente 60 veces por segundo) actualiza la pantalla. Esto se llama "frecuencia de actualización" y se expresa en Hz (Hertz).
La pantalla recibe información para su visualización desde la GPU (Unidad de procesamiento de gráficos), que es un circuito electrónico especializado optimizado y diseñado para formar rápidamente imágenes a partir de algunos datos (polígonos y texturas). El número de veces por segundo que el procesador de gráficos puede generar una "imagen" (= búfer de cuadros) para mostrar y enviarlo al hardware se denomina velocidad de cuadros ( nota: velocidad de cuadros ). Esto se mide usando un bloque de cuadros por segundo ( por ejemplo, 60 cuadros por segundo o 60 fps ).
Puede preguntarme por qué comencé este artículo con los conceptos de una imagen bidimensional mostrada por una GPU / hardware y un sensor de vidrio físico, y ¿cuál es la conexión con los widgets Flutter normales?
Creo que será más fácil entender cómo funciona realmente Flutter si lo miramos desde este punto de vista, ya que uno de los objetivos principales de la aplicación Flutter es crear esta imagen bidimensional y permitirle interactuar con ella. Además, porque en Flutter, lo creas o no, ¡casi todo se debe a la necesidad de actualizar la pantalla rápidamente y en el momento adecuado!
Interfaz entre código y dispositivo
De todos modos, todos los interesados en Flutter ya han visto la siguiente imagen que describe la arquitectura de alto nivel de Flutter.

Cuando escribimos una aplicación Flutter usando Dart, permanecemos en el nivel de Marco Flutter (resaltado en verde).
El Marco de Flutter interactúa con el Motor de Flutter (en azul) a través de una capa de abstracción llamada Ventana . Este nivel de abstracción proporciona una serie de API para la interacción indirecta con el dispositivo.
También a través de este nivel de abstracción, Flutter Engine notifica a Flutter Framework cuando:
- se produce un evento de interés a nivel del dispositivo (cambio de orientación, cambio de configuración, problema de memoria, estado operativo de la aplicación ...)
- algún evento ocurre a nivel del cristal (= gesto)
- el canal de la plataforma envía algunos datos
- pero también principalmente cuando el Flutter Engine está listo para renderizar un nuevo marco
Administrar el renderizado de Flutter Framework Flutter Engine
Es difícil de creer, pero es verdad. Excepto en algunos casos ( ver más abajo ), no se ejecuta el código de Flutter Framework sin iniciar el renderizado de Flutter Engine .
Excepciones:
- Gesto / Gesto (= evento en vidrio)
- Mensajes de plataforma (= mensajes generados por un dispositivo, como GPS)
- Mensajes del dispositivo (= mensajes relacionados con un cambio en el estado del dispositivo, por ejemplo, orientación, aplicación enviada en segundo plano, alertas de memoria, configuración del dispositivo ...)
- Respuestas futuras o http
(Entre nosotros, puede aplicar un cambio visual sin llamar desde Flutter Engine, pero esto no es recomendable )
Me preguntas: "Si se ejecuta algún tipo de código relacionado con el gesto y causa un cambio visual, o si uso un temporizador para establecer la frecuencia de la tarea que conduce a cambios visuales (por ejemplo, animación), ¿cómo funciona?"
Si desea que se produzca un cambio visual o que se ejecute algún código en función de un temporizador, debe decirle al motor de flutter que debe dibujarse algo.
Por lo general, la próxima vez que se actualice Flutter Engine , llamará a Flutter Framework para que ejecute algún código y, en última instancia, proporcionará una nueva escena para el renderizado.
Por lo tanto, una pregunta importante es cómo el motor Flutter organiza todo el comportamiento de la aplicación en función del renderizado.
Para tener una idea de los mecanismos internos, mire la siguiente animación:

Una breve explicación (más detalles vendrán más adelante):
- Algunos eventos externos (gestos, respuestas http, etc.) o incluso futuros pueden desencadenar tareas que hacen necesario actualizar la pantalla. El mensaje correspondiente se envía al Flutter Engine (= Marco de programación )
- Cuando el motor Flutter está listo para comenzar a actualizar el renderizado, crea una solicitud de inicio de marco
- Esta solicitud de marco de inicio es interceptada por Flutter Framework , que realiza tareas principalmente relacionadas con Tickers (por ejemplo, animación)
- Estas tareas pueden volver a crear la solicitud para su posterior representación (ejemplo: la animación no ha completado su ejecución y, para completarla, necesitará obtener otro Marco de inicio en una etapa posterior)
- A continuación, Flutter Engine envía un cuadro de dibujo , que es interceptado por Flutter Framework , que buscará cualquier tarea relacionada con la actualización del diseño en términos de estructura y tamaño.
- Después de completar todas estas tareas, continúa con las tareas asociadas con la actualización del diseño en términos de representación
- Si hay algo en la pantalla que necesita dibujarse, se envía una nueva escena ( Escena ) para visualización al Flutter Engine , que actualizará la pantalla
- El Flutter Framework realiza todas las tareas que se realizarán después de la representación (= devoluciones de llamada PostFrame), y cualquier otra tarea posterior que no esté relacionada con la representación
- ... y este proceso comienza de nuevo
RenderView y RenderObject
Antes de sumergirse en los detalles del flujo de trabajo, es hora de presentar el concepto del árbol de renderizado .
Como se mencionó anteriormente, todo se convertirá eventualmente en píxeles que se mostrarán en la pantalla, y el Marco Flutter convertirá los Widgets que usamos para desarrollar la aplicación en bloques visuales que se mostrarán en la pantalla.
Estas partes visuales corresponden a objetos llamados RenderObject , que se utilizan para:
- definir un área determinada de la pantalla en términos de tamaño, posición, geometría, así como en términos de "contenido renderizado"
- Identificar áreas de la pantalla que pueden verse afectadas por los gestos (= tocar con el dedo)
Un conjunto de todos los RenderObjects forma un árbol llamado Render Tree . En la parte superior de este árbol (= raíz ) encontramos un RenderView .
RenderView proporciona una superficie común para los objetos de Render Tree y es una versión especial de RenderObject .
Visualmente, podríamos representar todo esto de la siguiente manera:

La relación entre Widget y RenderObject se discutirá más adelante. Mientras tanto, es hora de ir un poco más profundo ...
Enlaces de inicialización
Cuando se inicia la aplicación Flutter, primero se llama a la función main()
, que finalmente llama al runApp(Widget app)
.
Cuando se runApp()
método runApp()
Marco de Flutter inicializa las interfaces entre sí mismo y el Motor de Flutter . Estas interfaces se denominan enlaces ( nota: enlaces ).
Introducción a los enlaces
Los enlaces están diseñados para ser el enlace entre el marco y el motor Flutter. Solo a través de enlaces se pueden intercambiar datos entre Flutter Framework y Flutter Engine .
(Solo hay una excepción a esta regla: RenderView , pero lo discutiremos más adelante).
Cada enlace es responsable de procesar un conjunto de tareas, acciones, eventos específicos, agrupados por área de actividad.
Al momento de escribir este artículo, el Marco Flutter tiene 8 enlaces.
A continuación hay 4 de ellos que serán considerados en este artículo:
- SchedulerBinding
- Gesto vinculante
- Enlace de renderizador
- Encuadernación de widgets
Para completar, mencionaré los 4 restantes:
- ServicesBinding : responsable de procesar los mensajes enviados por el canal de la plataforma
- PaintingBinding : responsable de procesar el caché de imágenes
- Enlace semántico : reservado para la implementación posterior de todo lo relacionado con la semántica
- TestWidgetsFlutterBinding : utilizado por la biblioteca de prueba de widgets
También puede mencionar WidgetsFlutterBinding , pero esto no es realmente un enlace, sino más bien un tipo de "inicializador de enlace" .
El siguiente diagrama muestra la interacción entre los enlaces, que voy a considerar a continuación, y el motor Flutter .

Veamos cada uno de estos enlaces "centrales".
SchedulerBinding
Este enlace tiene dos responsabilidades principales:
- Di Flutter Engine : "¡Hey! La próxima vez que no estés ocupado, despiértame para que pueda trabajar un poco y decirte qué hacer, o si necesito que me llames más tarde ..."
- Escuche y responda a tales "despertares perturbadores" (ver más abajo)
¿Cuándo SchedulerBinding solicita una llamada de atención ?
Cuando Ticker debe resolver un nuevo tic
Por ejemplo, tienes una animación, la comienzas. La animación se recorta utilizando el Ticker , que se llama a intervalos regulares (= tick ) para realizar una devolución de llamada . Para iniciar una devolución de llamada de este tipo , debemos informarle al motor de flutter para que nos despierte durante la próxima actualización (= marco inicial). Esto iniciará la devolución de llamada de ticker para completar su tarea. Si el ticker aún necesita continuar la ejecución, al final de su tarea, llamará a SchedulerBinding para programar otro marco.
Cuándo actualizar la pantalla
Por ejemplo, necesitamos resolver un evento que conduzca a un cambio visual (ejemplo: actualizar el color de una parte de la pantalla, desplazarse, agregar / eliminar algo de la pantalla), para esto debemos seguir los pasos necesarios para mostrar la imagen actualizada en la pantalla. En este caso, cuando se produce dicho cambio, el Marco de Flutter llama a SchedulerBinding para programar otro marco utilizando el Motor de Flutter . (Más adelante veremos cómo funciona esto)
Gesto vinculante
Este enlace escucha la interacción con el motor en términos del "dedo" (= gesto ).
En particular, es responsable de recibir datos relacionados con los dedos y de determinar con qué parte (s) de la pantalla trabajan los gestos. Luego notifica en consecuencia / de estas partes.
Enlace de renderizador
Este enlace es el enlace entre el motor Flutter y el árbol de renderizado . Ella es responsable de:
- escuchar eventos generados por el motor para informar sobre los cambios aplicados por el usuario a través de la configuración del dispositivo que afecta los efectos visuales y / o semánticos
- mensaje al motor sobre los cambios que se aplicarán a la pantalla
Para proporcionar los cambios que se mostrarán en la pantalla, RendererBinding es responsable de administrar PipelineOwner e inicializar RenderView .
PipelineOwner es un tipo de orquesta que sabe lo que se debe hacer con RenderObject de acuerdo con el componente y coordina estas acciones.
Este enlace escucha los cambios aplicados por el usuario a través de la configuración del dispositivo que afecta el idioma (= localidad ) y la semántica .
Pequeña nota
Supongo que en una etapa posterior en el desarrollo de Flutter, todos los eventos relacionados con la semántica se transferirán a SemanticsBinding , pero en el momento de escribir este artículo, este no es el caso.
Además, WidgetsBinding es el enlace entre widgets y Flutter Engine . Ella es responsable de:
- gestión del proceso de procesamiento de cambios en la estructura de widgets
- hacer una llamada
El procesamiento de los cambios en la estructura de los widgets se realiza con BuildOwner .
BuildOwner realiza un seguimiento de qué widgets deben reconstruirse y maneja otras tareas que se aplican a la estructura del widget en su conjunto.
Parte 2. De widgets a píxeles
Ahora que hemos aprendido los conceptos básicos del trabajo interno de Flutter , es hora de hablar sobre widgets.
En toda la documentación de Flutter leerá que todos los Widgets (widgets).
Esto es casi correcto. Pero para ser un poco más preciso, prefiero decir:
Del lado del desarrollador, todo lo relacionado con la interfaz de usuario en términos de diseño e interacción se realiza mediante widgets.
¿Por qué tanta precisión? Además del hecho de que Widget permite al desarrollador determinar parte de la pantalla en términos de tamaño, contenido, diseño e interacción, PERO hay mucho más. Entonces, ¿qué es realmente Widget ?
Configuración inmutable
Si observa el código fuente de Flutter , notará la siguiente definición de la clase Widget .
@immutable abstract class Widget extends DiagnosticableTree { const Widget({ this.key }); final Key key; ... }
¿Qué significa esto?
La anotación "@immutable" es muy importante y nos dice que cualquier variable en la clase Widget debe ser FINAL , en otras palabras: "definida y asignada UNA VEZ PARA TODOS ". Por lo tanto, después de crear una instancia, Widget ya no podrá cambiar sus variables internas.
Como Widget es inmutable, puede considerarse una configuración estática.
La estructura jerárquica de los widgets.
Cuando diseña con Flutter, define la estructura de su (s) pantalla (s) utilizando 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 ejemplo utiliza 7 widgets que juntos forman una estructura jerárquica. Un esquema muy simplificado basado en este código es el siguiente:

Como puede ver, el diagrama presentado se parece a un árbol, donde SafeArea es su raíz.
Bosque detrás de los árboles
Como ya sabe, un widget en sí mismo puede ser una agregación de otros widgets. Como ejemplo, puede modificar el código anterior de la siguiente manera:
Widget build(BuildContext context){ return MyOwnWidget(); }
Esta opción supone que el widget "MyOwnWidget" en sí mostrará SafeArea , Scaffold . Pero lo más importante en este ejemplo es que
Un widget puede representar una hoja, un nudo en un árbol, incluso el árbol mismo o, por qué no, un bosque de árboles ...
Elemento de comprensión en un árbol
¿Qué tiene esto que ver con eso?
Como se mostrará más adelante, para poder generar píxeles que componen la imagen que se muestra en el dispositivo, Flutter debe conocer en detalle todas las partes pequeñas que componen la pantalla, y para determinar todas las partes, necesita conocer la expansión de todos los widgets.
Para ilustrar este punto, considere el principio de una muñeca anidada: cuando está cerrado, solo ve 1 muñeca, pero contiene otra, que a su vez contiene otra y así sucesivamente ...

Cuando Flutter expande todos los widgets (parte de la pantalla) , será como obtener todas las muñecas (parte del todo) .
La siguiente imagen muestra parte de la estructura jerárquica final de los widgets correspondientes al código anterior. En amarillo, destaqué los widgets que se mencionaron en el código anteriormente, para que pueda definirlos en el árbol final.

Aclaraciones importantes
El lenguaje "Árbol de widgets" existe solo para facilitar la comprensión, ya que los programadores usan widgets, ¡pero NO hay un árbol de widgets en Flutter!
De hecho, sería más correcto decir "árbol de elementos"
Es hora de presentar el concepto de un elemento .
Cada widget tiene un elemento. Los elementos están conectados entre sí y forman un árbol. Por lo tanto, un elemento es una referencia a algo en el árbol.
Para empezar, piense en un elemento como un nodo que tiene un padre y posiblemente un hijo. Al vincularlos a través de una relación padre-hijo , obtenemos una estructura de árbol.

Como puede ver, el elemento apunta a un widget y también puede apuntar a un RenderObject .
Aún mejor ... ¡Element señala a Widget que creó este Element!
Resumamos:
- No hay un árbol de widgets, pero hay un árbol de elementos.
- Los elementos son creados por widgets.
- El elemento se refiere al widget que lo creó.
- Elementos vinculados a las relaciones entre padres
- Un artículo puede tener un "bebé".
- Los elementos también pueden apuntar a un RenderObject.
Los elementos determinan cómo las partes de los bloques mostrados están relacionadas entre sí.
Para imaginar mejor dónde encaja el concepto de un elemento , veamos la siguiente representación visual:

Como puede ver, el árbol de elementos es la relación real entre widgets y RenderObjects .
Pero, ¿por qué Widget crea un elemento ?
3 categorías de widgets
En Flutter, los widgets se dividen en 3 categorías, personalmente los llamo de la siguiente manera (pero esta es solo mi forma de clasificarlos) :
Proxy
La tarea principal de estos widgets es almacenar cierta información (que debería ser accesible para los widgets), parte de la estructura de árbol basada en Proxy. Un ejemplo de tales widgets es InheritedWidget o LayoutId .
Estos widgets no participan directamente en la formación de la interfaz de usuario, pero se utilizan para obtener la información que pueden proporcionar.
Renderizador
Estos widgets están directamente relacionados con el diseño de la pantalla, ya que determinan (o se utilizan para determinar) el tamaño , la posición y la representación . Los ejemplos típicos son: Fila , Columna , Pila , así como Relleno , Alinear , Opacidad , RawImage ...
Componente
Estos son otros widgets que proporcionan directamente no la información final relacionada con los tamaños, las posiciones, la apariencia, sino los datos (o consejos) que se utilizarán para obtener la información final. Estos widgets se conocen comúnmente como componentes.
Ejemplos: RaisedButton , Scaffold , Text , GestureDetector , Container ...

Este archivo PDF enumera la mayoría de los widgets agrupados por categoría.
¿Por qué es importante esta separación? Porque dependiendo de la categoría del widget, el tipo de elemento correspondiente está asociado con ...
Tipos de elementos
Hay varios tipos de elementos:

Como puede ver en la imagen de arriba, los elementos se dividen en 2 tipos principales:
Genial Tanta información, pero ¿cómo se relaciona todo esto entre sí y por qué es interesante hablar de ello?
Cómo funcionan juntos los widgets y los elementos
En Flutter, toda la mecánica se basa en invalidar un elemento o renderObject.
La invalidación de elementos se puede hacer de las siguientes maneras:
- usando
setState
, que invalida todo el StatefulElement (tenga en cuenta que intencionalmente no digo StatefulWidget ) - mediante notificaciones procesadas por proxyElement (por ejemplo, InheritedWidget), que invalida cualquier elemento que dependa de este proxyElement
El resultado de la invalidación es que aparece un enlace al elemento correspondiente en la lista de elementos sucios .
La invalidación de renderObject significa que la estructura de los elementos no cambia en absoluto, pero hay un cambio en el nivel de renderObject , por ejemplo:
- cambiando su tamaño, posición, geometría ...
- algo necesita ser repintado, por ejemplo, cuando solo cambia el color de fondo, el estilo de fuente ...
El resultado de dicha invalidación es un enlace al renderObject correspondiente en la lista de objetos de renderizado (renderObjects) que necesitan ser reconstruidos o repintados.
Independientemente del tipo de invalidación, se llama a SchedulerBinding (¿recuerda esto?) Para solicitar al Flutter Engine que programe un nuevo marco.
Este es exactamente el momento en que el motor Flutter "despierta" el SchedulerBinding y sucede toda la magia ...
onDrawFrame ()
Anteriormente en este artículo, notamos que SchedulerBinding tiene dos responsabilidades principales, una de las cuales es la disposición a manejar las solicitudes hechas por Flutter Engine relacionadas con la reconstrucción de marcos. Este es el momento perfecto para enfocarse en esto.
El siguiente diagrama de secuencia parcial muestra lo que sucede cuando SchedulerBinding recibe una solicitud onDrawFrame () de Flutter Engine .

Paso 1. Elementos
Se llama WidgetsBinding , y este enlace primero considera los cambios asociados con los elementos. WidgetsBinding llama al método buildScope del objeto buildOwner , ya que BuildOwner es responsable de procesar el árbol de elementos. Este método revisa la lista de elementos sucios y solicita su reconstrucción .
Los principios principales de este método de rebuild()
) son:
- Hay una solicitud para reconstruir el elemento (esto llevará la mayor parte del tiempo), llamando al método
build()
del widget al que se refiere este elemento build()
método = = Widget build (BuildContext context) {...}
). Este método build()
devolverá un nuevo widget - Si el elemento no tiene "hijos", se crea un elemento para el nuevo widget (ver más abajo) ( nota: inflateWidget ), de lo contrario
- el nuevo widget se compara con el referenciado por el elemento secundario del elemento
- Si son intercambiables (= el mismo tipo de widget y clave ), se produce la actualización y se guarda el elemento secundario.
- Si no son intercambiables, el niño se descarta ( ~ se descarta ) y se crea un elemento para el nuevo widget
- Este nuevo elemento se monta como elemento secundario del elemento. ( montado) = insertado en el árbol de elementos)
La siguiente animación intentará hacer esta explicación un poco más clara.

Nota sobre widgets y elementos
Para un nuevo widget, se crea un elemento de un tipo específico que corresponde a la categoría del widget, a saber:
- InheritedWidget -> InheritedElement
- StatefulWidget -> StatefulElement
- StatelessWidget -> StatelessElement
- InheritedModel -> InheritedModelElement
- InheritedNotifier -> InheritedNotifierElement
- LeafRenderObjectWidget -> LeafRenderObjectElement
- SingleChildRenderObjectWidget -> SingleChildRenderObjectElement
- MultiChildRenderObjectWidget -> MultiChildRenderObjectElement
- ParentDataWidget -> ParentDataElement
Cada uno de estos tipos de elementos tiene su propio comportamiento. Por ejemplo:
- StatefulElement llamará al método
widget.createState()
en la inicialización, lo que creará un estado y lo asociará con el elemento - Cuando se monta un elemento de tipo RenderObjectElement , se crea un RenderObject . Este renderObject se agregará al Render Tree y se asociará con el elemento.
Paso 2. renderObjects
Ahora, después de completar todas las acciones asociadas con elementos sucios , el Árbol de elementos es estable. Es hora de considerar el proceso de visualización.
Dado que RendererBinding es responsable de renderizar el Render Tree , WidgetsBinding llama al método drawFrame
RendererBinding .
El siguiente diagrama parcial muestra la secuencia de acciones realizadas durante la solicitud drawFrame () .

En este paso, se realizan las siguientes acciones:
- Cada renderObject marcado como sucio debe componerlo (es decir, calcular su tamaño y geometría)
- Cada renderObject marcado como "necesita volver a dibujar" se vuelve a dibujar utilizando su propio método de capa
- La escena resultante se forma y se envía al Flutter Engine , para que este último lo transfiera a la pantalla del dispositivo.
- Finalmente, la semántica también se actualiza y se envía al Flutter Engine.
Al final de este flujo de trabajo, la pantalla del dispositivo se actualiza.
Parte 3: gestos de manejo
Los gestos (= eventos relacionados con las acciones de los dedos en el cristal ) se procesan utilizando GestureBinding .
Cuando Flutter Engine envía información sobre un evento de gesto a través de la API window.onPointerDataPacket , GestureBinding lo intercepta, realiza un almacenamiento en búfer y:
- convierte las coordenadas proporcionadas por Flutter Engine para que coincidan con la relación de píxeles del dispositivo , y luego
- recupera de render Vea una lista de todos los RenderObjects que están en la parte de la pantalla relacionada con las coordenadas del evento
- luego recorre la lista resultante de renderObjects y envía un evento relacionado a cada uno de ellos
- si renderObject "escucha" eventos de este tipo, lo procesa
Con suerte, ahora entiendo lo importante que es renderObjects .
Parte 4: animaciones
Esta parte del artículo trata sobre el concepto de animación y una comprensión profunda de Ticker .
Cuando trabaja con animaciones, generalmente usa un AnimationController o cualquier widget para animaciones ( nota: AnimatedCrossFade ).
En Flutter, todo lo relacionado con animaciones se refiere a Ticker . Ticker , cuando está activo, solo tiene una tarea: "le pide a SchedulerBinding que registre una devolución de llamada y le dice al Flutter Engine que la active cuando aparezca una nueva devolución de llamada". Cuando Flutter Engine está listo, llama a SchedulerBinding a través de una solicitud: " onBeginFrame ". SchedulerBinding accede a la lista de devolución de llamada de ticker y ejecuta cada una.
Cada tic es interceptado por un controlador "interesado" para procesarlo. Si la animación está completa, el ticker está "deshabilitado", de lo contrario, el ticker solicita un SchedulerBinding para programar una nueva devolución de llamada. Y así sucesivamente ...
Imagen completa
Ahora hemos aprendido cómo funciona Flutter :

Buildcontext
Finalmente, regrese al diagrama que muestra los diferentes tipos de elementos y considere la firma del Elemento raíz:
abstract class Element extends DiagnosticableTree implements BuildContext { ... }
¡Vemos el famoso BuildContext ! Pero que es eso?
BuildContext es una interfaz que define una serie de captadores y métodos que un elemento puede implementar. Mayormente BuildContext se usa en el método build()
de StatelessWidget o State for StatefulWidget .
BuildContext no es más que el Elemento en sí, que coincide
- widget que se actualiza (dentro de los métodos de
build
o builder
) - StatefulWidget asociado con el estado en el que hace referencia a la variable de contexto.
Esto significa que la mayoría de los desarrolladores trabajan constantemente con elementos sin siquiera saberlo.
¿Qué tan útil puede ser un 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()
.
Conclusión
: " ". , , Flutter , , , , . , , Widget , Element , BuildContext , RenderObject , . , .
. .
PS , () .
PSS Flutter internals Didier Boelens, )