Closure-callbacks
Bottom line: onAnimationEnd: widget.onComplete is the preferred and most efficient when the API accepts VoidCallback?.
() => widget.onComplete?.call()
Behavior:
- creates a new closure that, when run, checks and invokes
onCompleteif non-null. widget.onCompletejust passes the function reference (or null) through.
- creates a new closure that, when run, checks and invokes
Allocation:
- The lambda form allocates a new closure every build.
- Passing
widget.onCompletedoes not allocate anything new.
Null semantics:
With the lambda,
onAnimationEndis always non-null (a no-op ifonCompleteis null).Passing
widget.onCompletepreserves null vs non-null, which some widgets use to change behavior/optimizations.
- Prefer:
onAnimationEnd: widget.onCompletewhen the parameter type isVoidCallback?. - It’s simpler, avoids an extra allocation, preserves intended semantics, and satisfies the Dart lint “unnecessary_lambdas”.
- Use a wrapper only if you need extra logic (e.g., chaining, arguments, logging):
onAnimationEnd: () { widget.onComplete?.call(); doSomethingElse(); }
If the target parameter is non-nullable
- If
onAnimationEndisVoidCallback(non-nullable) butonCompleteis nullable, use a no-op fallback: onAnimationEnd: widget.onComplete ?? _noop- Define
_nooponce to avoid per-build allocations:void _noop() {}(top-level orstatic void _noop() {}). - Avoid
() => widget.onComplete?.call()in this case unless you need the extra logic; it allocates a closure every build.