gradient_avatars 0.0.1
gradient_avatars: ^0.0.1 copied to clipboard
Generative gradient avatars for Flutter. A unique, deterministic mesh gradient for every seed - no stored images, no network. A 1:1 port of @outpacelabs/avatars.
import 'package:flutter/material.dart';
import 'package:gradient_avatars/gradient_avatars.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'gradient_avatars',
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.indigo,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// Whatever the user types becomes the seed. The same text always produces
// the same gradient, so an id, email, or username each maps to one avatar.
final _controller = TextEditingController(text: 'jane@example.com');
// A few seeds to show that different inputs give distinct gradients.
static const _people = [
'ada.lovelace',
'grace.hopper',
'alan.turing',
'katherine.johnson',
'dennis.ritchie',
'margaret.hamilton',
];
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final seed = _controller.text;
return Scaffold(
appBar: AppBar(title: const Text('gradient_avatars')),
body: ListView(
padding: const EdgeInsets.all(24),
children: [
// Type a seed and watch the avatar update live.
TextField(
controller: _controller,
decoration: const InputDecoration(
labelText: 'Seed',
helperText:
'Any string or int - the same seed is the same avatar',
border: OutlineInputBorder(),
),
onChanged: (_) => setState(() {}),
),
const SizedBox(height: 32),
// The whole API surface for most apps: GradientAvatar(seed, size).
Center(child: GradientAvatar(seed: seed, size: 128)),
const SizedBox(height: 32),
// The same seed in each of the built-in shapes.
Text('Shapes', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_Labeled(
label: 'Circle',
child: GradientAvatar(seed: seed, size: 72),
),
_Labeled(
label: 'Rounded',
child: GradientAvatar(
seed: seed,
size: 72,
borderRadius: BorderRadius.circular(16),
),
),
_Labeled(
label: 'Square',
child: GradientAvatar(
seed: seed,
size: 72,
borderRadius: BorderRadius.zero,
),
),
],
),
const SizedBox(height: 32),
// A list of distinct seeds, the way a user list would look.
Text('A directory', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 8),
for (final person in _people)
ListTile(
contentPadding: EdgeInsets.zero,
leading: GradientAvatar(seed: person, size: 44),
title: Text(person),
),
],
),
);
}
}
/// A small avatar with a caption underneath.
class _Labeled extends StatelessWidget {
const _Labeled({required this.label, required this.child});
final String label;
final Widget child;
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
child,
const SizedBox(height: 8),
Text(label, style: Theme.of(context).textTheme.labelMedium),
],
);
}
}