@@ -42,6 +42,25 @@ other `Image` — no `Color32`, no `Point2D`, nothing from the core package
4242imported directly. See [ Usage] ( #usage ) below for gradients, custom shapes,
4343SVG, and tap handling.
4444
45+ ## Gallery
46+
47+ A few scenes built entirely from ` Layers ` /` Scenes.of ` /` LayerPathBuilder ` —
48+ gradients, hand-drawn vector paths, SVG, dashed strokes, and wrapped text —
49+ to give a sense of what a ` LayerCanvas ` can render.
50+
51+ <table >
52+ <tr >
53+ <td width =" 33% " ><img src =" https://raw.githubusercontent.com/code3743/layer_canvas_flutter/main/doc/gallery/gradient-burst.png " alt =" Overlapping radial, linear, and sweep gradients " ></td >
54+ <td width =" 33% " ><img src =" https://raw.githubusercontent.com/code3743/layer_canvas_flutter/main/doc/gallery/vector-blob.png " alt =" An organic blob drawn with LayerPathBuilder cubic and quadratic curves " ></td >
55+ <td width =" 33% " ><img src =" https://raw.githubusercontent.com/code3743/layer_canvas_flutter/main/doc/gallery/bauhaus-grid.png " alt =" An abstract Bauhaus-style geometric composition " ></td >
56+ </tr >
57+ <tr >
58+ <td width =" 33% " ><img src =" https://raw.githubusercontent.com/code3743/layer_canvas_flutter/main/doc/gallery/svg-pattern.png " alt =" A parsed SVG document placed as a scattered pattern " ></td >
59+ <td width =" 33% " ><img src =" https://raw.githubusercontent.com/code3743/layer_canvas_flutter/main/doc/gallery/constellation.png " alt =" A dashed-stroke constellation/network diagram " ></td >
60+ <td width =" 33% " ><img src =" https://raw.githubusercontent.com/code3743/layer_canvas_flutter/main/doc/gallery/editorial-card.png " alt =" An editorial card with wrapped text, a clipped image badge, and a gradient background " ></td >
61+ </tr >
62+ </table >
63+
4564## Features
4665
4766* ** ` Layers ` ** — static factories (` rectangle ` , ` text ` , ` image ` , ` path ` ,
@@ -51,6 +70,13 @@ SVG, and tap handling.
5170* ** Gradients** — pass a Flutter ` LinearGradient ` /` RadialGradient ` /
5271 ` SweepGradient ` as ` Layers.rectangle ` /` Layers.path ` 's ` gradient: ` , no core
5372 gradient types involved.
73+ * ** Stroke cap/join/miter/dash** — ` strokeCap ` /` strokeJoin ` (` dart:ui ` 's own
74+ enums), ` strokeMiterLimit ` , and (on ` Layers.path ` ) ` dashArray ` /
75+ ` dashOffset ` for a dashed stroke.
76+ * ** ` clipBehavior ` ** — clips a sized layer to its own box, same idea as
77+ ` Container ` 's ` clipBehavior ` .
78+ * ** ` scale ` /` alignment ` ** — every factory's ` rotation ` gets a uniform
79+ ` scale ` and an ` alignment ` to pivot around, instead of always the center.
5480* ** ` LayerPathBuilder ` ** — draws a ` Layers.path ` shape with the same method
5581 names as ` dart:ui ` 's own ` Path ` (` moveTo ` , ` lineTo ` , ` cubicTo ` ,
5682 ` arcToPoint ` , ` close ` ...), so it reads like drawing on a ` Canvas ` .
@@ -60,6 +86,12 @@ SVG, and tap handling.
6086 widget; a different rendering path entirely).
6187* ** ` Scenes.of ` ** — builds a ` Scene ` from a ` children ` list instead of the
6288 core's mutate-after-construction ` Scene(...)..add(...)..add(...) ` .
89+ * ** ` AssetImageSource ` ** — an image layer/background source that lazily
90+ loads a Flutter asset by key, resolved automatically before every render.
91+ * ** Scene persistence** — ` Scene.toJson() ` /` fromJson() ` save and restore a
92+ whole scene, ` AssetImageSource ` included.
93+ * ** ` Scenes.encode ` /` Scenes.saveToFile ` ** — export a ` Scene ` to
94+ ` png ` /` bmp ` /` qoi ` bytes or a file, instead of displaying it.
6395* ** ` LayerCanvas ` ** — a widget that renders a ` Scene ` (fixed, or built from
6496 the widget's measured size and device pixel ratio) as an ` Image ` , with
6597 render caching, a placeholder while it's rendering, an error builder, and
@@ -82,7 +114,7 @@ cover the whole surface you need from Flutter code.
82114
83115``` yaml
84116dependencies :
85- layer_canvas_flutter : ^0.1.0-beta.1
117+ layer_canvas_flutter : ^0.1.0
86118` ` `
87119
88120` layer_canvas` embeds a default font (Roboto) in its native library so text
@@ -181,6 +213,66 @@ Layers.rectangle(
181213)
182214` ` `
183215
216+ # ## Strokes: cap, join, miter, dash
217+
218+ ` strokeCap` /`strokeJoin` take `dart:ui`'s own enums — the same ones a
219+ ` Paint` would — and `strokeMiterLimit` controls how far a `StrokeJoin.miter`
220+ corner may extend before it's clamped to a bevel. `Layers.path` additionally
221+ takes `dashArray`/`dashOffset` for a dashed stroke (only `PathLayer`s dash —
222+ a `RectangleLayer` has no path geometry of its own to dash) :
223+
224+ ` ` ` dart
225+ Layers.path(
226+ path: LayerPathBuilder()
227+ ..moveTo(const Offset(0, 50))
228+ ..lineTo(const Offset(300, 50)),
229+ color: const Color(0xFF4C6EF5),
230+ style: PaintingStyle.stroke,
231+ strokeWidth: 4,
232+ strokeCap: StrokeCap.round,
233+ dashArray: const [12, 8],
234+ pixelRatio: pixelRatio,
235+ )
236+ ` ` `
237+
238+ # ## Clipping with clipBehavior
239+
240+ ` clipBehavior` (any value but the default `Clip.none`) clips a sized layer
241+ to its own box — same idea as `Container`'s `clipBehavior`. The natural
242+ case is cropping a `cover`-fit image, exactly like `Image` inside a clipped
243+ box :
244+
245+ ` ` ` dart
246+ Layers.image(
247+ source: MemoryImageSource(bytes),
248+ size: const Size(200, 120),
249+ fit: BoxFit.cover,
250+ clipBehavior: Clip.hardEdge,
251+ pixelRatio: pixelRatio,
252+ )
253+ ` ` `
254+
255+ Not available on `Layers.group`/`Layers.svg` : the core expands a `Group`
256+ into its concrete descendants before rendering, leaving no single surface
257+ to clip — clip an individual child via its own factory instead.
258+
259+ # ## scale and alignment
260+
261+ Every factory's `rotation` gets two companions : ` scale` (uniform) and
262+ ` alignment` (where `rotation`/`scale` pivot from — `Alignment.center` by
263+ default, same as the core) :
264+
265+ ` ` ` dart
266+ Layers.rectangle(
267+ size: const Size(80, 80),
268+ color: const Color(0xFFFF6B6B),
269+ rotation: 0.3,
270+ scale: 1.2,
271+ alignment: Alignment.topLeft, // pivot from the corner, not the center
272+ pixelRatio: pixelRatio,
273+ )
274+ ` ` `
275+
184276# ## Custom shapes with LayerPathBuilder
185277
186278` LayerPathBuilder` mirrors `dart:ui`'s `Path` — the same method names, in
@@ -248,6 +340,25 @@ the document at its own natural size and lets Flutter's ordinary layout
248340scale/position that result, the same way it would any other
249341fixed-aspect-ratio child.
250342
343+ # ## Word-wrap
344+
345+ ` Layers.text` word-wraps into a `size` with a width set — greedily,
346+ breaking only at spaces (a single word wider than the box overflows on its
347+ own line rather than being split mid-word) — and the wrapped block is
348+ vertically centered within `size`'s height :
349+
350+ ` ` ` dart
351+ Layers.text(
352+ text: 'A longer caption that should wrap across a few lines.',
353+ size: const Size(220, 80),
354+ fontSize: 16,
355+ pixelRatio: pixelRatio,
356+ )
357+ ` ` `
358+
359+ Leave `size` unset (or give it no width) for a single, possibly overflowing
360+ line — the same as before this existed.
361+
251362# ## Tap handling with onLayerTap
252363
253364` LayerCanvas.onLayerTap` reports which `Layer` (if any) was under a tap,
@@ -310,21 +421,63 @@ one in place, pass a changing `rebuildKey` to force a re-render:
310421LayerCanvas(scene: scene, rebuildKey: generation)
311422` ` `
312423
424+ # ## Saving and loading a Scene
425+
426+ ` Scene` round-trips through JSON as-is — `toJson()`/`fromJson` recurse
427+ through every layer, paint, gradient, transform, and image source :
428+
429+ ` ` ` dart
430+ final json = jsonEncode(scene.toJson());
431+ // ...later, or on another device:
432+ final restored = Scene.fromJson(jsonDecode(json) as Map<String, Object?>);
433+ ` ` `
434+
435+ For an image that should serialize as a short asset key instead of a
436+ base64 blob, use `AssetImageSource` instead of `ImageSources.asset`
437+ (which reads the bytes immediately) :
438+
439+ ` ` ` dart
440+ Layers.image(
441+ source: AssetImageSource('assets/logo.png'),
442+ size: const Size(120, 40),
443+ )
444+ ` ` `
445+
446+ Every widget in this package resolves an `AssetImageSource` against the
447+ ambient `AssetBundle` right before rendering, and its `LayerRegistry`
448+ decoder is registered automatically the first time one is built or
449+ deserialized — no setup call needed.
450+
451+ # ## Exporting a Scene
452+
453+ Every widget in this package always displays a `Scene` as PNG. To export
454+ one instead — to a file, or as bytes in another format — use `Scenes` :
455+
456+ ` ` ` dart
457+ final pngBytes = await Scenes.encode(scene); // png by default
458+ await Scenes.saveToFile(scene, '/path/to/export.qoi', format: OutputFormat.qoi);
459+ ` ` `
460+
461+ Both resolve any `AssetImageSource` in `scene` first, same as `LayerCanvas`.
462+
313463# # Additional information
314464
315465This package only depends on `layer_canvas` and re-exports only the pieces
316466of its API that this package's own public API surfaces as parameters or
317467return types : ` Scene` , `Layer` and its subclasses (`RectangleLayer`,
318468` TextLayer` , `ImageLayer`, `PathLayer`, `Group`), `LayerImageSource` (with
319469` FileImageSource` /`MemoryImageSource`), `SvgDocument`/`SvgParseException`,
320- ` Renderer` /`RenderException`, and `FontRegistry`/`FontRegistrationException`.
321- Value types the core exposes that `Layers` and the adapters exist
322- specifically to shield you from (`Color32`, `Point2D`/`Size2D`,
323- ` TextWeight` , `TextAlignment`, `ImageFit`, `LayerPaint`, `LayerTransform`,
324- ` FillRule` , and the core's own `Gradient`/`LinearGradient`/
470+ ` Renderer` /`RenderException`, `OutputFormat`,
471+ ` FontRegistry` /`FontRegistrationException`, and `LayerRegistry` (with its
472+ ` LayerFromJson` /`ImageSourceFromJson` typedefs, for registering a custom
473+ ` Layer` /`LayerImageSource` subclass of your own). Value types the core
474+ exposes that `Layers` and the adapters exist specifically to shield you
475+ from (`Color32`, `Point2D`/`Size2D`, `TextWeight`, `TextAlignment`,
476+ ` ImageFit` , `LayerPaint`, `LayerTransform`, `FillRule`, the core's own
477+ ` StrokeCap` /`StrokeJoin`, and its `Gradient`/`LinearGradient`/
325478` RadialGradient` /`ConicGradient`, which would otherwise collide with
326- Flutter's own same-named gradient types) are intentionally not re-exported
327- — building UI with this package should never require importing
479+ Flutter's own same-named types) are intentionally not re-exported —
480+ building UI with this package should never require importing
328481` package:layer_canvas` directly.
329482
330483See [`layer_canvas`](https://pub.dev/packages/layer_canvas) and its
0 commit comments