A simple, plug-and-play theme management library for Flutter with optional persistence and zero configuration required.
β¨ Zero Configuration - Works out of the box with light and dark themes
π¨ Unlimited Custom Themes - Create as many themes as you need with a simple API
π Drop-in Replacement - Just replace MaterialApp with ThemedApp
Add this to your package's pubspec.yaml file:
dependencies:
flutter_themed: ^1.0.0+3Then run:
flutter pub getThe simplest possible implementation - just replace MaterialApp with ThemedApp:
import 'package:flutter/material.dart';
import 'package:flutter_themed/flutter_themed.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ThemedApp(
title: 'My App',
home: Scaffold(
appBar: AppBar(
title: const Text('Theme Manager'),
actions: [
IconButton(
icon: const Icon(Icons.palette),
onPressed: () => Themed.toggleTheme(),
),
],
),
body: Center(
child: Text('Current theme: ${Themed.currentThemeName}'),
),
),
);
}
}That's it! Your app now supports light and dark themes with a single button.
To persist theme preferences across app restarts, implement ThemeStorageAdapter with your preferred storage solution:
/// Example: In-memory storage (no persistence)
/// Replace with SharedPreferences, Hive, GetStorage, or any other solution
class MyThemeStorage implements ThemeStorageAdapter {
String? _savedTheme;
@override
Future<void> saveTheme(String themeName) async {
_savedTheme = themeName;
// Your storage logic here:
// - SharedPreferences: await prefs.setString('theme', themeName)
// - Hive: await box.put('theme', themeName)
// - GetStorage: await storage.write('theme', themeName)
}
@override
Future<String?> loadTheme() async {
return _savedTheme;
// Your storage logic here:
// - SharedPreferences: return prefs.getString('theme')
// - Hive: return box.get('theme')
// - GetStorage: return storage.read('theme')
}
}
void main() async {
// Only needed if using native plugins (SharedPreferences, Hive, etc.)
WidgetsFlutterBinding.ensureInitialized();
await Themed.initialize(
storageAdapter: MyThemeStorage(), // Optional: remove if you don't need persistence
);
runApp(const MyApp());
}Note: The storage adapter is completely optional. Without it, the library still works but won't persist theme preferences between app restarts. Uncomment WidgetsFlutterBinding.ensureInitialized() only if your storage uses native plugins (SharedPreferences, Hive, etc.).
Create multiple themed experiences for your app:
import 'package:flutter/material.dart';
import 'package:flutter_themed/flutter_themed.dart';
void main() async {
await Themed.initialize();
// π Ocean theme
Themed.createTheme(
name: 'ocean',
primaryColor: const Color(0xFF006994),
secondaryColor: const Color(0xFF4A90A4),
brightness: Brightness.light,
scaffoldBackgroundColor: const Color(0xFFF0F8FF),
);
// π² Forest theme
Themed.createTheme(
name: 'forest',
primaryColor: const Color(0xFF2E7D32),
secondaryColor: const Color(0xFF66BB6A),
brightness: Brightness.dark,
scaffoldBackgroundColor: const Color(0xFF1A2F1A),
cardColor: const Color(0xFF263D26),
);
// πΈ Rose theme
Themed.createTheme(
name: 'rose',
primaryColor: Colors.pink[400]!,
secondaryColor: Colors.pinkAccent,
brightness: Brightness.light,
scaffoldBackgroundColor: Colors.pink[50],
borderRadius: 20,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ThemedApp(
title: 'Custom Themes Demo',
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Theme Gallery')),
body: GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
itemCount: Themed.availableThemes.length,
itemBuilder: (context, index) {
final theme = Themed.availableThemes[index];
final isCurrent = Themed.currentThemeName == theme;
return ElevatedButton(
onPressed: () => Themed.setTheme(theme),
style: ElevatedButton.styleFrom(
backgroundColor: isCurrent ? Theme.of(context).primaryColor : null,
),
child: Text(theme),
);
},
),
);
}
}Themed provides a simple API to create custom themes. Only four parameters are required:
Themed.createTheme(
name: 'my_theme', // Required: unique identifier
primaryColor: Colors.blue, // Required: main app color
secondaryColor: Colors.blueAccent, // Required: accent color
brightness: Brightness.light, // Required: light or dark
// All other parameters are optional:
scaffoldBackgroundColor: Colors.white,
cardColor: Colors.grey[100],
appBarColor: Colors.blue,
buttonColor: Colors.blue,
fabColor: Colors.blue,
borderRadius: 16,
elevation: 4,
fontFamily: 'Roboto',
fontWeight: FontWeight.w500,
useMaterial3: true,
useRippleEffect: true,
// ... and many more
);ThemedApp is a drop-in replacement for MaterialApp that automatically handles theme changes. It accepts all the same parameters as MaterialApp:
ThemedApp(
title: 'My App',
home: HomePage(),
routes: {...},
// Don't use theme or darkTheme - Themed handles them
// ... all other MaterialApp parameters work normally
)Important: Do not set theme or darkTheme parameters manually as Themed controls these automatically.
// Initialize (optional, but required for storage)
await Themed.initialize(storageAdapter: MyStorage());
// Create a custom theme
Themed.createTheme(
name: 'custom',
primaryColor: Colors.purple,
secondaryColor: Colors.purpleAccent,
brightness: Brightness.dark,
);
// Switch themes
Themed.setTheme('custom');
// Toggle between light and dark
Themed.toggleTheme();
// Check available themes
List<String> themes = Themed.availableThemes;
// Get current theme
String currentName = Themed.currentThemeName;
ThemeData currentTheme = Themed.currentTheme;
// Check if theme exists
bool exists = Themed.hasTheme('ocean');
// Remove custom themes (cannot remove 'light' or 'dark')
Themed.removeTheme('custom');
Themed.clearCustomThemes();Define all your custom themes in main() before calling runApp():
void main() async {
await Themed.initialize();
Themed.createTheme(
name: 'custom',
primaryColor: Colors.purple,
secondaryColor: Colors.purpleAccent,
brightness: Brightness.dark,
);
runApp(const MyApp());
}Choose clear, meaningful names for your themes:
// β Bad
Themed.createTheme(name: 'theme1', ...);
// β
Good
Themed.createTheme(name: 'ocean_breeze', ...);Show users which theme is currently active:
ListTile(
title: Text(themeName),
trailing: Themed.currentThemeName == themeName
? const Icon(Icons.check)
: null,
onTap: () => Themed.setTheme(themeName),
)Theme data is accessible throughout your widget tree:
// Using Themed
final isDark = Themed.currentTheme.brightness == Brightness.dark;
// Using Theme.of(context) as usual
final primaryColor = Theme.of(context).primaryColor;Flutter Theme Manager supports persistence only when you provide a ThemeStorageAdapter.
If a ThemeStorageAdapter is provided:
- The selected theme is saved whenever it changes
- When the app starts, the previously saved theme is automatically loaded
- If no theme was saved yet, the default is light
If no adapter is provided:
- The selected theme is not persisted
- The app always starts with the default light theme
Yes! You can store themes using any solution that implements ThemeStorageAdapter (e.g., SharedPreferences, Hive, GetStorage, SQLite, custom solutions, etc.).
If you don't provide an adapter, the library still works β just without persistence.
Currently, Flutter Theme Manager only supports the standard MaterialApp. Support for MaterialApp.router may be added in a future version.
Absolutely! Use Themed.setTheme('themeName') from anywhere in your code - no BuildContext required.
No. The built-in light and dark themes cannot be replaced or modified.
This is intentional to avoid conflicts with Flutter's own ThemeData.light() and ThemeData.dark().
You can still create unlimited custom themes (e.g., ocean, sunset, my_dark).
Future features under consideration:
- π Theme transitions/animations
- π± System theme detection
- π¨ Theme presets library
- π Automatic dark mode scheduling
- π― Theme inheritance
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
If you encounter any issues or have questions, please open an issue on GitHub.
See CHANGELOG.md for a list of changes in each version.