{"id":51511972,"url":"https://github.com/code3743/layer_canvas_flutter","last_synced_at":"2026-07-08T07:30:31.862Z","repository":{"id":369323256,"uuid":"1289334279","full_name":"code3743/layer_canvas_flutter","owner":"code3743","description":"Flutter widgets and adapters for layer_canvas, a 2D compositing engine written in pure Dart (no dart:ui dependency) that rasterizes via Blend2D through FFI.","archived":false,"fork":false,"pushed_at":"2026-07-04T18:09:22.000Z","size":523,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-04T18:13:13.503Z","etag":null,"topics":["blend2d","dart","flutter","package"],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/code3743.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-04T16:05:33.000Z","updated_at":"2026-07-04T18:09:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/code3743/layer_canvas_flutter","commit_stats":null,"previous_names":["code3743/layer_canvas_flutter"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/code3743/layer_canvas_flutter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code3743%2Flayer_canvas_flutter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code3743%2Flayer_canvas_flutter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code3743%2Flayer_canvas_flutter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code3743%2Flayer_canvas_flutter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/code3743","download_url":"https://codeload.github.com/code3743/layer_canvas_flutter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code3743%2Flayer_canvas_flutter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35257082,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-08T02:00:06.796Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blend2d","dart","flutter","package"],"created_at":"2026-07-08T07:30:31.053Z","updated_at":"2026-07-08T07:30:31.826Z","avatar_url":"https://github.com/code3743.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# layer_canvas_flutter\n\n![layer_canvas_flutter — native 2D rendering for Flutter](https://raw.githubusercontent.com/code3743/layer_canvas_flutter/main/doc/hero.png)\n\nNative 2D rendering for Flutter, using only the Flutter types you already\nknow. [`layer_canvas`](https://pub.dev/packages/layer_canvas) composites\ntyped layers to a PNG through [Blend2D](https://blend2d.com) via FFI. \nThis package is the Flutter-native \nfront door to it: build a scene with `Color`, `Offset`, `Gradient`, and a\n`Path`-shaped builder, drop it in a widget, done.\n\n## Quick start\n\n```dart\nLayerCanvas(\n  sceneBuilder: (logicalSize, pixelRatio) =\u003e Scenes.of(\n    width: logicalSize.width * pixelRatio,\n    height: logicalSize.height * pixelRatio,\n    children: [\n      Layers.rectangle(\n        size: logicalSize,\n        gradient: const LinearGradient(\n          colors: [Color(0xFF1E1E2E), Color(0xFF2B2B45)],\n        ),\n        pixelRatio: pixelRatio,\n      ),\n      Layers.text(\n        text: 'Hello, layer_canvas!',\n        position: const Offset(24, 24),\n        color: const Color(0xFFFFFFFF),\n        fontSize: 22,\n        fontWeight: FontWeight.w600,\n        pixelRatio: pixelRatio,\n      ),\n    ],\n  ),\n)\n```\n\nThat's a native Blend2D render, dropped into your widget tree like any\nother `Image` — no `Color32`, no `Point2D`, nothing from the core package\nimported directly. See [Usage](#usage) below for gradients, custom shapes,\nSVG, and tap handling.\n\n## Features\n\n* **`Layers`** — static factories (`rectangle`, `text`, `image`, `path`,\n  `svg`, `group`) that build `layer_canvas` layers from Flutter types, with\n  an optional `pixelRatio` to scale a layer built in logical units to\n  physical-pixel resolution.\n* **Gradients** — pass a Flutter `LinearGradient`/`RadialGradient`/\n  `SweepGradient` as `Layers.rectangle`/`Layers.path`'s `gradient:`, no core\n  gradient types involved.\n* **`LayerPathBuilder`** — draws a `Layers.path` shape with the same method\n  names as `dart:ui`'s own `Path` (`moveTo`, `lineTo`, `cubicTo`,\n  `arcToPoint`, `close`...), so it reads like drawing on a `Canvas`.\n* **`Layers.svg`** — places an already-parsed `SvgDocument` as a layer.\n* **`SvgLayer`** — a widget that displays an `SvgDocument` at a given\n  size with a real `BoxFit` — not `SvgPicture` (that's `flutter_svg`'s\n  widget; a different rendering path entirely).\n* **`Scenes.of`** — builds a `Scene` from a `children` list instead of the\n  core's mutate-after-construction `Scene(...)..add(...)..add(...)`.\n* **`LayerCanvas`** — a widget that renders a `Scene` (fixed, or built from\n  the widget's measured size and device pixel ratio) as an `Image`, with\n  render caching, a placeholder while it's rendering, an error builder, and\n  an `onLayerTap` to find which `Layer` was tapped.\n* **`SceneWidget`** — `Scenes.of` + `LayerCanvas` in one widget, shaped like\n  `Stack(children: [...])`, for fixed-size scenes that don't need per-build\n  DPR scaling.\n* **`LayerCanvasFonts`** — loads every declared weight of the fonts in your\n  app's `pubspec.yaml` into `layer_canvas`'s native font registry at\n  startup, so you can turn off the core's embedded default font and use\n  your own.\n\n## Getting started\n\nRequires Flutter 3.27+ (for `Color.toARGB32()`, which the color adapter\nrelies on). Add this package — `layer_canvas` comes along transitively, so\nyou don't need to add it yourself, and you shouldn't need to import it\ndirectly either: `Layers`, `Scenes.of`, `LayerCanvas` and `SceneWidget`\ncover the whole surface you need from Flutter code.\n\n```yaml\ndependencies:\n  layer_canvas_flutter: ^0.1.0-beta.1\n```\n\n`layer_canvas` embeds a default font (Roboto) in its native library so text\nrenders out of the box, at a cost of roughly 1.4 MB. Since a Flutter app\nalready ships its own fonts, you can turn the embed off in **your app's**\n`pubspec.yaml` (this only works in the final app, not in a package):\n\n```yaml\nhooks:\n  user_defines:\n    layer_canvas:\n      embed_default_font: false\n```\n\nThen declare a font your app uses for canvas text and preload it with\n`LayerCanvasFonts` before `runApp`:\n\n```yaml\nflutter:\n  fonts:\n    - family: Roboto\n      fonts:\n        - asset: assets/fonts/Roboto-Regular.ttf\n```\n\n```dart\nFuture\u003cvoid\u003e main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n  await LayerCanvasFonts.ensureInitialized(\n    asDefault: 'Roboto',\n    families: {'Roboto'}, // scopes registration to just this font\n  );\n  runApp(const MyApp());\n}\n```\n\nSee `example/` for a full app wired up this way.\n\n## Usage\n\nThe snippets below build on [Quick start](#quick-start) above and assume:\n\n```dart\nimport 'package:flutter/material.dart';\nimport 'package:layer_canvas_flutter/layer_canvas_flutter.dart';\n```\n\nPassing `pixelRatio` to each `Layers` factory scales its measurements\n(position, size, and type-specific lengths like `cornerRadius`/`fontSize`)\ntogether, so a scene built in logical units still rasterizes at physical-pixel\nresolution.\n\n`Layers.group` shares one transform/opacity across a set of children, so\nmoving, rotating or fading a cluster of layers only requires updating the\ngroup, not every child:\n\n```dart\nLayers.group(\n  position: const Offset(60, 60),\n  rotation: -0.08,\n  pixelRatio: pixelRatio,\n  children: [\n    Layers.rectangle(\n      size: const Size(160, 70),\n      color: const Color(0xFF4C6EF5),\n      cornerRadius: 10,\n      pixelRatio: pixelRatio,\n    ),\n    Layers.text(\n      text: 'Grouped!',\n      position: const Offset(16, 22),\n      color: const Color(0xFFFFFFFF),\n      fontSize: 20,\n      fontWeight: FontWeight.w600,\n      pixelRatio: pixelRatio,\n    ),\n  ],\n)\n```\n\n### Gradients\n\nPass a Flutter `LinearGradient`, `RadialGradient`, or `SweepGradient` as\n`gradient:` — same types you'd give a `BoxDecoration`:\n\n```dart\nLayers.rectangle(\n  size: const Size(300, 120),\n  gradient: const LinearGradient(\n    colors: [Color(0xFFFF6B6B), Color(0xFFFFD93D)],\n    begin: Alignment.topLeft,\n    end: Alignment.bottomRight,\n  ),\n  cornerRadius: 16,\n  pixelRatio: pixelRatio,\n)\n```\n\n### Custom shapes with LayerPathBuilder\n\n`LayerPathBuilder` mirrors `dart:ui`'s `Path` — the same method names, in\nthe same order, so a shape you'd know how to draw in a `CustomPainter`\nreads the same way here:\n\n```dart\nLayers.path(\n  path: LayerPathBuilder()\n    ..moveTo(const Offset(60, 0))\n    ..lineTo(const Offset(120, 100))\n    ..lineTo(const Offset(0, 100))\n    ..close(),\n  color: const Color(0xFF06D6A0),\n  pixelRatio: pixelRatio,\n)\n```\n\n`LayerPathBuilder.circle`/`.oval`/`.polygon`/`.polyline` cover the common\nshapes without building one command at a time.\n\n### SVG\n\nParse an `SvgDocument` once (it's real XML parsing — don't do it inside a\n`sceneBuilder` that runs every frame) and place it with `Layers.svg`:\n\n```dart\nclass _MyWidgetState extends State\u003cMyWidget\u003e {\n  static final _logo = SvgDocument.parse(myLogoSvgSource);\n\n  @override\n  Widget build(BuildContext context) {\n    return LayerCanvas(\n      sceneBuilder: (logicalSize, pixelRatio) =\u003e Scenes.of(\n        width: logicalSize.width * pixelRatio,\n        height: logicalSize.height * pixelRatio,\n        children: [\n          Layers.svg(_logo, size: const Size(64, 64), pixelRatio: pixelRatio),\n        ],\n      ),\n    );\n  }\n}\n```\n\nDisplaying one SVG on its own (not composed with other layers) is simpler\nwith `SvgLayer`, the `Image.asset`-shaped widget for a single document:\n\n```dart\nclass _MyIconState extends State\u003cMyIcon\u003e {\n  static final _logo = SvgDocument.parse(myLogoSvgSource);\n\n  @override\n  Widget build(BuildContext context) =\u003e SvgLayer(_logo, width: 48, height: 48);\n}\n```\n\n`SvgLayer` is not `flutter_svg`'s `SvgPicture` — same idea (display a\nparsed SVG at a size, with a `BoxFit`), different rendering path entirely\n(this one goes through `layer_canvas`'s native Blend2D renderer). `fit`\nhere is real Flutter box-fitting (`FittedBox`), not something\n`layer_canvas` does natively: a `Group` (what a placed `SvgDocument` is)\nhas no native crop/cover concept to clip against, so `SvgLayer` rasterizes\nthe document at its own natural size and lets Flutter's ordinary layout\nscale/position that result, the same way it would any other\nfixed-aspect-ratio child.\n\n### Tap handling with onLayerTap\n\n`LayerCanvas.onLayerTap` reports which `Layer` (if any) was under a tap,\nusing the core's `hitTestScene`:\n\n```dart\nLayerCanvas(\n  scene: scene,\n  onLayerTap: (layer, localPosition) {\n    if (layer?.id == 'submit-button') {\n      submit();\n    }\n  },\n)\n```\n\nIt's a bounding-box test against each layer's own `size` (not its exact\npainted shape — a circular `PathLayer` hit-tests as its bounding square),\nand a layer with no explicit `size` (intrinsic sizing, e.g. an unset-size\n`TextLayer`) never matches, since its true rendered bounds are only known\nto the native backend once it's actually laid out. Give an interactive\nlayer an explicit `size` if it needs to receive taps. Coordinates are\nmapped through `fit`, so this works whether the widget's box matches the\nscene's own aspect ratio or not.\n\n### SceneWidget\n\n`SceneWidget` combines `Scenes.of` and `LayerCanvas` in one widget shaped\nlike `Stack`, for fixed-size scenes whose layers you'd rather write as a\nplain `children` list instead of a `sceneBuilder` callback:\n\n```dart\nSceneWidget(\n  width: 300,\n  height: 160,\n  children: [\n    Layers.rectangle(size: const Size(300, 160), color: const Color(0xFF1E1E2E)),\n    Layers.text(text: 'SceneWidget', position: const Offset(24, 24)),\n  ],\n)\n```\n\nUnlike `LayerCanvas(scene: someStableScene)`, `SceneWidget` builds a new\n`Scene` on every `build()` — there's no cheap way to tell \"same content,\nnew list\" apart from \"different content\" (`layer_canvas`'s `Layer` types\nhave no value equality). That's fine for content that changes rarely; if\n`SceneWidget` sits somewhere that rebuilds often (an animation, frequent\n`setState`), prefer building a `Scene` once with `Scenes.of` and passing it\nto `LayerCanvas` directly, so re-renders happen only when you decide to\nbuild a new one.\n\n### Treat `Scene` as immutable\n\n`LayerCanvas` re-renders when it sees a *new* `Scene` instance (or a change\nin measured size/pixel ratio) — not when an existing `Scene`'s contents\nchange. Build a new `Scene` whenever its contents change; if you must mutate\none in place, pass a changing `rebuildKey` to force a re-render:\n\n```dart\nLayerCanvas(scene: scene, rebuildKey: generation)\n```\n\n## Additional information\n\nThis package only depends on `layer_canvas` and re-exports only the pieces\nof its API that this package's own public API surfaces as parameters or\nreturn types: `Scene`, `Layer` and its subclasses (`RectangleLayer`,\n`TextLayer`, `ImageLayer`, `PathLayer`, `Group`), `LayerImageSource` (with\n`FileImageSource`/`MemoryImageSource`), `SvgDocument`/`SvgParseException`,\n`Renderer`/`RenderException`, and `FontRegistry`/`FontRegistrationException`.\nValue types the core exposes that `Layers` and the adapters exist\nspecifically to shield you from (`Color32`, `Point2D`/`Size2D`,\n`TextWeight`, `TextAlignment`, `ImageFit`, `LayerPaint`, `LayerTransform`,\n`FillRule`, and the core's own `Gradient`/`LinearGradient`/\n`RadialGradient`/`ConicGradient`, which would otherwise collide with\nFlutter's own same-named gradient types) are intentionally not re-exported\n— building UI with this package should never require importing\n`package:layer_canvas` directly.\n\nSee [`layer_canvas`](https://pub.dev/packages/layer_canvas) and its\n[repository](https://github.com/code3743/layer_canvas) for the underlying\nmodel and rendering engine.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcode3743%2Flayer_canvas_flutter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcode3743%2Flayer_canvas_flutter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcode3743%2Flayer_canvas_flutter/lists"}