Skip to content

Widget ​

Fondamentaux

Engine\Native\Widget n'est pas un widget qu'on instancie : c'est l'interface abstraite que tous les widgets du moteur natif implémentent — Container, Flex, Button, CustomPaint, absolument tous. Elle définit deux passes, exactement le contrat du RenderObject de Flutter : layout() est une négociation descendante (le parent propose une boîte de Constraints, l'enfant renvoie le Size qu'il a choisi — jamais l'inverse, ce qui garde le layout en une seule passe au lieu d'une boucle de reflow façon navigateur) ; paint() est une seconde passe descendante qui transforme ce que layout() a décidé en commandes de dessin à coordonnées absolues, empilées dans un Canvas.

Le contrat ​

php
<?php

namespace Engine\Native;

interface Widget
{
    public function layout(Constraints $constraints): Size;

    public function paint(Canvas $canvas, float $x, float $y): void;
}

layout() est toujours appelé avant paint(), jamais l'inverse : un nœud ne peut pas se peindre lui-même sans connaître d'abord sa taille finale, et cette taille doit tenir dans les Constraints reçues — c'est un contrat, pas une suggestion.

Exemple : implémenter son propre Widget ​

php
<?php

use Engine\Native\Widget;
use Engine\Native\Constraints;
use Engine\Native\Size;
use Engine\Native\Canvas;

/**
 * Un petit pastille de statut carrée, dessinée à la main —
 * un exemple minimal de ce que chaque widget du moteur fait déjà
 * en interne pour implémenter le contrat Widget.
 */
final class StatusDot implements Widget
{
    private Size $size;

    public function __construct(
        private readonly float $diameter,
        private readonly string $color,
    ) {
        $this->size = Size::zero();
    }

    public function layout(Constraints $constraints): Size
    {
        $this->size = $constraints->constrain(new Size($this->diameter, $this->diameter));

        return $this->size;
    }

    public function paint(Canvas $canvas, float $x, float $y): void
    {
        $radius = $this->size->width / 2;
        $canvas->circle($x + $radius, $y + $radius, $radius, $this->color);
    }
}

StatusDot respecte le contrat à la lettre : layout() se contente de contraindre sa taille intrinsèque (le diamètre demandé) dans la boîte imposée par le parent, et paint() traduit cette taille finale en une seule commande Canvas::circle() à coordonnées absolues. Une fois cette classe écrite, elle est utilisable partout où un Widget est attendu — comme enfant d'un Flex, d'un Stack, etc.

À savoir. Un Widget personnalisé n'a besoin d'aucune classe de base ni d'enregistrement particulier — implements Widget suffit, exactement comme CircularProgress ou CustomPaint le font déjà dans le cœur du framework.

Voir aussi ​

PhpNitro est un produit FINANFA TECH — Publié sous licence MIT.