flet-box 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/bin/cli.js +15 -0
- package/bin/commands/checkApi.js +761 -0
- package/bin/commands/create.js +77 -5
- package/bin/utils/dtsProps.js +191 -0
- package/bin/utils/moduleExports.js +165 -0
- package/bin/utils/neutralise.js +64 -0
- package/bin/utils/templates.js +366 -27
- package/docs/CONTRIBUTING.md +156 -0
- package/docs/README.md +68 -0
- package/docs/arquitectura.md +164 -0
- package/docs/cli/README.md +542 -0
- package/docs/guides/README.md +33 -0
- package/docs/guides/app-templates.md +197 -0
- package/docs/guides/compatibility.md +95 -0
- package/docs/guides/embedding.md +284 -0
- package/docs/guides/first-app.md +208 -0
- package/docs/guides/frontend-services.md +171 -0
- package/docs/guides/minification-and-protection.md +260 -0
- package/docs/guides/mobile-and-platforms.md +439 -0
- package/docs/guides/online-updates.md +196 -0
- package/docs/guides/router.md +282 -0
- package/docs/guides/state.md +283 -0
- package/docs/guides/syntax-philosophy.md +150 -0
- package/docs/guides/utilities.md +241 -0
- package/docs/guides/widget-structure.md +163 -0
- package/docs/server/README.md +68 -0
- package/docs/server/START_HERE.md +121 -0
- package/docs/server/api.md +225 -0
- package/docs/server/data-services.md +300 -0
- package/docs/server/deployment.md +113 -0
- package/docs/server/security.md +196 -0
- package/docs/services/README.md +79 -0
- package/docs/services/RamStore.md +84 -0
- package/docs/services/Session.md +84 -0
- package/docs/services/Storage.md +88 -0
- package/docs/services/http.md +86 -0
- package/docs/tools/README.md +138 -0
- package/docs/tools/animation.md +87 -0
- package/docs/tools/async-ids-log.md +80 -0
- package/docs/tools/device.md +132 -0
- package/docs/tools/introspection.md +67 -0
- package/docs/tools/lists.md +96 -0
- package/docs/tools/markdown.md +79 -0
- package/docs/tools/reactivity.md +288 -0
- package/docs/tools/state.md +103 -0
- package/docs/tools/styling.md +92 -0
- package/docs/tools/text-and-time.md +89 -0
- package/docs/tools/theme.md +102 -0
- package/docs/tools/validation.md +75 -0
- package/docs/tools/visual-effects.md +84 -0
- package/docs/widget/Accordion.md +144 -0
- package/docs/widget/AdMob.md +144 -0
- package/docs/widget/AdSense.md +133 -0
- package/docs/widget/AdaptiveScaffold.md +105 -0
- package/docs/widget/AlertDialog.md +103 -0
- package/docs/widget/AnimatedBox.md +130 -0
- package/docs/widget/AnimatedText.md +123 -0
- package/docs/widget/AppBar.md +118 -0
- package/docs/widget/Audio.md +135 -0
- package/docs/widget/Avatar.md +119 -0
- package/docs/widget/Badge.md +128 -0
- package/docs/widget/BottomNavigation.md +116 -0
- package/docs/widget/BottomSheet.md +125 -0
- package/docs/widget/Button.md +137 -0
- package/docs/widget/COMMON_PROPS.md +150 -0
- package/docs/widget/Card.md +125 -0
- package/docs/widget/Carousel.md +151 -0
- package/docs/widget/Chart.md +151 -0
- package/docs/widget/Checkbox.md +118 -0
- package/docs/widget/Chip.md +126 -0
- package/docs/widget/CircularBar.md +168 -0
- package/docs/widget/CircularChart.md +164 -0
- package/docs/widget/CodeViewer.md +130 -0
- package/docs/widget/CollapsibleSideBar.md +112 -0
- package/docs/widget/Column.md +129 -0
- package/docs/widget/Container.md +136 -0
- package/docs/widget/DataTable.md +150 -0
- package/docs/widget/Divider.md +109 -0
- package/docs/widget/DraggBox.md +129 -0
- package/docs/widget/Drawer.md +135 -0
- package/docs/widget/DrawerItem.md +98 -0
- package/docs/widget/Dropdown.md +139 -0
- package/docs/widget/DroppBox.md +152 -0
- package/docs/widget/FloatingActionButton.md +120 -0
- package/docs/widget/GridView.md +140 -0
- package/docs/widget/Icon.md +117 -0
- package/docs/widget/Image.md +125 -0
- package/docs/widget/Input.md +151 -0
- package/docs/widget/Inspector.md +92 -0
- package/docs/widget/InstallButton.md +128 -0
- package/docs/widget/ListTile.md +132 -0
- package/docs/widget/ListView.md +138 -0
- package/docs/widget/Markdown.md +175 -0
- package/docs/widget/MatrixRain.md +122 -0
- package/docs/widget/Modal.md +129 -0
- package/docs/widget/Pagination.md +135 -0
- package/docs/widget/ParallaxBox.md +128 -0
- package/docs/widget/ProgressBar.md +130 -0
- package/docs/widget/QRCode.md +117 -0
- package/docs/widget/README.md +188 -0
- package/docs/widget/Radio.md +123 -0
- package/docs/widget/Rating.md +131 -0
- package/docs/widget/Row.md +131 -0
- package/docs/widget/START_HERE.md +146 -0
- package/docs/widget/Scaffold.md +152 -0
- package/docs/widget/Skeleton.md +130 -0
- package/docs/widget/Slider.md +146 -0
- package/docs/widget/SnackBar.md +102 -0
- package/docs/widget/Stack.md +128 -0
- package/docs/widget/Stepper.md +145 -0
- package/docs/widget/Switch.md +118 -0
- package/docs/widget/Tabs.md +124 -0
- package/docs/widget/Text.md +133 -0
- package/docs/widget/Tooltip.md +145 -0
- package/docs/widget/TreeView.md +176 -0
- package/docs/widget/Video.md +127 -0
- package/docs/widget/_template.md +61 -0
- package/package.json +3 -2
- package/src/animations/animate.js +180 -70
- package/src/animations/index.js +1 -0
- package/src/create-index.sh +1 -1
- package/src/index.d.ts +45 -22
- package/src/index.js +1 -0
- package/src/navigations/AdaptiveScaffold.js +11 -2
- package/src/navigations/BottomNavigation.js +10 -3
- package/src/navigations/Tabs.js +6 -2
- package/src/tools/useState.js +54 -63
- package/src/widget-factory/assignProps.js +23 -1
- package/src/widget-factory/processProps.js +10 -3
- package/src/widget-factory/reactiveValue.js +43 -18
- package/src/widget-factory/translateProps.js +777 -901
- package/src/widgets/AlertDialog.js +7 -5
- package/src/widgets/Checkbox.js +9 -7
- package/src/widgets/CircularBar.js +12 -1
- package/src/widgets/FloatingActionButton.js +4 -7
- package/src/widgets/Image.js +5 -7
- package/src/widgets/Input.js +92 -0
- package/src/widgets/ListView.js +73 -29
- package/src/widgets/Pagination.js +6 -3
- package/src/widgets/ProgressBar.js +16 -13
- package/src/widgets/Radio.js +6 -7
- package/src/widgets/Slider.js +23 -4
- package/src/widgets/Stack.js +36 -3
- package/src/widgets/Stepper.js +4 -3
- package/src/widgets/Switch.js +3 -4
- package/src/widgets/TreeView.js +0 -1
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# Reactivity in FletBox
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Reactivity in FletBox means **a widget changes when something else changes** — a state
|
|
6
|
+
value, the theme, the route, or a prop you assign at runtime. There is no Virtual DOM
|
|
7
|
+
layer: each mechanism below writes to a real element in place, and the most efficient one
|
|
8
|
+
updates a single widget without re-running any component code.
|
|
9
|
+
|
|
10
|
+
This page is the **catalogue** of every reactivity path the framework ships. There is no single magic system; there are complementary layers, from the in-memory store up to per-widget DOM accessors.
|
|
11
|
+
|
|
12
|
+
## The catalogue at a glance
|
|
13
|
+
|
|
14
|
+
| # | Mechanism | Produced by | Scope of the update |
|
|
15
|
+
| --- | --- | --- | --- |
|
|
16
|
+
| 1 | Key-based shared state | `useState(key, value)` | Bound widgets patched; app render only when needed |
|
|
17
|
+
| 2 | Low-level store | `subscribeRam` / `saveRam` | Any code subscribed to RamStore keys |
|
|
18
|
+
| 3 | Registered-widget updates | `useState(key, value, widget, propName)` | A single widget prop |
|
|
19
|
+
| 4 | Reactive property setters | `widget.prop = value` | A single widget, no global render |
|
|
20
|
+
| 5 | Imperative updates | `widget.update(props)` | One widget's merged props |
|
|
21
|
+
| 6 | Live handles | `ref()` | The bound widget (methods + props) |
|
|
22
|
+
| 7 | Side-effect watchers | `useWatchState(key, cb)` | A callback, no widget |
|
|
23
|
+
| 8 | Theme reactivity | `subscribeTheme(cb)` / `watchSystemTheme()` | Theme subscribers |
|
|
24
|
+
| 9 | Router reactivity | `subscribe(cb)` (Router) | Route subscribers (Scaffold, nav bars) |
|
|
25
|
+
| 10 | Reactive prop values | `_isReactive` objects/functions | Resolved at apply time **and bound to the key** |
|
|
26
|
+
|
|
27
|
+
## 1. Key-based shared state — `useState`
|
|
28
|
+
|
|
29
|
+
The public state hook. Values live in the in-memory store keyed by a string and survive re-renders.
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
const [count, setCount, subscribe] = useState("counter", 0);
|
|
33
|
+
setCount((previous) => previous + 1);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`setState` writes to RamStore. One shared dispatcher then, in order: patches every
|
|
37
|
+
widget bound to that key, runs the `subscribe` / `useWatchState` callbacks for the key,
|
|
38
|
+
and **only if necessary** schedules an app render (debounced, so several changes made in
|
|
39
|
+
the same tick cost one render). See **The surgical decision**, further down this page.
|
|
40
|
+
|
|
41
|
+
Read the full rules in [State, memo & refs](state.md) and the [State guide](../guides/state.md).
|
|
42
|
+
|
|
43
|
+
## 2. The low-level store — `RamStore`
|
|
44
|
+
|
|
45
|
+
The raw reactive map behind `useState`. You can talk to it directly when you want one store shared by several subsystems.
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
import { saveRam, getRam, subscribeRam } from "flet-box";
|
|
49
|
+
|
|
50
|
+
subscribeRam((key, newValue, oldValue) => {
|
|
51
|
+
if (key === "user") renderProfile(newValue);
|
|
52
|
+
});
|
|
53
|
+
saveRam("user", { name: "Ada" });
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`saveRam`, `updateRam`, and `deleteRam` all notify subscribers with `(key, newValue, oldValue)`; `clearAllRam` notifies with `null`s.
|
|
57
|
+
|
|
58
|
+
## 3. Registered-widget updates — `useState(key, value, widget, propName)`
|
|
59
|
+
|
|
60
|
+
The optional `widget` and `propName` arguments register a specific widget so a state change pushes the new value straight into it via `widget.update({ [propName]: newValue })` — the machinery that powers targeted updates.
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
const label = Text({ text: "waiting…" });
|
|
64
|
+
const [status, setStatus] = useState("status", "idle", label, "text");
|
|
65
|
+
|
|
66
|
+
setStatus("done"); // calls label.update({ text: "done" })
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Because the widget is now bound to the key, this form is surgical on its own: `setStatus`
|
|
70
|
+
patches `label` and skips the app render, unless the component also *computed* with that
|
|
71
|
+
key during its last render (see **The surgical decision**). Prefer passing the state value
|
|
72
|
+
straight into a prop — mechanism 10 binds it for you — and use this signature when the
|
|
73
|
+
widget already exists.
|
|
74
|
+
|
|
75
|
+
## 4. Reactive property setters — `widget.prop = value`
|
|
76
|
+
|
|
77
|
+
Every style, spacing, colour, typography, and text prop installed by the widget factory becomes a live accessor: reading `widget.prop` returns the current value, assigning calls `widget.update({ prop })` and patches **that element only**. No app render, no subscribers involved.
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
header.text = "New title"; // element.textContent updates in place
|
|
81
|
+
panel.update({ bgColor: "#e11d48" });
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Two protections, by design:
|
|
85
|
+
|
|
86
|
+
- `value` is never shadowed on form elements so the browser keeps live input state (use `setValue` or `widget.value` on inputs).
|
|
87
|
+
- `width` / `height` are not shadowed on replaced elements (`img`, `canvas`, `video`, `iframe`, `embed`, `object`, `source`) to preserve their native intrinsic-size behaviour.
|
|
88
|
+
|
|
89
|
+
## 5. Imperative updates — `widget.update(props)`
|
|
90
|
+
|
|
91
|
+
The factory-level update method on every widget. It merges the new props into the stored props and re-applies styles, attributes, events, children, and effects through the same pipeline used at construction. A recursion guard (`MAX_UPDATE_DEPTH`) prevents runaway loops, and `child` / `children` are normalised so only one wins.
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
card.update({ padding: 24, borderRadius: 16 });
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 6. Live handles — `ref()`
|
|
98
|
+
|
|
99
|
+
A `ref` is a function bound to a widget's `ref` prop. The handle mirrors the widget's methods and property accessors, so you can read and write live:
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
const label = ref();
|
|
103
|
+
Text({ text: "Hi", ref: label });
|
|
104
|
+
|
|
105
|
+
label.update({ text: "Saved!" }); // same as widget.update
|
|
106
|
+
label.text = "Also works"; // forwarded to the widget
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## 7. Side-effect watchers — `useWatchState`
|
|
110
|
+
|
|
111
|
+
Like `useState`'s `subscribe`, but for side effects that should not touch widgets. Returns an unsubscribe function.
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
const unsubscribe = useWatchState("counter", (newValue) => {
|
|
115
|
+
console.log("Counter hit", newValue);
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## 8. Theme reactivity — `subscribeTheme`
|
|
120
|
+
|
|
121
|
+
Theme changes re-render subscribers. `runApp` subscribes itself so the whole app recolors on `setTheme` / `toggleTheme`, and some widgets (`ListTile`, `Rating`, `Badge`) subscribe individually to stay themed. `watchSystemTheme()` follows OS light/dark preference.
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
const unsubscribeTheme = subscribeTheme((palette) => {
|
|
125
|
+
root.style.backgroundColor = palette.background;
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## 9. Router reactivity — `subscribe`
|
|
130
|
+
|
|
131
|
+
The router notifies subscribers on navigation. Layout shells and navigation widgets (`Scaffold`, `BottomNavigation`, `DrawerItem`) subscribe so highlighting and route content stay in sync; they unsubscribe automatically when removed from the DOM.
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
subscribe((route, params, query) => setBreadcrumb(route.path));
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## 10. Reactive prop values — resolved **and bound when passed directly**
|
|
138
|
+
|
|
139
|
+
A prop value can be a function or object flagged as reactive (`_isReactive`). The
|
|
140
|
+
factory resolves it to its current value when the prop is applied — and when the
|
|
141
|
+
value carries a state key (what `useState` returns), the factory **binds the widget
|
|
142
|
+
to that key automatically**.
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
const [color, setColor] = useState("color", "red");
|
|
146
|
+
|
|
147
|
+
const box = Container({ bgColor: color }); // ← the binding is created here
|
|
148
|
+
setColor("blue"); // ← only `box` is patched
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
A directly-passed reactive prop gives you a **surgical** update: the change patches
|
|
152
|
+
that one widget. The app does **not** re-render — not even once — and the DOM node
|
|
153
|
+
is reused, so `widget.prop = value` writes made earlier are not thrown away.
|
|
154
|
+
|
|
155
|
+
The binding is released automatically when the widget is removed (`onUnmount`), so
|
|
156
|
+
detached widgets stop receiving updates.
|
|
157
|
+
|
|
158
|
+
### The surgical decision
|
|
159
|
+
|
|
160
|
+
The value returned by `useState` is a **live function**. When it reaches a prop, the
|
|
161
|
+
factory resolves it *and* records the `(key → widget → prop)` link, so a later change
|
|
162
|
+
patches that widget instead of re-running anything. When it is consumed inside the
|
|
163
|
+
component, the string or boolean that reaches the prop carries no key left to bind, so the
|
|
164
|
+
only way to reflect a change is to re-run the app.
|
|
165
|
+
|
|
166
|
+
The dispatcher decides **per key, per change**, from exactly two facts:
|
|
167
|
+
|
|
168
|
+
```javascript
|
|
169
|
+
if (handled === 0 || isStructuralRead(changedKey)) triggerRender();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
- `handled` — how many widgets were patched for this key (mechanisms 3 and 10).
|
|
173
|
+
- `isStructuralRead(key)` — whether the component read that value *while rendering*,
|
|
174
|
+
during the last app render pass.
|
|
175
|
+
|
|
176
|
+
So the app re-renders when the key has no bound widget, or when the component computed
|
|
177
|
+
with it. A key can be both: then the widget **is** patched *and* the app re-renders, and
|
|
178
|
+
the patch is wasted work on a node the rebuild replaces.
|
|
179
|
+
|
|
180
|
+
What counts as a structural read:
|
|
181
|
+
|
|
182
|
+
| Code inside the component | Kind of read | Update |
|
|
183
|
+
| --- | --- | --- |
|
|
184
|
+
| `Text({ text: count })` | value passed as a prop | surgical |
|
|
185
|
+
| `Container({ bgColor: color })` | value passed as a prop | surgical |
|
|
186
|
+
| `Text({ text: "Count: " + count })` | concatenation | app render |
|
|
187
|
+
| `count > 5 ? A : B` | comparison in the component | app render |
|
|
188
|
+
| `getState("items").map(…)` | list shape | app render |
|
|
189
|
+
| `onPress: () => setCount(count + 1)` | inside an event handler | surgical |
|
|
190
|
+
| `onCount((n) => …)` | inside a subscriber | surgical |
|
|
191
|
+
|
|
192
|
+
Two properties of that set are worth knowing:
|
|
193
|
+
|
|
194
|
+
- **It is rebuilt on every render pass.** A structural read that only happens under a
|
|
195
|
+
condition stops costing app renders as soon as the component stops doing it.
|
|
196
|
+
- **Changes coalesce.** The render is scheduled with `setTimeout(…, 0)`, so three
|
|
197
|
+
`setCount` calls in one tick cost **one** app render.
|
|
198
|
+
|
|
199
|
+
Measured on a `Column` whose `Text` children depend on one key:
|
|
200
|
+
|
|
201
|
+
| component | changes | app renders | `Text` built | node reused |
|
|
202
|
+
| --- | --- | --- | --- | --- |
|
|
203
|
+
| only `Text({ text: count })` | 3 in one tick | **0** | 1 | yes |
|
|
204
|
+
| Concatenation + `text: count` + ternary | 3 in one tick | 1 | 2 | no |
|
|
205
|
+
| Only the concatenation | 2 in separate ticks | 2 | 3 | no |
|
|
206
|
+
|
|
207
|
+
The first row is the surgical path: no component code runs, `count`'s single bound widget
|
|
208
|
+
gets `update({ text: … })`. The last row shows the coalescing limit — a render per tick, not
|
|
209
|
+
per change.
|
|
210
|
+
|
|
211
|
+
### Keeping derived text surgical
|
|
212
|
+
|
|
213
|
+
A derived string does not need an app render if you derive it in the subscriber instead of
|
|
214
|
+
in the template:
|
|
215
|
+
|
|
216
|
+
```javascript
|
|
217
|
+
const App = () => {
|
|
218
|
+
const [count, setCount, onCount] = useState("counter", 0);
|
|
219
|
+
const label = Text({ text: "Count: 0" });
|
|
220
|
+
const badge = Text({ text: "poco" });
|
|
221
|
+
|
|
222
|
+
onCount((n) => {
|
|
223
|
+
label.update({ text: `Count: ${n}` });
|
|
224
|
+
badge.update({ text: n > 5 ? "MUCHO" : "poco" });
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
return Column({ children: [label, badge] });
|
|
228
|
+
};
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`onCount` runs outside a render pass, so its reads are not structural and the key stays
|
|
232
|
+
surgical — measured at **0 app renders** for three changes. This is the same reason reading
|
|
233
|
+
state inside `onPress` is free.
|
|
234
|
+
|
|
235
|
+
### What the app render does *not* do
|
|
236
|
+
|
|
237
|
+
There is no Virtual DOM and no reconciliation: an app render **re-runs the component and
|
|
238
|
+
creates new elements**, and the previous ones are dropped. Earlier `widget.prop = value`
|
|
239
|
+
writes and any internal state of the replaced subtree are lost (form fields keep what the
|
|
240
|
+
user typed, because `value` is never shadowed). A key read structurally is therefore the
|
|
241
|
+
one case where the framework does more work than it needs to — which is why the
|
|
242
|
+
recommendation is always the same: pass state values straight into props.
|
|
243
|
+
|
|
244
|
+
## Widget-owned reactivity
|
|
245
|
+
|
|
246
|
+
Beyond the generic layer, many widgets expose their own imperative APIs and some are reactive setters themselves (`Slider.value`, `Accordion.expanded`, `Dropdown.options`). Each widget page lists them under **Instance methods**. These follow the same rule: they call `update(...)` internally on that widget.
|
|
247
|
+
|
|
248
|
+
## Lifecycle containment
|
|
249
|
+
|
|
250
|
+
Every subscriber API returns an unsubscribe function. State bindings are released through
|
|
251
|
+
the widget's `onUnmount` hook — fired on both teardown paths — so a detached widget stops
|
|
252
|
+
receiving patches and its key entry is dropped once no widget is left bound to it.
|
|
253
|
+
`runApp`'s teardown sweeps the whole tree so re-renders do not leak listeners, timers, or
|
|
254
|
+
observers.
|
|
255
|
+
|
|
256
|
+
Note that `_cleanup` is **not** a lifecycle hook the framework calls; it is legacy. Resource
|
|
257
|
+
release belongs in `onUnmount`.
|
|
258
|
+
|
|
259
|
+
## Which one should you use?
|
|
260
|
+
|
|
261
|
+
| You want to… | Use | Cost |
|
|
262
|
+
| --- | --- | --- |
|
|
263
|
+
| Update ONE widget from a state value, without touching the app | `useState` + pass the value straight into a prop (`Container({ bgColor: color })`) | surgical — 0 app renders |
|
|
264
|
+
| Update ONE widget that already exists | `useState(key, value, widget, propName)` | surgical — 0 app renders |
|
|
265
|
+
| Show derived text (`Count: 3`) without an app render | derive inside `subscribe` / `onCount` and call `widget.update(…)` | surgical — 0 app renders |
|
|
266
|
+
| Change the structure of the tree from state | `useState` + read it in the component (ternary, `.map()`) | one app render per tick |
|
|
267
|
+
| Update one widget without state at all | `widget.prop = value`, `widget.update(props)`, `ref().update` | surgical — 0 app renders |
|
|
268
|
+
| React to any RamStore change | `subscribeRam` | surgical |
|
|
269
|
+
| Run a side effect on a key | `useWatchState` / `subscribe` | none |
|
|
270
|
+
| Follow the theme or the route | `subscribeTheme` / router `subscribe` | theme re-renders the app |
|
|
271
|
+
| Update a widget's whole prop set | `widget.update(props)` | surgical |
|
|
272
|
+
|
|
273
|
+
## Related pages
|
|
274
|
+
|
|
275
|
+
- [State, memo & refs](state.md) — the `useState` / `ref` / `memo` reference (previous chapter).
|
|
276
|
+
- [State guide](../guides/state.md) — full `useState` semantics for app state.
|
|
277
|
+
- [Async, ids & logging](async-ids-log.md) — next chapter.
|
|
278
|
+
- [Themes](theme.md) — theme tokens and theme control.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Continue reading
|
|
283
|
+
|
|
284
|
+
- **Previous:** [State, memo & refs](state.md)
|
|
285
|
+
- **Next:** [Async, ids & logging](async-ids-log.md)
|
|
286
|
+
- **Index:** [Tools index](README.md) · [The FletBox Book](../README.md)
|
|
287
|
+
|
|
288
|
+
You are reading **Chapter 10 · Tools & Utilities** (4.5 of 12).
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# State, memo & refs
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
Three power-ups for state and performance: `useState`/`useWatchState` for reactive values, `memo`/`memoWithKey`/`clearMemo` for caching expensive results, and `ref` for keeping a live handle to a widget.
|
|
5
|
+
|
|
6
|
+
## When to use
|
|
7
|
+
- Share values between widgets and re-render on change (`useState`).
|
|
8
|
+
- Cache an expensive computation (parsing, formatting, mapping) with `memo`.
|
|
9
|
+
- Grab a widget instance to update it later (`ref`).
|
|
10
|
+
|
|
11
|
+
## Import
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import { useState, useWatchState, memo, ref } from "flet-box";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Functions
|
|
18
|
+
|
|
19
|
+
| Function | Signature | Returns | Description |
|
|
20
|
+
| --- | --- | --- | --- |
|
|
21
|
+
| `useState` | `useState(key, initialState, widget?, propName?)` | `[value, setValue, subscribe]` | Reactive state stored under `key`. `value` is live: pass it straight into a prop and the factory binds that widget to the key, so `setValue` patches only it. Optional `widget` + `propName` binds an existing widget explicitly. |
|
|
22
|
+
| `useWatchState` | `useWatchState(key, callback)` | `() => void` | Watch a state key; returns an unsubscribe function. |
|
|
23
|
+
| `memo` | `memo(fn)` | `T & { cache? }` | Cache a function by its arguments. |
|
|
24
|
+
| `memoWithKey` | `memoWithKey(fn, keyFn)` | `T` | Cache using a custom result key. |
|
|
25
|
+
| `clearMemo` | `clearMemo(memoizedFn)` | `void` | Drop a memoized function's cache. |
|
|
26
|
+
| `ref` | `ref()` | `(widget) => widget & { update }` | Assign to a widget's `ref` prop to get a live handle. |
|
|
27
|
+
|
|
28
|
+
## Examples
|
|
29
|
+
|
|
30
|
+
### Everyday example
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
import { useState, Button, Text, Column } from "flet-box";
|
|
34
|
+
|
|
35
|
+
const [count, setCount] = useState("counter", 0);
|
|
36
|
+
|
|
37
|
+
Column({
|
|
38
|
+
children: [
|
|
39
|
+
Text({ text: `Count: ${count}` }), // derived here → app render
|
|
40
|
+
Text({ text: count }), // passed as a prop → surgical
|
|
41
|
+
Button({
|
|
42
|
+
text: "Add one",
|
|
43
|
+
onPress: () => setCount((previous) => previous + 1),
|
|
44
|
+
}),
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Full example — live widget updates via `ref`
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
import { Text, Button, ref } from "flet-box";
|
|
53
|
+
|
|
54
|
+
const label = ref();
|
|
55
|
+
|
|
56
|
+
Button({
|
|
57
|
+
text: "Rename",
|
|
58
|
+
onPress: () => label.update({ text: "Saved!" }),
|
|
59
|
+
ref: label,
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Caching with `memo`
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
import { memo } from "flet-box";
|
|
67
|
+
|
|
68
|
+
const parseData = (raw) => JSON.parse(raw);
|
|
69
|
+
const parseMemo = memo(parseData);
|
|
70
|
+
|
|
71
|
+
const first = parseMemo(jsonText); // runs
|
|
72
|
+
const second = parseMemo(jsonText); // cached, fast
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Notes
|
|
76
|
+
|
|
77
|
+
- Read the full [State guide](../guides/state.md) for the complete `useState` rules: stable keys, updater functions, immutability, and widget binding.
|
|
78
|
+
- `useState` returns `[value, setValue, subscribe]` — `subscribe(cb)` returns an unsubscribe function.
|
|
79
|
+
- The returned value is **live**: passing it as a prop (`Text({ text: count })`) binds that
|
|
80
|
+
widget to the key and `setValue` patches it alone, with no app render. Computing with it
|
|
81
|
+
inside a component (`` `Count: ${count}` ``, a ternary, a `.map()`) is a structural read,
|
|
82
|
+
and that does re-render the app — see
|
|
83
|
+
[Reactivity in FletBox](reactivity.md).
|
|
84
|
+
- Reads inside an event handler or a `subscribe` callback are never structural, so they
|
|
85
|
+
never cost an app render.
|
|
86
|
+
- `memo` caches by serialized arguments; use `memoWithKey` when you want to control the cache key yourself, and `clearMemo` to free memory when the cache grows too large.
|
|
87
|
+
- `ref()` returns a function for a widget's `ref` prop; the handle gives you `update(props)` to push new props into the widget.
|
|
88
|
+
|
|
89
|
+
## Related pages
|
|
90
|
+
- [Reactivity in FletBox](reactivity.md) — the full catalogue of every update mechanism.
|
|
91
|
+
- [State guide](../guides/state.md) — full `useState` semantics.
|
|
92
|
+
- [Text & time](text-and-time.md) — previous chapter.
|
|
93
|
+
- [Async, ids & logging](async-ids-log.md) — next chapter.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Continue reading
|
|
98
|
+
|
|
99
|
+
- **Previous:** [Text & time](text-and-time.md)
|
|
100
|
+
- **Next:** [Reactivity in FletBox](reactivity.md)
|
|
101
|
+
- **Index:** [Tools index](README.md) · [The FletBox Book](../README.md)
|
|
102
|
+
|
|
103
|
+
You are reading **Chapter 10 · Tools & Utilities** (4 of 12).
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Styling helpers (CSS in JS)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
Each styling helper returns a **CSS string** you can pass to a widget prop (`style`, `shadow`, `gradient`, etc.) — so you style widgets without hand-writing raw CSS. Sizes are in px unless the widget converts units for you.
|
|
5
|
+
|
|
6
|
+
## When to use
|
|
7
|
+
- Build `border`, `margin`, `padding`, `shadow`, `transform`, or `transition` values on the fly.
|
|
8
|
+
- Create gradients and color variations with one line.
|
|
9
|
+
|
|
10
|
+
## Import
|
|
11
|
+
|
|
12
|
+
```javascript
|
|
13
|
+
import { border, margin, padding, gradient, shadow, color } from "flet-box";
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Functions
|
|
17
|
+
|
|
18
|
+
| Function | Signature | Returns | Description |
|
|
19
|
+
| --- | --- | --- | --- |
|
|
20
|
+
| `animation` | `animation(name, duration?, timing?, iteration?)` | `string` | CSS `animation` shorthand. `name` can also be an options object (`{ name, duration, timing, delay, iteration, direction, fillMode }`). |
|
|
21
|
+
| `border` | `border(width = 1, style = "solid", color?)` | `string` | CSS `border` shorthand, e.g. `1px solid #e2e8f0`. |
|
|
22
|
+
| `gradient` | `gradient(type, colors, angle = 135)` | `string` | A CSS gradient background. `type` is `"linear"`, `"circle"` (radial), or `"conic"`; `colors` is an array of color strings. |
|
|
23
|
+
| `margin` / `padding` | `margin(value)` / `padding(value)` | `string` | Spacing CSS. `value` can be a number, a string, or `{ all, horizontal, vertical, top, right, bottom, left }`. |
|
|
24
|
+
| `rgba` | `rgba(r, g, b, a = 1)` | `string` | `rgba(r, g, b, a)` with an alpha default of `1`. |
|
|
25
|
+
| `shadow` | `shadow(x, y, blur, spread, color)` | `string` | A `box-shadow` value. |
|
|
26
|
+
| `transform` | `transform(options)` | `string` | CSS `transform` built from `{ translate, translateX, rotate, scale, skew, matrix, matrix3d, perspective, … }`. |
|
|
27
|
+
| `transition` | `transition(options)` | `string` | CSS `transition` built from `{ property, duration, timing, delay }` (or an array of them). |
|
|
28
|
+
| `filter` | `filter(options)` | `string` | CSS `filter` from `{ blur, brightness, contrast, dropShadow, grayscale, hueRotate, invert, opacity, saturate, sepia }`. |
|
|
29
|
+
| `flex` | `flex(grow, shrink = 1, basis = "0%")` | `string` | CSS `flex` shorthand. `grow` can also be `{ grow, shrink, basis }`. |
|
|
30
|
+
| `grid` | `grid(options)` | `string` | CSS grid template from `{ columns, rows, gap }`; `gap` can be a number or `{ row, column }`. |
|
|
31
|
+
| `color` | `color(value)` | Color object | A mutable, chainable color. See the Color object below. |
|
|
32
|
+
| `stackPosition` | `stackPosition(widget, props)` | `Widget` | Applies absolute positioning/centering to a widget and returns it. |
|
|
33
|
+
| `toREM` / `toPX` | `toREM(value)` / `toPX(value)` | `string \| undefined` | Convert a value between `rem` and `px`. |
|
|
34
|
+
| `setBaseFontSize` | `setBaseFontSize(px)` | `void` | Change the `rem` base (default `16`). |
|
|
35
|
+
| `getBaseFontSize` | `getBaseFontSize()` | `number` | The current `rem` base. |
|
|
36
|
+
|
|
37
|
+
### The Color object
|
|
38
|
+
|
|
39
|
+
`color(value)` returns a chainable object:
|
|
40
|
+
|
|
41
|
+
`hex()`, `rgb()`, `rgba()` — string forms · `lighten(percent)`, `darken(percent)`, `alpha(value)`, `complement()` — mutate and return the object · `isDark()`, `isLight()` — booleans · `contrast()` — a readable contrast color.
|
|
42
|
+
|
|
43
|
+
## Examples
|
|
44
|
+
|
|
45
|
+
### Everyday example
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
import { Text, border } from "flet-box";
|
|
49
|
+
|
|
50
|
+
Text({
|
|
51
|
+
text: "Card",
|
|
52
|
+
padding: 12,
|
|
53
|
+
border: border(1, "solid", "#cbd5e1"),
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Full example
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
import { Container, Text, gradient, shadow, color } from "flet-box";
|
|
61
|
+
|
|
62
|
+
const blue = color("#2563eb").lighten(10);
|
|
63
|
+
|
|
64
|
+
Container({
|
|
65
|
+
padding: 24,
|
|
66
|
+
gradient: gradient("linear", ["#2563eb", "#7c3aed"], 120),
|
|
67
|
+
shadow: shadow(0, 8, 24, 0, "rgba(0,0,0,0.2)"),
|
|
68
|
+
child: Text({ text: blue.hex(), color: "white" }),
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Notes
|
|
73
|
+
|
|
74
|
+
- These helpers only build **strings** — they do not render anything by themselves. Hand the result to a widget prop or `style`.
|
|
75
|
+
- A number passed to `margin`/`padding` becomes `Npx`; a string is used as-is (so you can write `"1rem"` or `"10px 20px"`).
|
|
76
|
+
- `animation(...)` differs from the [animate function](animation.md): this builds the CSS string, `animate()` runs an animation on an element.
|
|
77
|
+
- The Color object is stateful — create a copy (`color(value)`) when you need two different variations.
|
|
78
|
+
|
|
79
|
+
## Related pages
|
|
80
|
+
- [Lists, arrays & data](lists.md) — the next chapter.
|
|
81
|
+
- [Visual effects](visual-effects.md) — effects that apply to elements directly.
|
|
82
|
+
- [Animation helpers](animation.md) — run animations on elements.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Continue reading
|
|
87
|
+
|
|
88
|
+
- **Previous:** [Tools index](README.md)
|
|
89
|
+
- **Next:** [Lists, arrays & data](lists.md)
|
|
90
|
+
- **Index:** [Tools index](README.md) · [The FletBox Book](../README.md)
|
|
91
|
+
|
|
92
|
+
You are reading **Chapter 10 · Tools & Utilities** (1 of 12).
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Text & time
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
Small pure helpers for strings, dates, and media time. They take a value and return a formatted one — no side effects.
|
|
5
|
+
|
|
6
|
+
## When to use
|
|
7
|
+
- Normalize user names or titles before display (`capitalize`, `truncate`).
|
|
8
|
+
- Show friendly dates and "2 minutes ago" labels.
|
|
9
|
+
- Format audio/video positions in player UIs.
|
|
10
|
+
|
|
11
|
+
## Import
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import { capitalize, truncate, formatDate, relativeTime } from "flet-box";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Functions
|
|
18
|
+
|
|
19
|
+
### Text
|
|
20
|
+
|
|
21
|
+
| Function | Signature | Returns | Description |
|
|
22
|
+
| --- | --- | --- | --- |
|
|
23
|
+
| `capitalize` | `capitalize(str)` | `string` | First letter uppercase. |
|
|
24
|
+
| `capitalizeWords` | `capitalizeWords(str)` | `string` | First letter of every word uppercase. |
|
|
25
|
+
| `lowerCase` | `lowerCase(str)` | `string` | All lowercase. |
|
|
26
|
+
| `upperCase` | `upperCase(str)` | `string` | All uppercase. |
|
|
27
|
+
| `reverseString` | `reverseString(str)` | `string` | Reversed text. |
|
|
28
|
+
| `truncate` | `truncate(str, length?, suffix?)` | `string` | Cut to `length` characters and append `suffix` (default `"..."`). |
|
|
29
|
+
|
|
30
|
+
### Dates & time
|
|
31
|
+
|
|
32
|
+
| Function | Signature | Returns | Description |
|
|
33
|
+
| --- | --- | --- | --- |
|
|
34
|
+
| `formatDate` | `formatDate(date, format?)` | `string` | Format a date/string/number as text. |
|
|
35
|
+
| `relativeTime` | `relativeTime(date)` | `string` | A human label, e.g. `"2 minutes ago"`. |
|
|
36
|
+
| `now` | `now()` | `number` | Timestamp in milliseconds. |
|
|
37
|
+
|
|
38
|
+
### Media time
|
|
39
|
+
|
|
40
|
+
| Function | Signature | Returns | Description |
|
|
41
|
+
| --- | --- | --- | --- |
|
|
42
|
+
| `formatMediaTime` | `formatMediaTime(seconds)` | `string` | Compact `m:ss` (or `h:mm:ss`). |
|
|
43
|
+
| `formatMediaTimeLong` | `formatMediaTimeLong(seconds)` | `string` | Long form with units. |
|
|
44
|
+
| `getProgressPercent` | `getProgressPercent(current, total)` | `number` | `current/total` as a `0–100` percent. |
|
|
45
|
+
| `percentToSeconds` | `percentToSeconds(percent, total)` | `number` | Inverse of `getProgressPercent`. |
|
|
46
|
+
| `formatMediaProgress` | `formatMediaProgress(current, total)` | `string` | `"current / total"` formatted for a player. |
|
|
47
|
+
|
|
48
|
+
## Examples
|
|
49
|
+
|
|
50
|
+
### Everyday example
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
import { Text, capitalizeWords, relativeTime } from "flet-box";
|
|
54
|
+
|
|
55
|
+
Text({
|
|
56
|
+
text: `${capitalizeWords("hello fletbox")} — ${relativeTime(Date.now() - 120000)}`,
|
|
57
|
+
});
|
|
58
|
+
// "Hello Fletbox — 2 minutes ago"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Full example
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
import { formatMediaProgress, getProgressPercent } from "flet-box";
|
|
65
|
+
|
|
66
|
+
const pct = getProgressPercent(90, 300); // 30
|
|
67
|
+
const label = formatMediaProgress(90, 300); // "1:30 / 5:00"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Notes
|
|
71
|
+
|
|
72
|
+
- `formatDate(date, format?)` accepts a `Date`, a timestamp, or a date string; `format` is optional and platform-specific tokens apply.
|
|
73
|
+
- The media-time helpers are used by the [Audio](../widget/Audio.md) and [Video](../widget/Video.md) widgets.
|
|
74
|
+
- All functions return new values — inputs are never modified.
|
|
75
|
+
|
|
76
|
+
## Related pages
|
|
77
|
+
- [Lists, arrays & data](lists.md) — previous chapter.
|
|
78
|
+
- [State, memo & refs](state.md) — next chapter.
|
|
79
|
+
- [Input validation](validation.md) — more string rules.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Continue reading
|
|
84
|
+
|
|
85
|
+
- **Previous:** [Lists, arrays & data](lists.md)
|
|
86
|
+
- **Next:** [State, memo & refs](state.md)
|
|
87
|
+
- **Index:** [Tools index](README.md) · [The FletBox Book](../README.md)
|
|
88
|
+
|
|
89
|
+
You are reading **Chapter 10 · Tools & Utilities** (3 of 12).
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Themes
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
FletBox ships a built-in color system: `colors` is the palette of the current theme, `palettes` holds all named palettes, and the theme functions switch between light and dark, subscribe to changes, and follow the system preference. A `ThemeProvider` and the `useTheme()` hook wrap them for component-based apps.
|
|
5
|
+
|
|
6
|
+
## When to use
|
|
7
|
+
- Style widgets with semantic tokens (`colors.surface`, `colors.primary`, `colors.error`) instead of hard-coded hex values.
|
|
8
|
+
- Add a light/dark toggle that re-colors the whole app instantly.
|
|
9
|
+
- Follow the OS dark/light setting automatically.
|
|
10
|
+
|
|
11
|
+
## Import
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import { colors, setTheme, toggleTheme, subscribeTheme, ThemeProvider } from "flet-box";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Functions
|
|
18
|
+
|
|
19
|
+
| Function | Signature | Returns | Description |
|
|
20
|
+
| --- | --- | --- | --- |
|
|
21
|
+
| `setTheme` | `setTheme('light' \| 'dark')` | `Record<string, string>` | Switch the active palette. |
|
|
22
|
+
| `getTheme` | `getTheme()` | `string` | The active theme name. |
|
|
23
|
+
| `toggleTheme` | `toggleTheme()` | `string` | Flip light/dark and return the new name. |
|
|
24
|
+
| `getColor` | `getColor(name, opacity?)` | `string` | Resolve a token (optionally with alpha). |
|
|
25
|
+
| `subscribeTheme` | `subscribeTheme(callback)` | `() => void` | Subscribe to theme changes; returns an unsubscribe function. |
|
|
26
|
+
| `applySystemTheme` | `applySystemTheme()` | `string` | Apply light/dark from the OS preference. |
|
|
27
|
+
| `watchSystemTheme` | `watchSystemTheme()` | `() => void` | Follow the OS preference live; returns an unsubscribe function. |
|
|
28
|
+
| `ThemeProvider` | `ThemeProvider({ children, theme? })` | `Widget` | Provide a theme (defaults to system) for a subtree. |
|
|
29
|
+
| `useTheme` | `useTheme()` | object | `{ colors, setTheme, getTheme, toggleTheme, getColor, applySystemTheme }`. |
|
|
30
|
+
|
|
31
|
+
Plus the constants: `colors` (current palette) and `palettes` (all named palettes, keyed by name).
|
|
32
|
+
|
|
33
|
+
## Color tokens
|
|
34
|
+
|
|
35
|
+
Every token is available on `colors`:
|
|
36
|
+
|
|
37
|
+
- **Brand:** `primary`, `secondary`, `info`, `success`, `warning`, `danger`, `error`.
|
|
38
|
+
- **Surfaces:** `background`, `surface`, `card`, `overlay`.
|
|
39
|
+
- **Text:** `text`, `textSecondary`, `textDisabled`, `divider`, `border`, `shadow`.
|
|
40
|
+
- **Tints:** `gray50` … `gray900`, plus the soft backgrounds `errorBg`, `successBg`, `warningBg`, `infoBg`.
|
|
41
|
+
|
|
42
|
+
The `dark` palette overrides the surface/text tokens; `gray*` and brand colors also shift to keep contrast. Read [the palette walkthrough](../guides/utilities.md) for usage patterns.
|
|
43
|
+
|
|
44
|
+
## Examples
|
|
45
|
+
|
|
46
|
+
### Everyday example
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
import { Button, colors, toggleTheme } from "flet-box";
|
|
50
|
+
|
|
51
|
+
Button({
|
|
52
|
+
text: "Switch theme",
|
|
53
|
+
onPress: () => toggleTheme(),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const card = { backgroundColor: colors.surface, color: colors.text };
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Full example
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
import { runApp, Container, Column, Button, Text, useTheme } from "flet-box";
|
|
63
|
+
|
|
64
|
+
const ThemedApp = () => {
|
|
65
|
+
const theme = useTheme();
|
|
66
|
+
|
|
67
|
+
return Container({
|
|
68
|
+
backgroundColor: theme.colors.background,
|
|
69
|
+
padding: 24,
|
|
70
|
+
child: Column({
|
|
71
|
+
gap: 12,
|
|
72
|
+
children: [
|
|
73
|
+
Text({ text: `Theme: ${theme.getTheme()}`, color: theme.colors.text }),
|
|
74
|
+
Button({ text: "Toggle", onPress: () => theme.toggleTheme() }),
|
|
75
|
+
],
|
|
76
|
+
}),
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
runApp(ThemeProvider({ children: ThemedApp() }), "root");
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Notes
|
|
84
|
+
|
|
85
|
+
- The shared palette tokens are updated across all widgets when you switch themes; widgets referencing `colors.*` at build time pick up the active palette.
|
|
86
|
+
- `watchSystemTheme()` and `applySystemTheme()` read `prefers-color-scheme`; both return/need cleanup to release listeners.
|
|
87
|
+
- `subscribeTheme(callback)` fires with the new palette object — remember to unsubscribe when a feature is destroyed.
|
|
88
|
+
|
|
89
|
+
## Related pages
|
|
90
|
+
- [Device & environment](device.md) — previous chapter.
|
|
91
|
+
- [Markdown, code & HTML](markdown.md) — next chapter.
|
|
92
|
+
- [Colors and utilities guide](../guides/utilities.md) — palette walkthrough.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Continue reading
|
|
97
|
+
|
|
98
|
+
- **Previous:** [Device & environment](device.md)
|
|
99
|
+
- **Next:** [Markdown, code & HTML](markdown.md)
|
|
100
|
+
- **Index:** [Tools index](README.md) · [The FletBox Book](../README.md)
|
|
101
|
+
|
|
102
|
+
You are reading **Chapter 10 · Tools & Utilities** (7 of 12).
|