Introduction#
A hand-crafted icon set covering 37 categories with six distinct visual styles including two duotone variants. Fully bundled offline in one Flutter package.
Solar Iconkit is a Flutter package that bundles the entire Solar icon set by 480 Design — 1,269 icons across 6 native styles (7,614 SVG variants total) — behind a single, type-safe SolarIcon widget. MIT-licensed, works offline, no setup beyond adding the dependency. Preview every icon on the browser.
Installation#
Add to pubspec.yaml:
dependencies:
solar_iconkit: ^1.0.0Or use the CLI:
flutter pub add solar_iconkit
# or
flutter pub getImport wherever icons are needed:
import 'package:solar_iconkit/solar_iconkit.dart';Consumers don't need to declare any assets in their own pubspec — the package bundles the SVGs and Flutter resolves them via the packages/scheme automatically. You also don't need to add flutter_svgdirectly; it's pulled in transitively.
Quick start#
The three most common shapes:
import 'package:flutter/material.dart';
import 'package:solar_iconkit/solar_iconkit.dart';
// Minimum — default linear style, size from IconTheme (24 px fallback).
SolarIcon(SolarIcons.home2)
// Explicit style and size.
SolarIcon(
SolarIcons.heart,
style: SolarIconStyle.bold,
size: 32,
)
// Fully-specified — every parameter that matters.
SolarIcon(
SolarIcons.rocket,
style: SolarIconStyle.boldDuotone,
size: 48,
color: Theme.of(context).colorScheme.primary,
opacity: 0.9,
semanticLabel: 'Launch',
)No init() call, no font loading, no asset path to remember, no flutter_svg import required from consumer code.
Widget API#
SolarIcon is a const-constructible, immutable StatelessWidget. Every parameter is optional except the required positional name.
| Parameter | Type | Default |
|---|---|---|
| name | String | required |
| style | SolarIconStyle | linear |
| size | double? | IconTheme → 24 |
| color | Color? | IconTheme → black87 |
| opacity | double | 1.0 |
| semanticLabel | String? | null |
| textDirection | TextDirection? | Directionality.of |
| matchTextDirection | bool | false |
| fit | BoxFit | BoxFit.contain |
| alignment | AlignmentGeometry | Alignment.center |
| blendMode | BlendMode | BlendMode.srcIn |
| shadows | List<Shadow>? | null |
| key | Key? | null |
How color and size resolve#
Matches Flutter's built-in Icon widget resolution order:
- If passed explicitly on the widget, use that value.
- Otherwise read
IconTheme.of(context). - Fall back to defaults (24 px,
Color(0xDD000000)).
Opacity is multiplied across both sources: widget opacity: 0.5 combined with IconTheme.opacity: 0.8 gives an effective alpha of 0.4.
Const-constructibility#
SolarIcon's constructor is const. Instances used in const contexts are cached by the Flutter framework and never trigger unnecessary rebuilds.
class HomeButton extends StatelessWidget {
const HomeButton({super.key});
@override
Widget build(BuildContext context) {
// `const` here means Flutter reuses the same widget instance
// across every rebuild — zero allocation cost.
return const SolarIcon(SolarIcons.home2);
}
}Static helpers#
SolarIcon.assetPath(name, style)— returns the raw asset path. Useful forprecachePictureor building your ownSvgAssetLoader.SolarIcon.packageName— the string'solar_iconkit'. Useful when passing toSvgAssetLoaderdirectly.
Debug validation (new in 1.0.2)#
In debug and profile builds, SolarIcon throws a clear FlutterErrorwhen constructed with a name that isn't in the Solar catalog. The assertion is stripped from release builds so there is zero production cost.
// Debug: throws FlutterError with a stack trace pointing here.
// Release: renders a blank square (same as pre-1.0.2 behaviour).
SolarIcon('totally-typoed-name')Prefer the SolarIcons.xxx constants — the Dart analyzer catches typos at compile time, before you ever hit the runtime assertion.
Styles#
Six native styles per icon. Each has a distinct visual character and pairs with specific UI roles.
linear for chrome, promote to bold for active or selected states, and reserve boldDuotone for feature moments and hero areas.Recipes#
Common Flutter patterns using SolarIcon. Copy any snippet and adapt.
Lists and tiles#
ListTile(
leading: SolarIcon(
SolarIcons.folder,
style: SolarIconStyle.boldDuotone,
color: Colors.amber,
),
title: const Text('Documents'),
subtitle: const Text('12 files'),
trailing: SolarIcon(SolarIcons.altArrowRight, size: 18),
onTap: () {},
)Inside a drawer with a leading style set:
IconTheme(
data: const IconThemeData(size: 22),
child: Drawer(
child: ListView(
children: const [
DrawerHeader(child: Text('Menu')),
ListTile(leading: SolarIcon(SolarIcons.home2), title: Text('Home')),
ListTile(leading: SolarIcon(SolarIcons.settings), title: Text('Settings')),
ListTile(leading: SolarIcon(SolarIcons.logout), title: Text('Sign out')),
],
),
),
)App bars#
AppBar(
leading: IconButton(
icon: const SolarIcon(SolarIcons.hamburgerMenu),
onPressed: () => Scaffold.of(context).openDrawer(),
),
title: const Text('Solar'),
actions: [
IconButton(
icon: const SolarIcon(SolarIcons.bell),
onPressed: () {},
),
IconButton(
icon: const SolarIcon(SolarIcons.settings),
onPressed: () {},
),
],
)A tab bar with icon-only tabs:
TabBar(
tabs: const [
Tab(icon: SolarIcon(SolarIcons.gallery)),
Tab(icon: SolarIcon(SolarIcons.videoLibrary)),
Tab(icon: SolarIcon(SolarIcons.musicLibrary2)),
],
)Text fields#
TextField(
decoration: InputDecoration(
prefixIcon: Padding(
padding: const EdgeInsets.all(12),
child: SolarIcon(SolarIcons.magnifier, size: 18),
),
suffixIcon: IconButton(
icon: const SolarIcon(SolarIcons.closeCircle, size: 18),
onPressed: _clear,
),
hintText: 'Search...',
border: const OutlineInputBorder(),
),
)prefixIcon / suffixIcon use a fixed size internally; wrap in Padding or set prefixIconConstraints if you need custom sizing.
Coloring#
Four progressively integrated approaches:
// 1. Explicit color.
SolarIcon(SolarIcons.heart, color: Colors.red)
// 2. Inherited from an ambient IconTheme (matches Flutter's Icon widget).
IconTheme(
data: const IconThemeData(color: Colors.blueAccent),
child: SolarIcon(SolarIcons.star),
)
// 3. Theme-driven — recolors on brightness / theme changes.
SolarIcon(
SolarIcons.settings,
color: Theme.of(context).colorScheme.primary,
)
// 4. Duotone with color-preserved accent — the 50% opacity accent
// remains translucent while the base takes your color.
SolarIcon(
SolarIcons.rocket,
style: SolarIconStyle.boldDuotone,
color: Colors.deepPurple,
)IconTheme scoping#
SolarIcon reads IconTheme.of(context)when the widget doesn't set size, color, or opacity explicitly. Wrap a subtree to theme every SolarIcon inside it at once:
IconTheme(
data: const IconThemeData(color: Colors.indigo, size: 20),
child: Row(children: const [
SolarIcon(SolarIcons.home2), // picks up indigo + size 20
SolarIcon(SolarIcons.heart), // same
]),
)Under MaterialApp, IconTheme.of(context) returns Theme.of(context).iconTheme. Setting iconTheme on ThemeData themes every SolarIcon in the entire app:
MaterialApp(
theme: ThemeData(
iconTheme: const IconThemeData(
color: Colors.black87,
size: 24,
opacity: 0.9,
),
),
home: const MyApp(),
)Drop shadows#
Mirroring the shadowsparameter on Flutter's built-in Icon widget: pass a List<Shadow> to paint blurred, offset copies of the icon behind the main render.
SolarIcon(
SolarIcons.heart,
style: SolarIconStyle.bold,
color: Colors.red,
shadows: const [
Shadow(
color: Colors.black26,
blurRadius: 6,
offset: Offset(0, 2),
),
],
)Cheap for one or two shadows. Avoid long shadow lists in scrolling contexts — each shadow is rendered as a separate filtered pass.
Blend modes#
Defaults to BlendMode.srcIn— the standard "recolor an icon" behaviour that replaces the icon's color while preserving alpha.
// Default — flat color replacement.
SolarIcon(SolarIcons.rocket, color: Colors.blue)
// Multiply — icon interacts with the background instead of
// replacing pixels. Great over textured / gradient backgrounds.
SolarIcon(
SolarIcons.rocket,
color: Colors.blue,
blendMode: BlendMode.multiply,
)
// Dst — keep the SVG's native colors, ignore the color argument.
SolarIcon(
SolarIcons.rocket,
blendMode: BlendMode.dst,
)Animated transitions#
Cross-fade between styles:
AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: SolarIcon(
SolarIcons.heart,
key: ValueKey(_liked ? 'bold' : 'linear'),
style: _liked ? SolarIconStyle.bold : SolarIconStyle.linear,
color: _liked ? Colors.red : null,
),
)Animate size with a tween:
TweenAnimationBuilder<double>(
tween: Tween(begin: 24, end: _big ? 48 : 24),
duration: const Duration(milliseconds: 250),
curve: Curves.easeOutCubic,
builder: (context, size, _) =>
SolarIcon(SolarIcons.home2, size: size),
)Custom effects#
SolarIconcomposes cleanly with Flutter's paint pipeline. Combine it with ShaderMask, Container decorations, BackdropFilter, and the built-in shadows parameter to build app-icon-grade visual effects — no external libraries required.
Every effect below is a self-contained widget. Copy the code, swap the icon, tune the colors.
Gradient sheen#
Paint the icon with a linear gradient using a ShaderMask. The gradient replaces the icon's color while preserving alpha — the two-tone effect from duotone styles stays intact.
ShaderMask(
shaderCallback: (bounds) => const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFFDBA74), Color(0xFFEC4899)],
).createShader(bounds),
blendMode: BlendMode.srcIn,
child: const SolarIcon(
SolarIcons.heart,
style: SolarIconStyle.boldDuotone,
size: 56,
color: Colors.white, // required for ShaderMask srcIn
),
)App-icon tile#
A vibrant rounded tile with a matching-color glow shadow. This is the pattern used by native app icons on iOS and Android — great for feature cards and home-screen-style grids.
Container(
width: 72,
height: 72,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFF97316), Color(0xFFEA580C)],
),
boxShadow: [
BoxShadow(
color: const Color(0xFFF97316).withValues(alpha: 0.5),
blurRadius: 32,
spreadRadius: 2,
offset: const Offset(0, 14),
),
],
),
child: const Center(
child: SolarIcon(
SolarIcons.rocket,
style: SolarIconStyle.boldDuotone,
size: 36,
color: Colors.white,
),
),
)Colored glow#
A single soft drop shadow in the icon's own color makes it feel lit-from-within. Pair with SolarIconStyle.boldDuotone for maximum impact.
const SolarIcon(
SolarIcons.star,
style: SolarIconStyle.boldDuotone,
size: 56,
color: Color(0xFFFACC15),
shadows: [
Shadow(
color: Color(0x80FACC15),
blurRadius: 24,
offset: Offset(0, 6),
),
],
)Neon#
Stack multiple shadows of increasing blur radius to create a bright neon halo. Best against a dark background where the color bleed is visible.
const SolarIcon(
SolarIcons.star,
style: SolarIconStyle.bold,
size: 56,
color: Colors.white,
shadows: [
Shadow(color: Color(0xFF22D3EE), blurRadius: 6),
Shadow(color: Color(0xFF22D3EE), blurRadius: 16),
Shadow(color: Color(0xFF22D3EE), blurRadius: 32),
],
)Frosted glass#
A translucent white icon on a colored gradient with a subtle border. Emulates the iOS 'glass' effect popular in 2024 design systems.
Container(
width: 72,
height: 72,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFF6366F1),
Color(0xFF8B5CF6),
Color(0xFFEC4899),
],
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(22),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.15),
border: Border.all(
color: Colors.white.withValues(alpha: 0.3),
width: 1,
),
borderRadius: BorderRadius.circular(22),
),
child: const Center(
child: SolarIcon(
SolarIcons.magicStick3,
style: SolarIconStyle.boldDuotone,
size: 36,
color: Colors.white,
),
),
),
),
),
)Outline glow (dual layer)#
Layer a bold-duotone icon over a semi-transparent Bold variant to fake a soft outer stroke. Cheap trick, striking result.
Stack(
alignment: Alignment.center,
children: [
// Faux outline — a larger, low-alpha bold icon behind.
SolarIcon(
SolarIcons.heart,
style: SolarIconStyle.bold,
size: 60,
color: const Color(0xFFFB7185).withValues(alpha: 0.25),
),
// Main icon on top.
const SolarIcon(
SolarIcons.heart,
style: SolarIconStyle.boldDuotone,
size: 48,
color: Color(0xFFFB7185),
),
],
)ShaderMask, Container, Stack) are non-const, but the SolarIcon inside stays const — Flutter still avoids unnecessary rebuilds.Icon naming#
Solar names use kebab-case (e.g. alt-arrow-down, home-2). The Flutter package converts them to camelCase Dart identifiers on SolarIcons.
| Input | Dart identifier | Rule |
|---|---|---|
| home | home | plain identifier |
| home-2 | home2 | kebab → camelCase |
| alt-arrow-down | altArrowDown | multi-word camelCase |
| 4k | i4k | leading digit prefixed with 'i' |
| case | caseIcon | Dart reserved word → suffix 'Icon' |
| duplicate | duplicate2 | collision → numeric suffix |
Iconify identifiers used at runtime follow the pattern solar:<name>-<style>:
solar:home-2-linear → Linear style
solar:home-2-outline → Outline style
solar:home-2-broken → Broken stroke
solar:home-2-bold → Bold filled
solar:home-2-line-duotone → Line duotone
solar:home-2-bold-duotone → Bold duotoneSolarIcons class carries a Dartdoc comment (Solar icon "X".) so autocomplete / hover in your IDE reveals the source name.Accessibility#
Screen readers treat unlabeled decorative icons as skippable. Provide a semanticLabel on any icon that carries meaning without a visible text label:
IconButton(
onPressed: _delete,
icon: SolarIcon(
SolarIcons.trashBinTrash,
semanticLabel: 'Delete this item',
),
)Do not set semanticLabel on icons that sit next to visible descriptive text — the label would be announced twice by the screen reader.
When semanticLabel is null, SolarIcon automatically wraps the rendered SVG in ExcludeSemantics. Decorative icons produce zero announcements. This is the correct default for the majority of icons in a UI.
RTL and text direction#
Directional icons (arrows, chevrons, forward, back) should mirror horizontally under right-to-left locales. Set matchTextDirection: true:
SolarIcon(
SolarIcons.altArrowRight,
matchTextDirection: true,
)SolarIcon reads Directionality.of(context) to determine current direction. Pass textDirection to override for a specific instance:
SolarIcon(
SolarIcons.altArrowRight,
matchTextDirection: true,
textDirection: TextDirection.rtl, // force flip regardless of context
)Non-directional icons (settings, heart, home) never mirror — leave matchTextDirection at its default false.
Performance#
- Const-constructible. Reuse instances in
constcontexts to skip allocation. Rebuilds are cheap becauseSvgPicture.assetcaches its decoded picture per icon path. - Strict layout box. A
SizedBox.squarewrapper enforces the requested size — icons never overflow or shrink a parent unexpectedly. - Per-icon decode cost.
flutter_svgparses each SVG and rasterises to a cachedPicturethe first time it renders — typically well under 1 ms. Subsequent instances of the same icon reuse the cached picture. - Startup cost is zero. SVG files are read only when the corresponding widget first mounts. Package assets are bundled but not decoded eagerly.
- Semantic hygiene. Decorative icons wrap in
ExcludeSemanticsautomatically — screen readers do not receive noise from every icon on screen.
Precaching in long lists#
As new cards appear on-screen, each unique icon decodes once. In a long list, that first frame can jank. Precache visible icons up-front:
Future<void> precacheSolarIcons(
BuildContext context,
List<String> names,
SolarIconStyle style,
) async {
for (final name in names) {
await precachePicture(
SvgAssetLoader(
'assets/icons/${style.folderName}/$name.svg',
packageName: 'solar_iconkit',
),
context,
);
}
}Call from an initState or route-enter callback for icons visible in the first screen.
Bundle size#
The full asset bundle is ~6.1 MB of raw SVG bytes across all six styles (7,614 individually-minified files). Compressed on the pub.dev archive: about 18 MB including the example app, tests, and screenshots.
Flutter's tree-shaker doesn't remove unreferenced assets because SolarIcon resolves paths at runtime — so the whole set ships by default.
Trimming unused styles#
If your app uses only a couple of styles, fork the package (or use a dependency_overrides path) and edit pubspec.yaml:
flutter:
assets:
- assets/icons/linear/
- assets/icons/bold/
# remove the styles you do not useThen delete the unused style folders under assets/icons/. The build only includes assets you declare. Cutting from 6 styles to 2 removes roughly two thirds of the bundle.
build_runner step that scans consumer code for SolarIcons.xxx references and emits an asset subset) is planned for v2.0. Track progress at the issues page.Testing#
Basic widget test:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:solar_iconkit/solar_iconkit.dart';
void main() {
testWidgets('SolarIcon renders with expected props', (tester) async {
await tester.pumpWidget(const MaterialApp(
home: Scaffold(
body: SolarIcon(
SolarIcons.heart,
style: SolarIconStyle.bold,
size: 48,
),
),
));
final widget = tester.widget<SolarIcon>(find.byType(SolarIcon));
expect(widget.name, 'heart');
expect(widget.style, SolarIconStyle.bold);
expect(widget.size, 48);
});
}Golden tests are supported out of the box — the widget produces deterministic output for any (name, style, size, color) combination.
testWidgets('boldDuotone home-2 golden', (tester) async {
await tester.pumpWidget(const MaterialApp(
home: Center(
child: SolarIcon(
SolarIcons.home2,
style: SolarIconStyle.boldDuotone,
size: 96,
),
),
));
await tester.pumpAndSettle();
await expectLater(
find.byType(SolarIcon),
matchesGoldenFile('goldens/home-2-boldDuotone.png'),
);
});Regenerate goldens with flutter test --update-goldens. Lock to a single platform (usually Linux) for cross-platform test consistency — font and SVG rasterization can vary subtly between OSes.
Troubleshooting#
The icon renders as an empty space.
FlutterError in debug builds — if you see nothing at all, you might be running a release build (assertions stripped) or the name might be off by one character. Prefer the SolarIcons.<name> constants so the analyzer catches typos."Unable to load asset: assets/icons/linear/xxx.svg" at runtime.
"A value of type 'List<dynamic>' can't be assigned to 'List<String>'."
SolarIcons.all as List<dynamic> right after the generated file is loaded. Force-refresh with flutter clean && flutter pub get, then restart the Dart analysis server in your IDE (VS Code: Dart: Restart Analysis Server).Icons look pixelated.
Icon color doesn't update on theme change.
Theme.of(context) (a rebuild-tracked source), not a captured constant. SolarIcon rebuilds when its color prop changes; the surrounding widget must also rebuild when the theme changes.flutter_svg version conflict with another package.
solar_iconkit pins flutter_svg: ^2.0.10. If a peer package pins a lower version, run flutter pub upgrade flutter_svg. If two packages pin incompatible ranges, use dependency_overrides to force a single version.Versioning#
solar_iconkit follows Semantic Versioning 2.0.0. Since 1.0, the API is committed to stability:
- 1.x.y patch bumps — bug fixes, doc updates, and non-breaking internal improvements.
- 1.x minor bumps — new optional widget parameters, new icons pulled from Solar upstream.
- 2.0 — reserved for breaking API changes (per-icon tree-shaking, per-style sub-packages, etc.).
Pin ^1.0.0 to safely receive every 1.x update. Consumer code that works on 1.0.0 will keep working on 1.99.9.
Credits#
- Icons — Solar icon set by 480 Design (MIT).
- Icon data — Provided by Iconify (MIT).
- Flutter package — Published on pub.dev.