Shape transitions with a point of view.
Deterministic geometry, spring motion, and a native Flutter surface in one small API.
Repository · Live showcase · Showcase source · 中文文档
Meld turns one vector shape into another without the “rubber stamp” feeling of point-by-point interpolation. It plans correspondence, rotation, scale, topology, and spring state once, then keeps every frame cheap and inspectable.
dependencies:
meld: ^1.0.0import 'package:flutter/material.dart';
import 'package:meld/meld.dart';
class MenuButton extends StatelessWidget {
const MenuButton({super.key});
static const menu = PathDataSource('M3 6H21M3 12H21M3 18H21');
static const close = PathDataSource('M5 5L19 19M19 5L5 19');
@override
Widget build(BuildContext context) {
return const MeldIcon(
from: menu,
to: close,
progress: 0.5,
size: 32,
label: 'Menu',
);
}
}The same MeldIcon supports an icon endpoint for declarative updates and a controller for imperative motion. progress is controlled and deterministic; it is ideal for a drag gesture, a scrubber, or a timeline editor.
final controller = MeldIconController(initialSource: MenuButton.menu);
// Command mode: attach the controller through MeldIcon, then await completion.
await controller.morphTo(MenuButton.close, preset: SpringPreset.snappy);
// Scrub mode: no ticker, no hidden state.
controller.seek(MenuButton.close, 0.72);
// Reverse in-flight motion in place; a settled endpoint becomes the new start.
await controller.reverse(preset: SpringPreset.snappy);
// Sequence mode: transitions remain composable.
await controller.playSequence([MenuButton.menu, MenuButton.close]);Use MeldIconTheme for a shared visual language. MeldPaintStyle.outline always draws the geometry outline, original preserves the source's declared or parsed paint intent, and both fills closed contours before adding an outline. Geometry-only sources can declare MeldSourcePaintStyle.fill, .outline, or .both; SVG markup derives the intent from inline fill/stroke attributes when no override is supplied.
PathDataSource: SVG path commands including relative commands, shorthand curves, arcs, scientific notation, and implicit repeats.SvgMarkupSource: portable SVG geometry (path,line,circle,ellipse,rect,polyline,polygon) withviewBoxsupport.GeometrySource: structured, serializable geometry for generated icons and fixtures.CubicSource: normalized cubic paths for precomputed assets and adapters.meld_font: optional static TTF/OTF outline adapter. Pass the font bytes you already have; Meld never downloads or bundles a font.
Font outlines are filled glyph geometry, not strokes. The adapter supports Unicode cmap, simple and composite TrueType glyphs, and quadratic-to-cubic conversion. CFF, color, and variable outlines fail explicitly with a diagnostic so a visual fallback cannot silently ship.
final data = await rootBundle.load('assets/YourLicensedFont.ttf');
final bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
final home = Meld.sourceFromIconData(Icons.home_rounded, fontBytes: bytes);
final homeIcon = MeldIcon(
icon: home,
paintStyle: MeldPaintStyle.original,
label: 'Home',
);The font asset and its license stay in your application; only the outline geometry enters the plan.
Ordinary interpolation assumes point n in one icon belongs to point n in the next. Meld first matches subpaths, searches closed-path cuts and winding, then solves a similarity transform. Polar interpolation lets a shape rotate and scale as a coherent object while tangent-aware easing protects smooth curves and real corners. If topology is ambiguous, the plan keeps the ambiguity in diagnostics instead of hiding it in a frame.
final plan = MeldEngine(
sampling: const SamplingConfig(pointCount: 64, adaptive: true),
).plan(MenuButton.menu, MenuButton.close);
final json = plan.toJson();
final restored = MeldPlan.fromJson(json);Plans are deterministic and serializable. Caches are bounded LRU caches with
entry and approximate byte budgets; hit/miss and retained-byte metrics are
available through MeldCacheStats. MeldDiagnosticsOverlay is an opt-in panel
for cache, residual, sample, spring, and planning information; keep it out of
release layouts when you do not need it.
The Flutter layer uses one shared frame scheduler per isolate and reuses the
painter's Path/Paint buffers during flight. This keeps per-frame work small
enough for high-refresh displays; actual 120 Hz delivery still depends on the
device, scene complexity, and the rest of the app's frame budget.
Run the reproducible desktop benchmark from the workspace:
melos bootstrap
melos run benchmarkBenchmark output is intentionally machine-readable enough to compare in CI. Treat a regression as a design problem and inspect the plan diagnostics before changing quality settings.
MeldIcon is a regular Flutter widget: it works on mobile, desktop, and web without platform channels. Supply a label for image semantics or use excludeFromSemantics when the icon is purely decorative. MeldMotionMode.user follows MediaQuery.disableAnimations; never snaps to the endpoint and always is useful for a demo or a controlled preview.
The examples/showcase app is a responsive inspection tool, not mock product data. It includes independent start/end selection, quick icon pairs, raw path data, SVG markup, real Flutter font glyph outlines, a progress scrubber, spring controls, quality presets, and a live diagnostics panel.
Meld uses Melos. Read GOALS.md before changing the workspace. The development flow is design → complete implementation → static checks → focused verification → full regression. This project does not use TDD.
melos bootstrap
melos run format
melos run analyze
melos run test
melos run benchmarkMeld is released under the MIT License. Font files remain the responsibility of the application that supplies them; check the font's own license before distributing its bytes.
If Meld helps your interface, try it in the showcase, open an issue with a reproducible fixture, or send a focused pull request: https://github.com/fluttercandies/meld.