I want to change the primary color on runtime. For this I use a Provider. When the color is changed, it changes with a transition with different shades, which leads to multiple builds, until final color is reached. My log from a single color change looks like this:
flutter: –> Changing primary color to Color(0xff43b0e6)
flutter: Build with primary color Color(0xffe54c44)
flutter: Build with primary color Color(0xffc75e62)
flutter: Build with primary color Color(0xffb86770)
flutter: Build with primary color Color(0xffaa707f)
flutter: Build with primary color Color(0xff9b798e)
flutter: Build with primary color Color(0xff8c829c)
flutter: Build with primary color Color(0xff7d8bab)
flutter: Build with primary color Color(0xff6f94b9)
flutter: Build with primary color Color(0xff609dc8)
flutter: Build with primary color Color(0xff51a6d7)
flutter: Build with primary color Color(0xff43b0e6)
I actually like the smooth transition effect, but in the app the color changes quite often, therefore I don’t want to run into performance problems.
How can I achieve to build only once?
ThemeProvider
import 'package:flutter/material.dart';
class ThemeProvider extends ChangeNotifier {
ThemeMode _themeMode;
Color _primaryColor;
ThemeProvider({required ThemeMode themeMode, required Color primaryColor})
: _themeMode = themeMode,
_primaryColor = primaryColor;
ThemeMode get themeMode => _themeMode;
Color get primaryColor => _primaryColor;
set themeMode(ThemeMode themeMode) {
if (themeMode != _themeMode) {
_themeMode = themeMode;
notifyListeners();
}
}
set primaryColor(Color primaryColor) {
if (primaryColor != _primaryColor) {
_primaryColor = primaryColor;
notifyListeners();
}
}
}
Main
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<ThemeProvider>(
create: (_) => ThemeProvider(themeMode: ThemeMode.light, primaryColor: Colors.red),
),
],
child: MyApp(),
),
);
}
MyApp
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return OverlaySupport.global(
child: Consumer<ThemeProvider>(
builder: (context, themeProvider, child) {
return MaterialApp(
title: 'Organizo',
navigatorKey: navigatorKey,
theme: buildLightTheme(themeProvider),
darkTheme: buildDarkTheme(themeProvider),
themeMode: themeProvider.themeMode,
debugShowCheckedModeBanner: false,
home: const HomePage(),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
localeResolutionCallback: (locale, supportedLocales) {
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale?.languageCode) {
return supportedLocale;
}
}
return const Locale('en');
},
);
},
),
);
}
}
HomePage
Color getRandomColor() {
final random = Random();
return Color.fromRGBO(
random.nextInt(256),
random.nextInt(256),
random.nextInt(256),
1,
);
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final themeProvider = Provider.of<ThemeProvider>(context, listen: false);
print("Build with primary color ${context.theme.primaryColor}");
return TextButton(
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
foregroundColor: context.theme.primaryColor,
backgroundColor: context.theme.primaryColor,
),
onPressed: () async {
var newColor = getRandomColor();
print("Changing primary color to $newColor");
themeProvider.primaryColor = newColor;
},
child: Text(
"--> Change primary color",
style: context.theme.textTheme.bodyMedium,
),
);
}
}