Web Components
Spark is built around the concept of Web Components. Components are reusable, encapsulated units of UI that can be rendered on both the server and the client.
Creating a Component
Extend the `WebComponent` class to create a new component.
import 'package:spark/spark.dart';
@Component(tag: 'my-counter')
class Counter extends WebComponent {
static const tag = 'my-counter';
final int start;
Counter({this.start = 0});
@override
String get tagName => tag;
@override
List<String> get observedAttributes => ['start'];
// Note: render method is now an instance method
@override
Element render() {
return element(
tag,
[
template(shadowrootmode: 'open', [
style(['button { padding: 8px 16px; }']),
button(id: 'btn', [
'Count: ',
span(id: 'val', [start]),
]),
]),
],
attributes: {'start': start},
);
}
@override
void onMount() {
final btn = query('#btn');
if (btn == null) return;
int count = propInt('start', 0);
btn.on('click', (e) {
count++;
// Updates the attribute on the DOM element
setAttr('start', count.toString());
});
}
@override
void attributeChangedCallback(String name, String? oldValue, String? newValue) {
if (name == 'start') {
final val = query('#val');
val?.innerText = newValue ?? '0';
}
}
}Reactivity
Components can react to attribute changes using `observedAttributes` and `attributeChangedCallback`.
- `observedAttributes`: A list of attribute names to watch for changes.
- `attributeChangedCallback`: Called whenever an observed attribute changes.
Attribute Methods
WebComponent provides methods to manage attributes on the host element:
- `setAttr(name, value)`: Sets an attribute.
- `removeAttr(name)`: Removes an attribute.
- `toggleAttr(name)`: Toggles a boolean attribute.
- `hasAttr(name)`: Checks if an attribute exists.
Declarative Shadow DOM
Spark uses Declarative Shadow DOM (`<template shadowrootmode="open">`) to send the component's shadow DOM from the server. This ensures that styles are encapsulated and content is visible immediately without waiting for JavaScript.
Property Helpers
WebComponent provides typed helpers for reading attributes from the element:
// String property with fallback
final name = prop('name', 'default');
// Integer property with fallback
final count = propInt('count', 0);
// Boolean property with fallback
final enabled = propBool('enabled', false);
// Double property with fallback
final ratio = propDouble('ratio', 1.0);