Morph any Flutter icon
into any other.
Animate Lucide, Tabler, Heroicons or any stroke icon set. Optimal rotation solved in closed form, spring physics, zero dependencies.
flutter pub add morphicons_flutter
naive x/y lerp
Quick pairs
tap to previewAnimation set
Choose two or more icons to make a sequence. Auto-morph cycles slowly.
Every icon auto-morphs on load (slow). Toggle to normal. Click to build a sequence.
SVG → Dart converter
Drop an SVG or paste markup. Flattens stroke geometry to a morph-ready d string. Preview morphs automatically (slow).
// No stroke geometry yet
D strings hidden from layout; use Copy to grab raw. Converter morphs automatically on preview.
Swap the pair for any two icons above. No wrappers, no keys, no configuration.
MorphIcon(icon: isOpen ? MorphIconsLucide.x : MorphIconsLucide.menu, spring: SpringPreset.snappy)
MorphIcon(icon: isOpen ? MorphIconsLucide.x : MorphIconsLucide.menu)
MorphIcon.controlled(from: MorphIconsLucide.menu, icon: MorphIconsLucide.x, progress: t)
MorphMask(icon: MorphIconsLucide.heart, child: DecoratedBox(decoration: BoxDecoration(gradient: LinearGradient(colors: [Color(0xfff857a6), Color(0xffff5858)])), child: SizedBox(width: 220, height: 150)))
Six kilobytes of math.
morphicons solves the optimal similarity between two shapes in closed form (2D Procrustes): if a pair is congruent under rotation, it rotates; if not, it morphs in the aligned frame. Nobody declares rotation groups by hand. Springs are interruptible, corners stay sharp at rest, and the core is pure Dart — no Widget, no BuildContext — so every Flutter surface is a first-class driver: Widget, CustomPainter, charts, games.
Icons are consumed as data, not widgets: an IconData or a raw SVG d maps to the same 24×24 cubic pipeline, structurally typed. Custom d just works; font glyphs resolve through the curated table in icon_data_resolver.dart. Raw paths stay off-layout until you Copy.
See the full docs- core, gzipped
- 6.5 KB
- runtime dependencies
- 0
- to plan any morph pair
- <1 ms
- shared Ticker
- 1
core, gzipped, everything included
runtime dependencies
to plan any morph pair
shared Ticker for every icon on screen
Works with any icon set — stroke or filled, any grid, via fitIcon + IconData resolver.
Same MorphIcon for String d and IconData. Canonical 24 grid; other grids (20, 16, 32…) fitted once via fitIcon(raw, "0 0 20 20"). Paste custom SVG as d — no extra widget. Add any font glyph by extending the resolver table (see lib/src/icon_data_resolver.dart).
- Lucide
- Tabler
- Heroicons
- Iconoir
- Phosphor
- Material Symbols
- Cupertino
- Any SVG → String d
- Any IconData
- Feather
- Font Awesome
- Fluent UI
- Custom TTF
Mixed morphs: IconData ↔ String d in one widget. Stroke ↔ filled same solver. Grids normalized in aligned frame — no rotation bleed.
Frame is one scroll label: playground · converter · code · info live inside. D hidden. Square 6px · dark minimal.
Flutter-native · IconData · filled
Morph Icons.home into any other IconData.
Morph any IconData with the same MorphIcon widget and solver used for path strings. Filled and stroked icons share one ticker and morph seamlessly — pass an IconData or a String d to the same widget.
Filled silhouettes morph with the same Procrustes + polar solver. Rapid clicks re-sample the interpolated polyline — no jumps.
import 'package:flutter/material.dart';
import 'package:morphicons_flutter/morphicons_flutter.dart';
// Uncontrolled — swap IconData, spring animates
MorphIcon(
icon: isHome ? Icons.home : Icons.favorite,
spring: SpringPreset.snappy,
size: 48,
)
// ALSO works: same widget, String d
MorphIcon(
icon: isHome ? MorphIconsLucide.house : MorphIconsLucide.heart,
)
// Mixed — IconData ↔ String d
MorphIcon(
icon: isHome ? Icons.home : MorphIconsLucide.x,
)
// Controlled — scrub progress yourself
MorphIcon.controlled(
from: Icons.home,
icon: Icons.settings,
progress: t,
)
// Typed factories — static safety
MorphIcon.font(icon: Icons.home)
MorphIcon.svg(icon: MorphIconsLucide.house)
MorphIcon.controlledFont(from: Icons.home, icon: Icons.settings, progress: t)
// Imperative
final key = GlobalKey<MorphIconState>();
MorphIcon(key: key, icon: Icons.home);
key.currentState?.morphTo(Icons.favorite);
Flutter iframe above uses the real morphicons_flutter package compiled to web (same MorphIcon(icon: Icons.home) call).
Curated resolver table — lib/src/icon_data_resolver.dart
home 0xe318 · favorite 0xe25b · star 0xe5f9 · search 0xe567 · settings 0xe57f · menu 0xe3dc · close 0xe16a · check 0xe156
Add entries via tool/generate_font_map.dart-style codegen (TTF → Map<IconData,String>) — see report §5. Runtime stays sync.