elk_icon_picker
A themeable Flutter picker for Lucide icons, usable inline or as a bottom sheet, with search and category tabs. The icons come from elk_stroke_icons, which draws them with CustomPainter (no fonts or SVG assets). It is re-exported, so one import gives you both.
Unofficial. Not affiliated with or endorsed by the Lucide project.
Features
- 📦 Minimal Dependencies: Only depends on its sibling package
elk_stroke_icons. Noflutter_svg, icon fonts, or other third-party packages. - 🎨 Fully Customizable: Control colors, sizes, stroke widths, rounded caps/joins, and fonts — globally via
ThemeDataor per-widget. - 🔍 Searchable: Built-in fuzzy search and categorized icon browsing.
- 📱 Adaptive UI: Works beautifully as an inline widget or a professional bottom sheet.
Getting Started
Add elk_icon_picker to your pubspec.yaml:
dependencies:
elk_icon_picker: ^0.2.0
Usage
Show as a Modal Sheet
The quickest way to integrate the picker is using the showElkIconPicker helper function.
import 'package:elk_icon_picker/elk_icon_picker.dart';
void _selectIcon() async {
final selection = await showElkIconPicker(
context,
currentSelection: _mySelection,
selectedColor: Theme.of(context).primaryColor,
);
if (selection is LucideIconSelection) {
setState(() {
_mySelection = selection;
});
}
}
Use Inline Widget
You can also embed the ElkIconPicker directly into your layouts.
ElkIconPicker(
onSelected: (selection) {
print('Selected: ${selection.name}');
},
crossAxisCount: 6,
borderRadius: 12.0,
)
Displaying the Selected Icon
Use the LucideIcon widget (from elk_stroke_icons, re-exported here) to render the selected icon data. If you only need to show icons, without the picker, depend on elk_stroke_icons directly.
LucideIcon(
LucideIcons.house,
size: 32,
color: Colors.blue,
strokeWidth: 2.5,
)
| Property | Description | Default |
|---|---|---|
data |
Required. The icon data to render (e.g., LucideIcons.house) |
- |
size |
The size of the icon | 24 |
color |
The color of the icon's stroke | IconTheme.of(context).color |
strokeWidth |
The width of the icon's stroke | 2.0 |
rounded |
Whether to use rounded stroke caps and joins | true |
Saving and Restoring a Selection
Store the icon's name (e.g. 'circle-check') and look it up again with
IconSearchService.findByName. Lucide occasionally renames icons upstream;
old names (e.g. 'smile', now 'face-slightly-smiling') still resolve via
kLucideIconAliases, so saved data keeps working after an icon set update.
final savedName = selection.data.name; // store this string
final icon = IconSearchService.findByName(savedName) ?? LucideIcons.circleQuestionMark;
Renamed icons also keep deprecated constants (e.g. LucideIcons.smile), so
existing code still compiles and the analyzer points you at the new name.
Theming
You can style all pickers in one place by adding ElkIconPickerThemeData to your app's ThemeData.extensions. This integrates naturally with Material 3 light/dark themes — just provide a different extension in your dark theme.
MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
extensions: [
ElkIconPickerThemeData(
selectedColor: Colors.teal,
backgroundColor: Colors.white,
iconColor: Colors.black54,
borderRadius: 12.0,
searchStyle: TextStyle(fontFamily: 'Inter'),
searchHintStyle: TextStyle(fontFamily: 'Inter', color: Colors.grey),
tabLabelStyle: TextStyle(fontFamily: 'Inter', fontWeight: FontWeight.w600),
titleStyle: TextStyle(fontFamily: 'Inter', fontSize: 18, fontWeight: FontWeight.bold),
emptyStateStyle: TextStyle(fontFamily: 'Inter', color: Colors.grey),
),
],
),
darkTheme: ThemeData.dark(useMaterial3: true).copyWith(
extensions: [
ElkIconPickerThemeData(
selectedColor: Colors.tealAccent,
backgroundColor: Color(0xFF1E1E1E),
iconColor: Colors.white70,
),
],
),
)
Values are resolved in this priority order:
- Explicit constructor param — highest priority, overrides everything
ElkIconPickerThemeDataextension — global app theme- Material 3
ColorScheme/TextTheme— automatic fallback
So you can set defaults globally and still override individual pickers when needed:
// Uses the global theme
showElkIconPicker(context);
// Overrides just the selected color for this instance
showElkIconPicker(context, selectedColor: Colors.orange);
ElkIconPickerThemeData properties
| Property | Description |
|---|---|
backgroundColor |
Background color of the picker and bottom sheet container |
iconColor |
Color for unselected icons |
selectedColor |
Color for the selected icon highlight |
selectedIconColor |
Color of the icon stroke itself when selected |
iconSize |
Rendered size of each icon in the grid (default 24.0) |
borderRadius |
Corner radius for selection indicators and the sheet |
searchStyle |
Text style for the search field input |
searchHintStyle |
Text style for the search field hint text |
tabLabelStyle |
Text style for category tab labels |
titleStyle |
Text style for the bottom sheet title ("Select Icon") |
emptyStateStyle |
Text style for the "no results" message |
iconStrokeWidth |
Stroke width for icon rendering (e.g. 1.5 for thin, 2.5 for bold) |
iconRounded |
Whether icons use rounded stroke caps and joins |
showSearch |
Whether the search bar is shown by default |
showCategories |
Whether category tabs are shown by default |
categoryStyle |
Default category tab layout style (both, iconsOnly, textOnly) |
allowUserToggleCategories |
Whether to show the toggle button for categories |
crossAxisCount |
Global override for the number of columns (default: adaptive) |
allowedCategoryIds |
Global filter for which categories to show |
categoryIconSize |
Rendered size of icons in the category tab bar (default 18.0) |
categoryTextSpacing |
Spacing between icon and text in tabs (default 8.0) |
gridPadding |
Padding around the main icon grid (default EdgeInsets.all(16.0)) |
searchBarFillColor |
Fill color of the search input field |
sheetHandleColor |
Color of the drag handle in the bottom sheet |
sheetTitleBarColor |
Background color of the title bar area in the sheet |
tabIndicatorColor |
Color of the selected tab underline/indicator |
categoryTabWidth |
Fixed width for each category tab — controls how many are visible at once |
showCategoryFade |
Show gradient fade at the edges of the tab bar when more tabs exist (default true) |
categoryFadeColor |
Color of the tab bar edge fade (defaults to the picker's background color) |
swipeCategoryOnGrid |
Allow horizontal swipes on the icon grid to change category (default true) |
swipeVelocityThreshold |
Minimum swipe velocity (px/s) to trigger a category change (default 300.0) |
Category navigation
Edge fade
When the category tab bar has more tabs than fit in the viewport, gradient overlays appear at the left and/or right edges to signal that the bar is scrollable. The fade color automatically matches the picker background, so it blends seamlessly. Set showCategoryFade: false to disable it, or categoryFadeColor to override the color.
Controlling visible tab count
Use categoryTabWidth to give all tabs a fixed width. For example, setting categoryTabWidth: 110 on a 360dp-wide picker shows approximately three tabs at a time. Leave it null (the default) for natural intrinsic widths.
Swipe to change category
By default, a fast horizontal swipe anywhere on the icon grid advances or retreats the active category — the same gesture that a tab bar swipe would produce. Flutter's gesture arena handles the disambiguation: vertical scrolling of the grid and horizontal category switching work without conflict. Use swipeCategoryOnGrid: false to disable, or swipeVelocityThreshold to tune the sensitivity.
Customization
Per-widget constructor params work exactly as before. These take priority over any theme extension.
| Property | Description | Default |
|---|---|---|
onSelected |
Required. Callback when an icon is selected | - |
currentSelection |
The currently selected icon to highlight | null |
crossAxisCount |
Number of columns in the grid | from theme / adaptive |
iconColor |
Color for unselected icons | from theme |
selectedColor |
Color for the selected icon/indicator highlight | from theme |
selectedIconColor |
Color of the icon stroke itself when selected | from theme |
backgroundColor |
Background color of the picker | from theme |
borderRadius |
Curvature of selection boxes | from theme |
iconStrokeWidth |
Stroke width for icon rendering | from theme |
iconRounded |
Rounded stroke caps/joins on icons | from theme |
iconSize |
Size of icons in the grid | from theme |
searchBarFillColor |
Background color for the search bar | from theme |
tabIndicatorColor |
Color of the category tab indicator | from theme |
showSearch |
Whether to show the search bar | from theme |
showCategories |
Whether to show the category tabs | from theme |
categoryStyle |
Tab layout: both, iconsOnly, or textOnly |
from theme |
allowedCategoryIds |
Limit which categories appear | from theme |
allowUserToggleCategories |
Show a user toggle for categories | from theme |
categoryIconSize |
Size of icons in the category tabs | from theme |
categoryTextSpacing |
Gap between icon and text in tabs | from theme |
gridPadding |
Padding around the icon grid | from theme |
scrollController |
Controller for the icon grid scroll | null |
categoryTabWidth |
Fixed width for each category tab | from theme |
showCategoryFade |
Show gradient fade at the tab bar edges | from theme (true) |
categoryFadeColor |
Color of the tab bar edge fade | from theme / bg |
swipeCategoryOnGrid |
Horizontal swipe on grid changes category | from theme (true) |
swipeVelocityThreshold |
Min swipe velocity to trigger category change | from theme (300.0) |
License
- The picker code is licensed under the MIT License.
- Lucide icon data ships in
elk_stroke_iconsunder the ISC License.