In Flutter, you can set a dark theme to be used, which will be used if certain contexts (e.g. Dark Mode is set on Android Q). Is there a recommended way to allow that to be overridden programmatically; e.g, in response to a user preference?
I guess this could be done at the very top level of the app:
MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: fromSomeState,
)
However, I'd also like to be able switch on whether or not the app is in dark mode programmatically at various points in the app (e.g. for image selection):
MediaQuery.of(context).platformBrightness == Brightness.dark;
I could try and update the MediaQueryData to override platformBrightness as well, but that can only be done inside MaterialApp, which seems awkward.
Is there a better pattern for doing this kind of thing?
You can check out dynamic_theme
How to use
Just add Dynamic Theme as the parent of your MaterialApp
DynamicTheme(
defaultBrightness: Brightness.light,
data: (brightness) => new ThemeData(
primarySwatch: Colors.indigo,
brightness: brightness,
),
themedWidgetBuilder: (context, theme) {
return new MaterialApp(
.......
);
}
)
Get the currentBrightness using
DynamicTheme.of(context).brightness;
And Set it using
DynamicTheme.of(context).setBrightness(Theme.of(context).brightness == Brightness.dark? Brightness.light: Brightness.dark);
You can read more about it or create your own, check this medium Post
Edited
You can check the current OS theme using
MediaQuery.of(context).platformBrightness;
And set the theme accordingly.
Related
I am (very) new to Flutter, so please go easy on me. š I'm using Flutter 3 and Dart 2.17.
I am making a web app and am trying to set up the color scheme for my app. In CSS, I'd normally do something like this to define a set of colors for light and dark mode:
:root {
--alpha: #FFF;
--bravo: #F5F5F6;
}
#media (prefers-color-scheme: dark) {
--alpha: #222630;
--bravo: #171920;
}
I've read about Flutter's ThemeData, and as I understand it, it's a big list of styles so that if I were making an Android app, I'd have some sensible defaults for various pieces of the mobile UI. But since I am making a web app that has a fully custom UI, it feels like a lot of extra stuff I don't want.
What is a good way to define a bunch of color names and have them automatically switch their values when I switch between light and dark mode? Should I still use ThemeData? Or is there a simpler way when my UI is custom?
I'm hoping for something like this (this probably isn't valid Dart; I'm still learning):
//My view
Container(color: MyColors.alpha)
...and the values are read somewhere like this:
//Some other class
class MyColors {
final alpha = darkMode ? const Color(0XFF222630) : const Color(0xFFFFFFFF);
final bravo = darkMode ? const Color(0xFF171920) : const Color(0xFFF5F5F6);
}
Has anyone done something like this?
I'd suggest you use an #observable ThemeMode themeMode variable (read about the mobx state management here https://pub.dev/packages/mobx) to change the state of your whole widget tree starting from MaterialApp when the user changes the app theme, for example
themeMode: themeMode != null
? _controller.themeMode!
: WidgetsBinding.instance.window.platformBrightness == Brightness.light
? ThemeMode.light
: ThemeMode.dark,
and still use ThemeData to set which colors you want your components to use, like this
darkTheme: ThemeData(
scaffoldBackgroundColor: MyColors.alphaDark,
backgroundColor: MyColors.alphaDark,
cardColor: MyColors.alphaDark,)
theme: ThemeData(
scaffoldBackgroundColor: MyColors.bravoLight,
backgroundColor: MyColors.bravoLight,
cardColor: MyColors.bravoLight,)
class MyColors {
final alphaDark = const Color(0XFF222630);
final bravoLight = const Color(0xFFF5F5F6);
}
Container(color: Theme.of(context).cardColor)
And you can always create a custom ThemeData so you can call your components whatever you want
I'm working through the startup_namer tutorial example from flutter.dev. Here's the definition of MyApp:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Startup Name Generator',
theme: ThemeData(
// Add the 3 lines from here...
primaryColor: Colors.white,
), // ... to here.
home: RandomWords());
}
}
It used to be that when I ran the app, the title bar would be white. But now, it retains the default (blue) theme. Looks like either ThemeData or at least primaryColor are being ignored. Does anyone know how to fix this? Thank you.
Checkout this link and this quote from the Flutter-Team:
The ThemeData accentColor, accentColorBrightness, accentIconTheme and
accentTextTheme properties have been deprecated.
The Material Design spec no longer specifies or uses an āaccentā color
for the Material components. The default values for component colors
are derived from the overall themeās color scheme. The ColorSchemeās
secondary color is now typically used instead of accentColor and the
onSecondary color is used when a contrasting color is needed.
Also checkout the migration guide.
Regarding your appBar problem do this within the ThemeData():
appBarTheme: AppBarTheme(backgroundColor: Colors.white),
This appears to be a regression: https://github.com/flutter/flutter/issues/89839.
Nowhere on the Flutter site does it say that primaryColor has been deprecated.
I was following a YouTuber step by step creating this project but when I want to change the 'ThemeData primary colors' It just won't change and still remain the default color which is light blue. Here is a screenshot of it
According to AppBar description, it uses ColorScheme.primary by default.
The default app bar [backgroundColor] is the overall theme's
[ColorScheme.primary] if the overall theme's brightness is [Brightness.light]. Unfortunately this is the same as the default
[ButtonStyle.foregroundColor] for [TextButton] for light themes.
In this case a preferable text button foreground color is
[ColorScheme.onPrimary], a color that contrasts nicely with
[ColorScheme.primary]. to remedy the problem, override
[TextButton.style]:
try using colorScheme
MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSwatch(
primarySwatch: Colors.purple,
),
),
home: MyApp(),
),
And to use somewhere else
Theme.of(context).colorScheme.primary,
For more, visit ThemeData-class
I'm trying to set the font of my MaterialApp. Since I'm using the dark theme, I'd like to just use copyWith and then change the fontFamily. However, copyWith doesn't have an option to change the fontFamily.
MaterialApp(
theme: ThemeData.dark().copyWith(
fontFamily: 'MyFontFamily',
),
The named parameter 'fontFamily' isn't defined.
How do I keep the dark theme but also change the font family? (Same problem with ThemeData.light().)
I found a solution and am posting below.
If you look at the source code for ThemeData.light() and ThemeData.dark(), you can see that all it does is set the brightness value:
/// A default light blue theme.
///
/// This theme does not contain text geometry. Instead, it is expected that
/// this theme is localized using text geometry using [ThemeData.localize].
factory ThemeData.light() => ThemeData(brightness: Brightness.light);
/// A default dark theme with a teal secondary [ColorScheme] color.
///
/// This theme does not contain text geometry. Instead, it is expected that
/// this theme is localized using text geometry using [ThemeData.localize].
factory ThemeData.dark() => ThemeData(brightness: Brightness.dark);
That means to solve your problem you don't need to bother with ThemeData.light() or ThemeData.dark(). Just create a new ThemeData and set the brightness yourself in addition to the fontFamily:
MaterialApp(
theme: ThemeData(
brightness: Brightness.dark,
fontFamily: 'MyFontFamily',
),
Since, all you're doing is setting the brightness, I think the easier way would be to just use DefaultTextStyle.
DefaultTextStyle(
style: TextStyle(
fontFamily: 'YourFontFamily',
),
child: YourWidget(),
)
To manage themes in my MaterialApp(), i use :
MaterialApp(
...
theme: isDarkTheme ? darkTheme() : lightTheme(),
)
darkTheme() and lightTheme() are just simple function that return ThemeData()
So, I use an inherited Widget to change the variable isDarkTheme and then i use the setState((){}) method and all the application is rebuild
But, I saw on the Flutter documentation that we can use darkTheme:.
I'm trying to do this :
MaterialApp(
...
theme: lightTheme(),
darkTheme: darkTheme()
)
And we can change the theme during the course of the application
darkTheme property is used to define a Theme to be used when the device enters Dark mode, So you can't force your app to use darkTheme property because it depends on the value of MediaQueryData.platformBrightness which is a read-only field.
You can rather define several Themes to be used as a value for Theme property, and switching between them during the app course by using a StreamBuilder wrapped around your MaterialApp,which won't cause the app to stop and rebuild like the case when you were using setState to change theme (you'll need rxdart package to apply the below solution):
//BehaviorSubject stream any changes immediately without explicit call
var themeSubject = BehaviorSubject<ThemeData>();
//This way the app won't rebuild when the user selects the same theme
Stream<ThemeData> getTheme() => themeSubject.stream.distinct();
void setTheme (MaterialColor color){
pointsSubject.sink.add(ThemeData(primarySwatch: color) ;
}
return StreamBuilder<ThemeData>(
stream: getTheme(),
initialData: ThemeData(
primarySwatch: Colors.blue,
primaryTextTheme: TextTheme(
title: TextStyle(color: Colors.white),
),
builder: (context, themeSnapshot){
return MaterialApp(theme: themeSnapshot.data);
}
):
Then use your InheritedWidget to access setTheme and change it as you please.