build method

  1. @override
void build()
override

Declares this node's children and behavior.

Runs once on mount, and again whenever the code reassembles.

super.build() is required, as skipping it drops whatever the superclass declared.

Implementation

@override
void build() {
  super.build();

  tick((dt) {
    final state = _current;
    if (state.isFinished) return;
    final amount = dt * speed;
    if (amount <= 0 || !amount.isFinite) return;

    state._elapsed += amount;

    // Every pass re-reads the state, so a handler calling play() redirects
    // this loop instead of racing it.
    while (true) {
      final duration = state._entry.duration(state.frame);

      if (duration <= 0 || !duration.isFinite) {
        state._elapsed = 0;
        return;
      }

      if (state._elapsed < duration) return;
      state._elapsed -= duration;
      final next = state.frame + 1;

      if (next < state._entry.frames) {
        state._seek(next);
      } else if (state.loops) {
        state._seek(0);
        onLoop.emit();
      } else {
        state._elapsed = 0;
        state._finished = true;
        onFinish.emit();

        if (cleanup) {
          detach();
        }

        return;
      }
    }
  });

  void painter(Canvas canvas, Paint paint) {
    final state = _current;

    canvas.drawImageRect(
      state._entry.image,
      state._source ??= state._entry.rect(state.frame),
      state._destination ??= .fromLTWH(0, 0, width, height),
      paint,
    );
  }

  draw((canvas) {
    palette.draw(canvas, painter);
  });

  Ignis.cache.onChanged(() {
    _sprite = _sprite.reload();

    // Follow the entry by name. A dropped entry falls back to the first; a
    // shorter one starts over.
    final entry = _sprite.resolve(_current.key);

    if (entry == null) {
      _current._select(_sprite.entries.first, 0);
      return;
    }

    final frame = _current.frame;
    _current._select(entry, frame < entry.frames ? frame : 0);
  });
}