smooth_loader 3.0.0
smooth_loader: ^3.0.0 copied to clipboard
A modern, lightweight, and highly customizable collection of smooth Flutter loading animations.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:smooth_loader/smooth_loader.dart';
void main() => runApp(const LoaderGallery());
/// Interactive example application for smooth_loader.
class LoaderGallery extends StatefulWidget {
/// Creates the example gallery.
const LoaderGallery({super.key});
@override
State<LoaderGallery> createState() => _LoaderGalleryState();
}
class _LoaderGalleryState extends State<LoaderGallery> {
bool _dark = false;
bool _enabled = true;
double _size = 52;
double _speed = 900;
Color _color = Colors.indigo;
@override
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData(
colorSchemeSeed: _color,
brightness: _dark ? Brightness.dark : Brightness.light,
useMaterial3: true,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Smooth Loader Gallery'),
actions: <Widget>[
IconButton(
icon: Icon(_dark ? Icons.light_mode : Icons.dark_mode),
onPressed: () => setState(() => _dark = !_dark),
),
],
),
body: Column(
children: <Widget>[
Expanded(child: _grid()),
_controls(),
],
),
),
);
Widget _grid() => GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.15,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
itemCount: LoaderType.values.length,
itemBuilder: (BuildContext context, int index) {
final LoaderType type = LoaderType.values[index];
return Card(
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () => _preview(type),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SmoothLoader(
type: type,
color: _color,
size: _size,
duration: Duration(milliseconds: _speed.round()),
enabled: _enabled,
),
const SizedBox(height: 12),
Text(type.name.toUpperCase()),
],
),
),
);
},
);
Widget _controls() => SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 4, 20, 12),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
const Text('Color'),
const SizedBox(width: 8),
...<Color>[
Colors.indigo,
Colors.teal,
Colors.pink,
Colors.orange,
].map(_colorChoice),
],
),
Row(
children: <Widget>[
const Text('Size'),
Expanded(
child: Slider(
value: _size,
min: 28,
max: 90,
onChanged: (double value) => setState(() => _size = value),
),
),
Text(_size.round().toString()),
],
),
Row(
children: <Widget>[
const Text('Speed'),
Expanded(
child: Slider(
value: _speed,
min: 350,
max: 1800,
onChanged: (double value) => setState(() => _speed = value),
),
),
IconButton(
tooltip: _enabled ? 'Pause' : 'Play',
icon: Icon(_enabled ? Icons.pause : Icons.play_arrow),
onPressed: () => setState(() => _enabled = !_enabled),
),
IconButton(
tooltip: 'Restart',
icon: const Icon(Icons.restart_alt),
onPressed: _restart,
),
],
),
],
),
),
);
Widget _colorChoice(Color color) => GestureDetector(
onTap: () => setState(() => _color = color),
child: Container(
margin: const EdgeInsets.all(4),
width: 24,
height: 24,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
border: Border.all(
color: _color == color ? Colors.white : Colors.transparent,
width: 2,
),
),
),
);
void _restart() {
setState(() => _enabled = false);
Future<void>.delayed(const Duration(milliseconds: 40), () {
if (mounted) setState(() => _enabled = true);
});
}
void _preview(LoaderType type) {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) => SizedBox(
height: 260,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SmoothLoader(
type: type,
color: _color,
size: _size * 1.5,
duration: Duration(milliseconds: _speed.round()),
enabled: _enabled,
),
const SizedBox(height: 20),
Text(type.name),
],
),
),
),
);
}
}