circular_theme_reveal 1.0.2 copy "circular_theme_reveal: ^1.0.2" to clipboard
circular_theme_reveal: ^1.0.2 copied to clipboard

A beautiful Telegram-style circular reveal animation for theme transitions in Flutter. Smooth, performant, and zero dependencies.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:circular_theme_reveal/circular_theme_reveal.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  ThemeMode _themeMode = ThemeMode.light;

  void _toggleTheme() {
    setState(() {
      _themeMode = _themeMode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Circular Theme Reveal Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.blue,
          brightness: Brightness.light,
        ),
        useMaterial3: true,
      ),
      darkTheme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.blue,
          brightness: Brightness.dark,
        ),
        useMaterial3: true,
      ),
      themeMode: _themeMode,
      themeAnimationDuration: Duration.zero,
      builder: (BuildContext context, Widget? child) {
        return CircularThemeRevealOverlay(
          child: child ?? const SizedBox.shrink(),
        );
      },
      home: HomePage(
        onThemeToggle: _toggleTheme,
        isDark: _themeMode == ThemeMode.dark,
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({
    required this.onThemeToggle,
    required this.isDark,
    super.key,
  });

  final VoidCallback onThemeToggle;
  final bool isDark;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Circular Theme Reveal'),
        actions: [
          ThemeToggleButton(
            isDark: isDark,
            onToggle: onThemeToggle,
          ),
          const SizedBox(width: 8),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(
              isDark ? Icons.dark_mode : Icons.light_mode,
              size: 100,
              color: Theme.of(context).colorScheme.primary,
            ),
            const SizedBox(height: 24),
            Text(
              isDark ? 'Dark Mode' : 'Light Mode',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            const SizedBox(height: 16),
            const Padding(
              padding: EdgeInsets.symmetric(horizontal: 32),
              child: Text(
                'Tap the button in the app bar to see the beautiful circular reveal animation!',
                textAlign: TextAlign.center,
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {},
        icon: const Icon(Icons.info_outline),
        label: const Text('Example Button'),
      ),
    );
  }
}

class ThemeToggleButton extends StatelessWidget {
  const ThemeToggleButton({
    required this.isDark,
    required this.onToggle,
    super.key,
  });

  final bool isDark;
  final VoidCallback onToggle;

  @override
  Widget build(BuildContext context) {
    return IconButton(
      icon: AnimatedSwitcher(
        duration: const Duration(milliseconds: 300),
        transitionBuilder: (Widget child, Animation<double> animation) {
          return RotationTransition(
            turns: animation,
            child: child,
          );
        },
        child: Icon(
          isDark ? Icons.light_mode : Icons.dark_mode,
          key: ValueKey<bool>(isDark),
        ),
      ),
      onPressed: () async {
        // Get the center position of the button
        final Offset center = CircularThemeRevealOverlay.getCenterFromContext(context);

        // Get the overlay state
        final CircularThemeRevealOverlayState? overlay = CircularThemeRevealOverlay.of(context);

        if (overlay != null) {
          // Start the circular reveal transition
          await overlay.startTransition(
            center: center,
            reverse: isDark, // true when going dark→light
            onThemeChange: onToggle,
          );
        } else {
          // Fallback if overlay is not found
          onToggle();
        }
      },
    );
  }
}
3
likes
150
points
35
downloads

Publisher

unverified uploader

Weekly Downloads

A beautiful Telegram-style circular reveal animation for theme transitions in Flutter. Smooth, performant, and zero dependencies.

Repository (GitHub)
View/report issues

Topics

#animation #theme #telegram #circular-reveal #dark-mode

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on circular_theme_reveal