agent_orbs 0.1.0
agent_orbs: ^0.1.0 copied to clipboard
Dotted thought-orb loading indicators for AI and agent UIs: nine tuned states, two sizes, auto dark/light. A faithful Flutter port of thinking-orbs.
import 'package:flutter/material.dart';
import 'package:agent_orbs/agent_orbs.dart';
void main() => runApp(const DemoApp());
class DemoApp extends StatefulWidget {
const DemoApp({super.key});
@override
State<DemoApp> createState() => _DemoAppState();
}
class _DemoAppState extends State<DemoApp> {
ThemeMode _themeMode = ThemeMode.dark;
double _size = 64;
double _speed = 1;
bool _paused = false;
OrbTheme get _orbTheme => switch (_themeMode) {
ThemeMode.dark => OrbTheme.dark,
ThemeMode.light => OrbTheme.light,
ThemeMode.system => OrbTheme.auto,
};
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'agent_orbs',
debugShowCheckedModeBanner: false,
themeMode: _themeMode,
theme: ThemeData(brightness: Brightness.light, useMaterial3: true),
darkTheme: ThemeData(brightness: Brightness.dark, useMaterial3: true),
home: Scaffold(
appBar: AppBar(
title: const Text('agent_orbs'),
actions: [
IconButton(
tooltip: 'Toggle theme',
icon: Icon(
_themeMode == ThemeMode.dark
? Icons.dark_mode
: Icons.light_mode,
),
onPressed: () => setState(
() => _themeMode = _themeMode == ThemeMode.dark
? ThemeMode.light
: ThemeMode.dark,
),
),
],
),
body: Column(
children: [
Expanded(child: _grid(context)),
_controls(),
],
),
),
);
}
Widget _grid(BuildContext context) {
return GridView.count(
crossAxisCount: 3,
padding: const EdgeInsets.all(16),
children: [
for (final state in OrbState.values)
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 96,
child: Center(
child: ThinkingOrb(
state: state,
size: _size,
speed: _speed,
paused: _paused,
theme: _orbTheme,
),
),
),
const SizedBox(height: 8),
Text(state.name, style: Theme.of(context).textTheme.bodySmall),
],
),
],
);
}
Widget _controls() {
return SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
const Text('Size'),
const SizedBox(width: 12),
ChoiceChip(
label: const Text('64'),
selected: _size == 64,
onSelected: (_) => setState(() => _size = 64),
),
const SizedBox(width: 8),
ChoiceChip(
label: const Text('20'),
selected: _size == 20,
onSelected: (_) => setState(() => _size = 20),
),
const Spacer(),
FilterChip(
label: const Text('Paused'),
selected: _paused,
onSelected: (v) => setState(() => _paused = v),
),
],
),
Row(
children: [
const Text('Speed'),
const SizedBox(width: 12),
Expanded(
child: Slider(
min: 0.25,
max: 3,
divisions: 11,
label: '${_speed.toStringAsFixed(2)}x',
value: _speed,
onChanged: (v) => setState(() => _speed = v),
),
),
],
),
],
),
),
);
}
}