string_to_icon 1.0.0
string_to_icon: ^1.0.0 copied to clipboard
A simple, lightweight Flutter package to map string names to Material Design Icons, with fallback support for unknown icons.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:string_to_icon/string_to_icon.dart';
void main() {
runApp(const StringToIconExampleApp());
}
class StringToIconExampleApp extends StatelessWidget {
const StringToIconExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'String to Icon Example',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
debugShowCheckedModeBanner: false,
home: const IconDemoPage(),
);
}
}
class IconDemoPage extends StatefulWidget {
const IconDemoPage({super.key});
@override
State<IconDemoPage> createState() => _IconDemoPageState();
}
class _IconDemoPageState extends State<IconDemoPage> {
final TextEditingController _textController = TextEditingController();
String _currentIconName = 'home';
IconData _currentIcon = Icons.home;
@override
void initState() {
super.initState();
_textController.text = _currentIconName;
}
@override
void dispose() {
_textController.dispose();
super.dispose();
}
void _updateIcon() {
setState(() {
_currentIconName = _textController.text.trim();
_currentIcon = IconMapper.getIconData(_currentIconName);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('String to Icon Demo'),
backgroundColor: Colors.blue,
elevation: 2,
),
body: SafeArea(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header Card
Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.circular(50),
),
child: Icon(
_currentIcon,
size: 80,
color: Colors.blue,
),
),
const SizedBox(height: 16),
Text(
'Current Icon: $_currentIconName',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: IconMapper.isSupported(_currentIconName)
? Colors.green.withOpacity(0.1)
: Colors.orange.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: IconMapper.isSupported(_currentIconName)
? Colors.green
: Colors.orange,
width: 1,
),
),
child: Text(
IconMapper.isSupported(_currentIconName)
? 'Icon found ✓'
: 'Icon not found - using default ⚠️',
style: TextStyle(
color: IconMapper.isSupported(_currentIconName)
? Colors.green.shade700
: Colors.orange.shade700,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
),
],
),
),
),
const SizedBox(height: 16),
// Input Section Card
Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Try Different Icon Names:',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
TextField(
controller: _textController,
decoration: InputDecoration(
labelText: 'Icon Name',
hintText: 'e.g., home, map, settings',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
color: Colors.blue,
width: 2,
),
),
isDense: true,
prefixIcon: const Icon(Icons.search),
suffixIcon: _textController.text.isNotEmpty
? IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
_textController.clear();
_updateIcon();
},
)
: null,
),
onChanged: (_) => _updateIcon(),
onSubmitted: (_) => _updateIcon(),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(6),
),
child: Text(
'Total supported icons: ${IconMapper.getSupportedIconCount()}',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontWeight: FontWeight.w500,
),
),
),
],
),
),
),
const SizedBox(height: 16),
// Examples Section Card
Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Popular Examples:',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
'home', 'map', 'settings', 'notifications',
'person', 'analytics', 'batteryfull', 'wifi',
'bluetooth', 'alarm', 'warning', 'feedback',
'favorite', 'star', 'phone', 'email',
'camera', 'musicnote', 'playarrow'
].map((iconName) => ExampleChip(
iconName: iconName,
onTap: () {
_textController.text = iconName;
_updateIcon();
},
)).toList(),
),
],
),
),
),
const SizedBox(height: 16),
// All Icons Grid Card
Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(20.0),
child: Row(
children: [
Icon(
Icons.grid_view,
color: Colors.blue,
size: 24,
),
const SizedBox(width: 8),
Text(
'All Available Icons:',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
),
SizedBox(
height: 400, // Increased height for better viewing
child: const IconGrid(),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Tap any icon to see its name',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.grey[600],
fontStyle: FontStyle.italic,
),
),
),
],
),
),
const SizedBox(height: 20), // Bottom padding
],
),
),
),
),
);
}
}
class ExampleChip extends StatelessWidget {
final String iconName;
final VoidCallback onTap;
const ExampleChip({
super.key,
required this.iconName,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(20),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Colors.blue.withOpacity(0.3),
width: 1,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
IconMapper.getIconData(iconName),
size: 18,
color: Colors.blue,
),
const SizedBox(width: 6),
Text(
iconName,
style: const TextStyle(
color: Colors.blue,
fontWeight: FontWeight.w500,
fontSize: 12,
),
),
],
),
),
),
);
}
}
class IconGrid extends StatelessWidget {
const IconGrid({super.key});
@override
Widget build(BuildContext context) {
final iconNames = IconMapper.getSupportedIconNames();
return GridView.builder(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
physics: const BouncingScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 0.9,
),
itemCount: iconNames.length,
itemBuilder: (context, index) {
final iconName = iconNames[index];
final iconData = IconMapper.getIconData(iconName);
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
child: InkWell(
onTap: () {
// Copy icon name to clipboard
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
Icon(iconData, color: Colors.white, size: 20),
const SizedBox(width: 8),
Text('Icon: $iconName'),
],
),
duration: const Duration(seconds: 2),
backgroundColor: Colors.blue,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
margin: const EdgeInsets.all(16),
),
);
},
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
iconData,
size: 28,
color: Colors.blue.shade700,
),
const SizedBox(height: 4),
Text(
iconName,
style: const TextStyle(
fontSize: 9,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
),
);
},
);
}
}