The Feathers UI component lifecycle
To develop custom UI components for Feathers UI, you should understand the basics of the Feathers UI component lifecycle. A component goes through a number of distinct stages between when it is created and when it is destroyed.
Instantiation / Constructor
A UI component instance is created with the new
keyword, and its constructor will be called.
var instance = new MyComponent();
Generally, code in a UI component's constructor should be kept to a minimum. It's okay to add event listeners and set some properties, but save more expensive things (such as the creation of children) until initialization.
public function new() {
super();
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
After the constructor returns, the UI component has not yet been added to the display list, and it is not completely initialized. However, the UI component's public properties may be changed, and their values will be stored for later.
Initialization
When a UI component is added to the display list for the first time, its initialize()
method will be called.
To force a UI component to initialize before adding it to the display list, you may call
initializeNow()
.
The initialize()
method is intended for any code that should run only one time before a UI component is rendered. Most often, initialize()
is used to create the UI component's children.
private var myChild:Button;
override private function initialize() {
super.initialize();
myChild = new Button();
addChild(myChild);
}
After initialize()
returns, the UI component will dispatch FeathersEvent.INITIALIZE
.
Invalidation
Any time one of a UI component's properties is changed, it should call the setInvalid()
method.
public var someProperty(default, set):Bool = false;
private function set_someProperty(value:Bool):Bool {
if (someProperty == value) {
return someProperty;
}
someProperty = value;
setInvalid(DATA);
return someProperty
}
Calls to setInvalid()
may optionally include an InvalidationFlag
to clarify exactly what is invalid. When the UI component updates, it can use the flags to determine which code needs to be run. In other words, it may be able to skip some of the update code if certain properties have not been changed.
The exact meaning of each invalidation flag is not enforced in any way, and UI component developers may choose to interpret each flag differently. These flags are meant to be an internal implementation detail and not something that should be exposed as a public API. Ideally, only developers creating custom UI Components will ever need to call setInvalid()
and use invalidation flags.
After being set invalid, the UI component will wait to update its layout and appearance until just before it is rendered by OpenFL. This allows multiple properties to be changed at once, and the changes will be batched in a single update, which improves performance.
Update
Before a UI component is rendered to the screen, it will update its layout and appearance.
To force a UI component to update at any time, you may call
validateNow()
.
When a component validates, its update()
method is called. In update()
, a UI component will generally handle any changes that have been made to its properties, calculate its ideal width and height, and update its internal layout.
override private function update():Void {
var dataInvalid = isInvalid(DATA);
var stylesInvalid = isInvalid(STYLES);
if (dataInvalid) {
// do something if InvalidationFlag.DATA is set
}
if (stylesInvalid) {
// do something if InvalidationFlag.STYLES is set
}
if (dataInvalid || stylesInvalid) {
// do something if either flag is set
}
// let the superclass finish updating
super.update();
}
A UI component will typically make one or more calls to isInvalid()
to determine what it needs to update.
Creation complete
After a UI component updates for the first time, it will dispatch FeathersEvent.CREATION_COMPLETE
. This event will be dispatched just one time in a UI component's lifecycle.
Render
All UI components extend the openfl.display.Sprite
class, and rendering is handled by OpenFL.
Invalidation -> Update -> Render
The process of 1) invalidation, 2) update, and 3) rendering will repeat indefinitely until the component is removed from the display list.
Removal from display list
When a UI component is removed from the display list, it will no longer update automatically. Any additional property changes will be stored, but they will not be committed unless the UI component is added to the display list again.
Disposal
All UI components have a dispose()
function that may be used to clean things up when a UI component will no longer be used in the app anymore. Disposal can perform actions like clearing data providers, removing and disposing children, unloading assets, and removing event listeners.
Disposal is considered optional, but should be considered a good practice as part of maintaining a well-optimized codebase. The garbage collector can certainly handle an undisposed UI component with no more references in other code. However, disposing it may make the GC's job a bit easier and faster, with fewer strong references to traverse.
Once a UI component is disposed, it should not be added to the display list again or validated again. This may result in runtime exceptions or undefined behavior. Additionally, disposed components may be completely unresponsive to user interaction, such as mouse, touch, or keyboard.
Garbage collection
After all references to the UI component instance have been removed (for instance, by setting variables to null
), it becomes eligible for garbage collection. The component's lifecycle has completed.