app_theme_setter 0.0.1
app_theme_setter: ^0.0.1 copied to clipboard
A powerful, SOLID-compliant dynamic theming package for Flutter. Effortlessly switch themes, customize deep UI elements using extensions, and persist theme selection.
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