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.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:app_theme_setter/app_theme_setter.dart';
// 1. Implement IThemePersistence (usually with SharedPref or Hive)
class MockThemePersistence implements IThemePersistence {
String? _savedId;
@override
Future<String?> loadThemeId() async {
// Simulate network or disk delay
await Future.delayed(const Duration(milliseconds: 500));
return _savedId;
}
@override
Future<void> saveThemeId(String themeId) async {
_savedId = themeId;
debugPrint('Saved theme: $themeId');
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 2. Initialize the controller with available themes and persistence
final themeController = ThemeController(
initialTheme: AppLightTheme(),
persistence: MockThemePersistence(),
availableThemes: [AppLightTheme(), AppDarkTheme()],
);
// 3. Load the saved theme before running the app
await themeController.init();
runApp(ThemeExampleApp(controller: themeController));
}
class ThemeExampleApp extends StatelessWidget {
final ThemeController controller;
const ThemeExampleApp({super.key, required this.controller});
@override
Widget build(BuildContext context) {
// 4. Wrap the app with ThemeProvider
return ThemeProvider(
controller: controller,
child: Builder(
builder: (context) {
// 5. Use ValueListenableBuilder to react to theme changes
return ValueListenableBuilder<IAppTheme>(
valueListenable: ThemeProvider.of(context),
builder: (context, currentTheme, _) {
return MaterialApp(
title: 'Dynamic Theme Demo',
theme: currentTheme.fullThemeData,
home: const HomeScreen(),
);
},
);
},
),
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = ThemeProvider.of(context);
final currentTheme = themeController.value;
// Accessing custom extension data
final appExtension = Theme.of(context).extension<AppThemeExtension>()!;
return Scaffold(
appBar: AppBar(
title: Text('${currentTheme.name} Demo'),
actions: [
IconButton(
icon: const Icon(Icons.palette),
onPressed: () => themeController.toggleTheme(),
),
],
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
appExtension.primaryGradientStart,
appExtension.primaryGradientEnd,
],
),
),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
appExtension.borderRadius,
),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Text(
'Theme Persistence!',
style: Theme.of(context).textTheme.headlineLarge,
),
const SizedBox(height: 8),
Text(
'The choice is saved using MockPersistence.',
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
),
),
const SizedBox(height: 24),
TextField(
decoration: appExtension.customFieldDecoration.copyWith(
labelText: 'Dynamic Field Decoration',
hintText: 'Border and fill change with theme',
),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {},
child: const Text('Sample Button'),
),
],
),
),
),
);
}
}