flutter_avatar_kit 1.0.2
flutter_avatar_kit: ^1.0.2 copied to clipboard
SVG avatar UI kit with eight categories, grid picker, animated ring, theme-aware card, and path helpers for bundled assets.
import 'package:flutter/material.dart';
import 'package:flutter_avatar_kit/flutter_avatar_kit.dart';
void main() => runApp(const AvatarKitDemoApp());
class AvatarKitDemoApp extends StatelessWidget {
const AvatarKitDemoApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Avatar Kit',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
useMaterial3: true,
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.indigo,
brightness: Brightness.dark,
),
useMaterial3: true,
),
home: const DemoHomePage(),
);
}
}
class DemoHomePage extends StatefulWidget {
const DemoHomePage({super.key});
@override
State<DemoHomePage> createState() => _DemoHomePageState();
}
class _DemoHomePageState extends State<DemoHomePage> {
AvatarType _type = AvatarType.vibrant;
int? _gridIndex;
String? _gridAsset;
int _heroIndex = 0;
AvatarShape _heroShape = AvatarShape.rounded;
bool _showRing = false;
bool _randomHero = false;
@override
Widget build(BuildContext context) {
final total = AvatarAssets.count(_type);
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Avatar Kit'),
actions: [
IconButton(
tooltip: 'Toggle animated ring on hero',
onPressed: () => setState(() => _showRing = !_showRing),
icon: Icon(_showRing ? Icons.toll : Icons.toll_outlined),
),
IconButton(
tooltip: 'Random hero avatar',
onPressed: () => setState(() {
_randomHero = !_randomHero;
if (!_randomHero) _heroIndex = 0;
}),
icon: Icon(_randomHero ? Icons.shuffle : Icons.shuffle_outlined),
),
],
),
body: ListView(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
children: [
Text(
'Category',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: AvatarType.values.map((t) {
final selected = t == _type;
return FilterChip(
label: Text(_label(t)),
selected: selected,
onSelected: (_) {
setState(() {
_type = t;
_gridIndex = null;
_gridAsset = null;
_heroIndex = _heroIndex.clamp(0, AvatarAssets.count(t) - 1);
});
},
);
}).toList(),
),
const SizedBox(height: 20),
Text(
'Hero preview',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AvatarCard(
onTap: () {
setState(() {
_randomHero = false;
_heroIndex = (_heroIndex + 1) % total;
});
},
child: AvatarWidget(
type: _type,
index: _heroIndex,
size: 96,
shape: _heroShape,
showRing: _showRing,
isRandom: _randomHero,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_randomHero
? 'Random asset each rebuild (tap card to cycle index when off).'
: 'Index $_heroIndex of $total — tap card to cycle.',
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 8),
Text(
_randomHero
? AvatarAssets.getRandomAsset(_type)
: AvatarAssets.getAsset(_type, _heroIndex),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontFamily: 'monospace',
),
),
const SizedBox(height: 12),
Text(
'Shape',
style: Theme.of(context).textTheme.labelLarge,
),
const SizedBox(height: 4),
SegmentedButton<AvatarShape>(
segments: AvatarShape.values
.map(
(s) => ButtonSegment<AvatarShape>(
value: s,
label: Text(s.name),
),
)
.toList(),
selected: {_heroShape},
onSelectionChanged: (s) =>
setState(() => _heroShape = s.first),
),
],
),
),
],
),
const SizedBox(height: 24),
Text(
'Grid (${AvatarAssets.count(_type)} items)',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
SizedBox(
height: 320,
child: AvatarGrid(
type: _type,
crossAxisCount: 5,
padding: const EdgeInsets.only(bottom: 8),
onSelect: (index, asset) {
setState(() {
_gridIndex = index;
_gridAsset = asset;
});
},
onLongPress: (index, asset) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Long press #$index → $asset'),
behavior: SnackBarBehavior.floating,
),
);
},
),
),
if (_gridIndex != null && _gridAsset != null) ...[
const SizedBox(height: 8),
Text(
'Selected: #$_gridIndex — $_gridAsset',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontFamily: 'monospace',
),
),
],
],
),
);
}
static String _label(AvatarType t) {
switch (t) {
case AvatarType.d3:
return '3D';
case AvatarType.vibrant:
return 'Vibrant';
case AvatarType.bluey:
return 'Bluey';
case AvatarType.memo:
return 'Memo';
case AvatarType.notion:
return 'Notion';
case AvatarType.teams:
return 'Teams';
case AvatarType.toon:
return 'Toon';
case AvatarType.upstream:
return 'Upstream';
}
}
}