elk_icon_picker 0.2.0
elk_icon_picker: ^0.2.0 copied to clipboard
A themeable Flutter picker for Lucide icons: inline widget or bottom sheet, with search and categories. Icons come from elk_stroke_icons.
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.