App Theme Setter
A powerful, SOLID-compliant dynamic theming package for Flutter. Effortlessly switch themes, customize deep UI elements using extensions, and add new themes in a plug-and-play manner.
Features
- 🚀 Dynamic Switching: Change themes on the fly with a single method call.
- 🎨 Deep Customization: Use
ThemeExtensionto style field decorations, gradients, fonts, and more. - 🏗️ SOLID Principles: Clean architecture for maintainability and scalability.
- 🔌 Plug & Play: Add new themes without modifying existing UI or controller code.
- 💾 Persistence: Built-in support for saving theme selection across app restarts.
Getting Started
1. Define Your Theme
Extend IAppTheme for each theme in your app. Using extends allows you to inherit the fullThemeData helper.
class MyLightTheme extends IAppTheme {
@override
String get id => 'light';
@override
String get name => 'Light Mode';
@override
ThemeData get themeData => ThemeData(
useMaterial3: true,
brightness: Brightness.light,
primarySwatch: Colors.blue,
);
@override
AppThemeExtension get extension => AppThemeExtension(
primaryGradientStart: Colors.blue.shade300,
primaryGradientEnd: Colors.blue.shade700,
borderRadius: 12.0,
customFieldDecoration: InputDecoration(
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
filled: true,
fillColor: Colors.blue.shade50,
),
);
}
2. Persistence (Optional)
Implement IThemePersistence to save the theme choice (e.g., using shared_preferences).
class MyThemePersistence implements IThemePersistence {
@override
Future<void> saveThemeId(String themeId) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('selected_theme', themeId);
}
@override
Future<String?> loadThemeId() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('selected_theme');
}
}
3. Provide the Theme
Initialize the ThemeController in your main() and call init() to load saved settings.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final controller = ThemeController(
initialTheme: MyLightTheme(),
persistence: MyThemePersistence(), // Pass your persistence implementation
availableThemes: [MyLightTheme(), MyDarkTheme()],
);
// Load saved theme before running app
await controller.init();
runApp(
ThemeProvider(
controller: controller,
child: MyApp(),
),
);
}
4. Consume the Theme
Use ValueListenableBuilder to rebuild the MaterialApp when the theme changes.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<IAppTheme>(
valueListenable: ThemeProvider.of(context),
builder: (context, currentTheme, _) {
return MaterialApp(
theme: currentTheme.fullThemeData, // Includes custom extensions
home: HomeScreen(),
);
},
);
}
}
5. Custom Styling Access
Access your custom extensions anywhere in the widget tree:
final appExtension = Theme.of(context).extension<AppThemeExtension>()!;
final decoration = appExtension.customFieldDecoration;
Folder Structure
lib/
├── src/
│ ├── core/ # Interfaces (IAppTheme, IThemePersistence) & Extensions
│ ├── controller/ # ThemeController State Management
│ ├── widgets/ # ThemeProvider Inherited Widget
│ └── themes/ # Concrete Theme Implementations
└── app_theme_setter.dart
License
MIT
Libraries
- app_theme_setter
- A powerful, SOLID-compliant dynamic theming package for Flutter.