theme_toggle_animation 1.0.2
theme_toggle_animation: ^1.0.2 copied to clipboard
Smooth animated theme switching for Flutter with circle, line, and custom GIF mask effects. Zero dependencies.
Theme Toggle Animation #
Smooth, animated theme switching for Flutter.
Circle reveals, scanning lines, and custom GIF mask animations — zero dependencies, fully customizable.
Get Started · Examples · API
Demo #
| Circle | Line | Custom Mask |
![]() |
![]() |
![]() |
| Expanding circle reveal from any corner or tap position. | Scanning line sweep across the screen. | Animated GIF mask reveal effect. |
Note
Demo GIFs looks a bit pixalated due to the conversion from mp4 recording. It looks better originally in the UI when using this theme toggle animation.
✨ Features #
| Feature | Description |
|---|---|
| 🎯 3 animation types | circle, line, customMask |
| 📍 5 circle directions | 4 corners + from tap position |
| 📏 10 line directions | Cardinal, diagonal, and from tap position |
| 🖼️ GIF mask animation | Animated GIFs as reveal masks |
| 🔀 Custom clip paths | Bring your own Path function |
| 🌫️ Blur effects | Configurable blur on animation edges |
| 🎛️ Configurable | Duration, curve, enabled flag |
| 🔔 Lifecycle callbacks | onAnimationStart / onAnimationEnd |
| ⚡ Optimized | RepaintBoundary, content caching, GPU clips |
| 🧩 State management agnostic | Provider, Riverpod, Bloc, GetX — all work |
| 📦 Zero dependencies | Only Flutter SDK |
📦 Installation #
Add to your pubspec.yaml:
dependencies:
theme_toggle_animation: ^1.0.2
Then run:
flutter pub get
🚀 Quick Start #
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _isDark = false;
@override
Widget build(BuildContext context) {
return ThemeToggleAnimation(
currentTheme: _isDark ? ThemeData.dark() : ThemeData.light(),
onToggle: () => setState(() => _isDark = !_isDark),
builder: (context, toggle) => MaterialApp(
home: Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(_isDark ? Icons.light_mode : Icons.dark_mode),
onPressed: toggle,
),
],
),
body: const Center(child: Text('Hello World')),
),
),
child: const SizedBox.expand(),
);
}
}
🎬 Examples #
Circle #
Expanding circle reveal from any corner or the tap position:
ThemeToggleAnimation(
currentTheme: myTheme,
animationType: ThemeAnimationType.circle,
circleDirection: CircleAnimationDirection.fromWidget,
blurAmount: 4.0,
onToggle: () => setState(() => _isDark = !_isDark),
builder: (context, toggle) => MyToggleWidget(onTap: toggle),
child: MyApp(),
)
| Direction | Description |
|---|---|
ftl |
From top-left |
ftr |
From top-right |
fbl |
From bottom-left |
fbr |
From bottom-right |
fromWidget |
From tap position |
Line #
Scanning line sweep in 10 directions:
ThemeToggleAnimation(
currentTheme: myTheme,
animationType: ThemeAnimationType.line,
lineDirection: LineAnimationDirection.ltr,
blurAmount: 4.0,
onToggle: () => setState(() => _isDark = !_isDark),
builder: (context, toggle) => MyToggleWidget(onTap: toggle),
child: MyApp(),
)
| Direction | Description |
|---|---|
ltr |
Left → right |
rtl |
Right → left |
ttb |
Top → bottom |
btt |
Bottom → top |
fromWidgetHorizontal |
From tap, horizontal |
fromWidgetVertical |
From tap, vertical |
ftl |
Diagonal top-left |
ftr |
Diagonal top-right |
fbl |
Diagonal bottom-left |
fbr |
Diagonal bottom-right |
Custom Mask #
Reveal the new theme through an animated GIF:
ThemeToggleAnimation(
currentTheme: myTheme,
animationType: ThemeAnimationType.customMask,
customMaskImage: AssetImage('assets/mask.gif'),
customMaskOffset: Offset(50, 100),
customMaskSize: Size(150, 150),
duration: Duration(milliseconds: 2000),
onToggle: () => setState(() => _isDark = !_isDark),
builder: (context, toggle) => MyToggleWidget(onTap: toggle),
child: MyApp(),
)
💡 GIFs with transparent backgrounds work best — opaque pixels reveal the new theme.
🔌 State Management #
Works with any state management solution — just call onToggle to flip your state.
Provider / Riverpod
ThemeToggleAnimation(
currentTheme: ref.watch(themeProvider),
onToggle: () => ref.read(themeProvider.notifier).toggle(),
builder: (context, toggle) => MyToggleWidget(onTap: toggle),
child: MyApp(),
)
Bloc
ThemeToggleAnimation(
currentTheme: context.read<ThemeBloc>().state.themeData,
onToggle: () => context.read<ThemeBloc>().add(ToggleTheme()),
builder: (context, toggle) => MyToggleWidget(onTap: toggle),
child: MyApp(),
)
GetX
ThemeToggleAnimation(
currentTheme: Get.isDarkMode ? darkTheme : lightTheme,
onToggle: () => Get.isDarkMode
? Get.changeTheme(lightTheme)
: Get.changeTheme(darkTheme),
builder: (context, toggle) => MyToggleWidget(onTap: toggle),
child: MyApp(),
)
setState
ThemeToggleAnimation(
currentTheme: _isDark ? darkTheme : lightTheme,
onToggle: () => setState(() => _isDark = !_isDark),
builder: (context, toggle) => MyToggleWidget(onTap: toggle),
child: MyApp(),
)
📖 API Reference #
ThemeToggleAnimation #
| Parameter | Type | Default | Description |
|---|---|---|---|
currentTheme |
ThemeData |
required | The current theme to apply |
builder |
Widget Function(BuildContext, VoidCallback) |
required | Builder with toggle callback |
child |
Widget |
required | Child widget tree |
onToggle |
VoidCallback? |
null |
Called when toggle is tapped |
animationType |
ThemeAnimationType |
circle |
Animation effect |
duration |
Duration |
750ms |
Animation duration |
curve |
Curve |
easeInOut |
Animation curve |
enabled |
bool |
true |
Enable / disable animation |
onAnimationStart |
VoidCallback? |
null |
Fired when animation starts |
onAnimationEnd |
VoidCallback? |
null |
Fired when animation completes |
circleDirection |
CircleAnimationDirection |
ftl |
Circle origin |
lineDirection |
LineAnimationDirection |
ltr |
Line sweep direction |
blurAmount |
double |
0.0 |
Blur intensity (circle / line only) |
clipper |
Path Function(Size, Offset, double)? |
null |
Custom clip path |
customMaskImage |
ImageProvider? |
null |
GIF / image for mask |
customMaskOffset |
Offset? |
center | Mask position |
customMaskSize |
Size? |
200 × 200 |
Mask size during play phase |
Enums #
ThemeAnimationType — circle · line · customMask
CircleAnimationDirection — ftl · ftr · fbl · fbr · fromWidget
LineAnimationDirection — ltr · rtl · ttb · btt · fromWidgetHorizontal · fromWidgetVertical · ftl · ftr · fbl · fbr
📋 Requirements #
- Flutter
>=3.0.0 - Dart SDK
^3.0.0
📄 License #
MIT © Hirdaya Shrestha — see LICENSE for details.
Made with ❤️ by Hirdaya Shrestha


