dynamic_app_icon_flutter_plus 0.0.6
dynamic_app_icon_flutter_plus: ^0.0.6 copied to clipboard
Dynamic App Icon For Flutter
example/lib/main.dart
import 'package:dynamic_app_icon_flutter_plus/dynamic_app_icon_flutter.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Dynamic App Icon Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.light,
),
useMaterial3: true,
cardTheme: CardThemeData(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.dark,
),
useMaterial3: true,
cardTheme: CardThemeData(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
),
themeMode: ThemeMode.system,
home: const IconSelectorScreen(),
);
}
}
class IconSelectorScreen extends StatefulWidget {
const IconSelectorScreen({super.key});
@override
State<IconSelectorScreen> createState() => _IconSelectorScreenState();
}
class _IconSelectorScreenState extends State<IconSelectorScreen> {
bool _isSupported = false;
bool _isLoading = true;
List<String> _availableIcons = [];
String? _currentIcon;
bool _isChangingIcon = false;
@override
void initState() {
super.initState();
_loadData();
}
Future<void> _loadData() async {
setState(() {
_isLoading = true;
});
try {
final supported = await DynamicAppIconFlutter.supportsAlternateIcons;
final icons = await DynamicAppIconFlutter.getAvailableIcons();
final current = await DynamicAppIconFlutter.getAlternateIconName();
setState(() {
_isSupported = supported;
_availableIcons = icons;
_currentIcon = current;
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
});
if (mounted) {
debugPrint('Error loading data: $e');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error loading data: $e'),
backgroundColor: Colors.red,
),
);
}
}
}
Future<void> _setIcon(String? iconName) async {
setState(() {
_isChangingIcon = true;
});
try {
await DynamicAppIconFlutter.setAlternateIconName(iconName);
await _loadData();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
iconName == null
? 'Default icon restored'
: 'Icon changed to "$iconName"',
),
backgroundColor: Colors.green,
behavior: SnackBarBehavior.floating,
),
);
}
} catch (e) {
debugPrint("Error changing icon: $e");
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error changing icon: $e'),
backgroundColor: Colors.red,
),
);
}
} finally {
if (mounted) {
setState(() {
_isChangingIcon = false;
});
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dynamic App Icon'),
centerTitle: true,
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _loadData,
tooltip: 'Refresh',
),
],
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Support Status Card
_buildSupportStatusCard(),
const SizedBox(height: 16),
// Current Icon Card
_buildCurrentIconCard(),
const SizedBox(height: 16),
// Available Icons Card
if (_isSupported) _buildAvailableIconsCard(),
],
),
),
);
}
Widget _buildSupportStatusCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: _isSupported
? Colors.green.withValues(alpha: 0.1)
: Colors.orange.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: Icon(
_isSupported ? Icons.check_circle : Icons.info,
color: _isSupported ? Colors.green : Colors.orange,
size: 32,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Platform Support',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
_isSupported
? 'Dynamic icons are supported on this platform'
: 'Dynamic icons are not supported on this platform',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey[600],
),
),
],
),
),
],
),
),
);
}
Widget _buildCurrentIconCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.apps,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(width: 12),
Text(
'Current Icon',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
borderRadius: BorderRadius.circular(12),
),
child: Icon(
Icons.android,
color: Theme.of(context).colorScheme.onPrimary,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_currentIcon ?? 'Default',
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
_currentIcon == null
? 'Using the default app icon'
: 'Alternate icon is active',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
],
),
),
],
),
),
);
}
Widget _buildAvailableIconsCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.palette,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(width: 12),
Text(
'Available Icons',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 16),
if (_availableIcons.isEmpty)
Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: Column(
children: [
Icon(
Icons.info_outline,
size: 48,
color: Colors.grey[400],
),
const SizedBox(height: 8),
Text(
'No alternate icons available',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey[600],
),
),
const SizedBox(height: 4),
Text(
'Configure icons in AndroidManifest.xml',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.grey[500],
),
),
],
),
),
)
else
..._availableIcons.map((iconName) => _buildIconTile(iconName)),
const SizedBox(height: 8),
_buildDefaultIconTile(),
],
),
),
);
}
Widget _buildIconTile(String iconName) {
final isSelected = _currentIcon == iconName;
return Container(
margin: const EdgeInsets.only(bottom: 8),
decoration: BoxDecoration(
color: isSelected
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isSelected
? Theme.of(context).colorScheme.primary
: Colors.transparent,
width: 2,
),
),
child: ListTile(
leading: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: isSelected
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.android,
color: isSelected
? Theme.of(context).colorScheme.onPrimary
: Theme.of(context).colorScheme.onSurfaceVariant,
size: 24,
),
),
title: Text(
iconName,
style: TextStyle(
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
trailing: isSelected
? Icon(
Icons.check_circle,
color: Theme.of(context).colorScheme.primary,
)
: const Icon(Icons.radio_button_unchecked),
onTap: _isChangingIcon
? null
: () => _setIcon(iconName),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
);
}
Widget _buildDefaultIconTile() {
final isSelected = _currentIcon == null;
return Container(
decoration: BoxDecoration(
color: isSelected
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isSelected
? Theme.of(context).colorScheme.primary
: Colors.transparent,
width: 2,
),
),
child: ListTile(
leading: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: isSelected
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.home,
color: isSelected
? Theme.of(context).colorScheme.onPrimary
: Theme.of(context).colorScheme.onSurfaceVariant,
size: 24,
),
),
title: Text(
'Default',
style: TextStyle(
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
trailing: isSelected
? Icon(
Icons.check_circle,
color: Theme.of(context).colorScheme.primary,
)
: const Icon(Icons.radio_button_unchecked),
onTap: _isChangingIcon ? null : () => _setIcon(null),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
);
}
}