Skip to content

Instantly share code, notes, and snippets.

@olivoil
Last active November 4, 2025 15:51
Show Gist options
  • Save olivoil/1d80a8702d24c0919a7ad439a3c1b750 to your computer and use it in GitHub Desktop.
Save olivoil/1d80a8702d24c0919a7ad439a3c1b750 to your computer and use it in GitHub Desktop.
Wiggle
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.white,
body: Center(child: WiggleIcon()),
),
);
}
}
class WiggleIcon extends StatefulWidget {
const WiggleIcon({super.key});
@override
State<WiggleIcon> createState() => _WiggleIconState();
}
class _WiggleIconState extends State<WiggleIcon> with TickerProviderStateMixin {
late AnimationController _rotationController;
late Animation<double> _rotation;
late AnimationController _bounceController;
late Animation<double> _bounce;
final _rand = Random();
Duration _randomDuration(int baseMs, int varianceMs) {
final offset =
_rand.nextInt(varianceMs * 2) - varianceMs;
return Duration(milliseconds: baseMs + offset);
}
@override
void initState() {
super.initState();
// Rotation wiggle with random timing
_rotationController = AnimationController(
vsync: this,
duration: _randomDuration(120, 25), // 100-145ms range
)..repeat(reverse: true);
_rotation = Tween(begin: -0.043, end: 0.043).animate(
CurvedAnimation(parent: _rotationController, curve: Curves.easeInOut),
);
// Vertical bounce with slightly different random timing
_bounceController = AnimationController(
vsync: this,
duration: _randomDuration(150, 30),
)..repeat(reverse: true);
_bounce = Tween(begin: 2.0, end: 0.0).animate(
CurvedAnimation(parent: _bounceController, curve: Curves.easeOut),
);
}
@override
void dispose() {
_rotationController.dispose();
_bounceController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: Listenable.merge([_rotationController, _bounceController]),
builder: (context, child) {
return Transform.translate(
offset: Offset(0, _bounce.value),
child: Transform.rotate(angle: _rotation.value, child: child),
);
},
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(16),
),
child: const Icon(Icons.apps, size: 48, color: Colors.white),
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment