CustomWidget is the main escape hatch when the built-in widget set and higher-level helpers are not enough.
It lets you attach a custom lowerer, and optionally a custom render object, to a Widget in the tree. That means you can emit your own intermediate representation subtree, participate in hit-testing and event handling, and add custom painting behavior.
Most app authors should not start here. If ordinary layout widgets solve the problem, use them. If you only need lightweight custom drawing, use canvas(...). Reach for CustomWidget only when you truly need a new kind of node behavior.
Example
use std::sync::Arc;
use fission::prelude::*;
let widget: Widget = CustomWidget {
debug_tag: "TimelineSurface".into(),
lowerer: Some(Arc::new(MyTimelineLowerer)),
render_object: None,
}.into();
In this example, MyTimelineLowerer would implement Fission's internal lowering trait and emit the subtree needed for the custom surface.
Field table
| | | |
|---|
| | Human-readable label for the custom node. | Useful when inspecting output or debugging. |
| Option<Arc<dyn InternalLowerer>> | Object-safe lowering implementation that emits the intermediate representation subtree. | Required in practice. Lowering panics if no lowerer is set. |
| Option<Arc<dyn CustomRenderObject>> | Optional runtime object for custom hit-testing, event handling, input method editor handling, and painting. | None means the node is lowering-only. |
What problems it solves
CustomWidget exists for the cases where app or library authors need capabilities the normal widget catalog does not expose directly. Examples include advanced text surfaces, bespoke chart primitives, or domain-specific interactive canvases.
The split between lowerer and render_object matters. The lowerer describes what nodes should exist in the intermediate representation (IR). The render object is only necessary when the node also needs runtime behavior such as custom hit-testing or painting.
When not to use it
Do not use CustomWidget just to avoid writing a normal widget. A named widget that returns ordinary Row, Column, Container, Text, and related nodes will usually be easier to maintain, easier to teach, and easier to test.
Also do not use CustomWidget when CanvasLowerer or canvas(...) already fits the problem. Those helpers are narrower, which is usually a good thing.
Specific advice
Keep the custom surface boundary small. The more of your app that depends on CustomWidget, the harder it becomes for teammates to reason about ordinary layout, semantics, and test behavior.
If you add a render_object, document its semantics and event model for your team. Once you leave the standard widget path, behavior becomes less obvious to future readers.
Production checklist
For CustomWidget, review the fields that change behavior before treating the widget as finished: debug_tag, lowerer, render_object. The goal is to make the product rule visible in state and actions, not hidden inside ad-hoc construction code.
If this widget appears inside an interactive flow, keep the surrounding action binding in the parent component and test that the flow still has one clear reducer path.
Check the semantics tree for the user-facing label or role that makes this widget understandable without relying only on pixels.
Add at least one component or harness test that confirms the visible text, semantic role, action dispatch, and layout constraint that matter for this widget in context.
If a screen starts repeating the same CustomWidget setup, extract a named component around this widget. That keeps the reference API small while making product code easier to read and safer for generated code to copy.
canvas(...), CanvasLowerer, and Composite.