Inside my app.dart I have my BlocProviders:
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => SysUiCubit(context: context, "currentTheme: currentTheme"),
), child: MaterialApp(
theme: AppTheme.lightTheme.copyWith(brightness: Brightness.light),
darkTheme: AppTheme.darkTheme.copyWith(brightness: Brightness.dark),
themeMode: themeService.getSysMode ? ThemeMode.system : (themeService.getDarkMode ? ThemeMode.dark : ThemeMode.light),
and after my BlocProviders I have the MaterialApp. The problem is: For the SysUiCubit I need the state of the current theme like I tried to visualize inside the quotation mark. The easiest way would be to have the MaterialApp before my BlocProvider, but I think this isn't possible - isn't it?
To understand the use case: Inside my SysUiCubit I have a
factory SysUiState.initial(BuildContext context){
return const SysUiState(systemUiOverlayStyle: SystemUiOverlayStyle.dark);
}
Where I want to add an Overlaystyle dependent from the current theme. So as you can guess, to realize that inside the init state, the theme mode must already be set.
Any advice how I can do this?
I believe you added systemUiOverlayStyle as a required parameter I would suggest trying this solution:
in SysUiCubit constructor make systemUiOverlayStyle an optional parameter (no need to pass "currentTheme: currentTheme")
in SysUiCubit contractor check if systemUiOverlayStyle parameter is null, if it is full then check the shared preferences if the app stored the last theme (dark or light), if the shareprefrance is empty (mainly because the user opened the app for the first time) force a default theme based on the system theme (there are packages or system functions that retrieve the active theme).
whenever you want to change the theme, call bloc controller and update the value (systemUiOverlayStyle).
no need to change the order of the current widgets
let me know if you got my points.
Related
Flutter 3 is out and I've been experimenting a little.
I have used the ThemeExtension
yt ref: https://www.youtube.com/watch?v=8-szcYzFVao
api ref: https://api.flutter.dev/flutter/material/ThemeData/extensions.html
and its great. However I'm starting to realize and ask myself I could have done the same result if I created a class with static const as properties such as colors, textstyles or any related theming.
Can someone enlighten me if why I should use ThemeExtensions instead?
Static class way:
// Setup
class AppColors {
static const primaryColor = Color(0xFFFFFF);
static const secondaryColor = Color(0xFFFFFF);
}
// Use case inside build
return Container(
child: Text('Hello world'),
color: AppColors.primaryColor,
)
ThemeExtension Way
// Setup
class AppColors extends ThemeExtension<AppColors>{
final Color primaryColor;
final Color secondaryColor;
AppColors(this.primaryColor, this.secondaryColor);
// .
// ..
// ... some #overrides such as copyWith() and lerp()
}
// Use case inside build
final colors = Theme.of(context).extensions<AppColors>()!;
return Container(
child: Text('Hello world'),
color: colors.primaryColor,
)
As you can see here setting up for ThemeExtension is quite huge compared on using just the static classes for theme to achieve the same result.
I think you should use Theme and/or ThemeExtension because:
You can use Theme(and ThemeExtension) in order to get widgets reactive. If some value is changed(color, text style, etc...), it will notify all listeners and the build method of dependent widgets will be dispatched.
If you change the themeMode to dark/light, all application will react to update widgets that are using the theme.
You can get scoped theme, so if you need some widgets or screen to use a different theme from the entire application, it will be possible too.
You can share you ThemeData between multiple apps and his use will looks like very similar in all apps
Theme is a good choice to keep a consistent visual pattern
The child/children widgets just need to know the Theme, as a Single source of truth. If you need to change all screens button's color of the entire app, you will be safe and happy to do that in a single place :)
You don't need to use ThemeExtension to get all these mentioned benefits, but if you do, it will make simpler to keep a very good documented Design System as code.
I'm expecting that widgets in the display tree would be effected by a dividerColor having been set on my Theme in Flutter.
High up in my widget tree is:
MaterialApp(
/* ... */
theme: ThemeData(dividerColor: Colors.green)
/* ... */
);
And yet when using a Divider() inside my child widgets, the colour is not present. Only when I set the colour explicitly, does it appear, like so:
Divider(color: Colors.green)
My understanding was that the Theme's dividerColor would be used when no colour is specified?
Apologies; I had not restarted the application... 🤦♂️
Posting answer here in case it helps anyone else; Theme changes need a full restart of the application as hot-reloading does not seem to have an affect when changing theme properties.
I want some advice on a topic which I'm curious about. What is the best way and best practice? I tried to draw a UI about this topic. I am using the flutter_bloc package.
Scenario: I have a list as shown in the picture and there are score buttons at the bottom. Buttons are not active without making a selection in the list. Buttons are active when I select an item from the list. When I press the score button, a score is written to the selected element in the list and the score buttons become inactive again.
This UI looks simple, but my problem is with a more complicated UI. So I will definitely use more than one bloc on a screen.
I wonder how do I get this communication between the blocs in the best way? Should I create a parent block on a top layer?
You should have a single bloc which will control the whole screen.
On your bloc state you could have a property which holds the selected item(or its index - really up to you here). Your point buttons will be enabled/disabled based on this prop through a BlocBuilder.
On a point button tap you just add an event like PointsAssigned(amount: 50) or FiftyPointsAssigned() - again up to you. This event will be mapped to a state where the points are attached to the selected item and will rebuild the UI through a BlocBuilder so your change will be reflected.
Multiple bloc can be used in same context. You can use MultiBlocProvider from flutter_bloc
Then, you can access both bloc data and communicate whatever you defined in the Bloc.
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<TodoBloc>(create: (BuildContext context) => TodoBloc()),
BlocProvider<PointBloc>(
create: (BuildContext context) => PointBloc()),
],
child: Scaffold(
......
),
);
}
It's hard to explain without knowing how your current codes look like. You don't really need additional bloc. Your Todo instance could have some sort of bool property onSelected.
Todo(
final String name;
final int point;
final bool onSelected;
Todo(this.name, this.point, this.onSelected);
)
I'm setting my theme this way:
theme: ThemeData(
primaryColor: themeModel.primaryColor,
...
),
Yes, is dynamic.
The problem is: if the user set, let's say, indigo[100], some items (like the bottomAppBar) has light icons over that light color (so bad visibility).
I thought the items adapt their text color to the given color property but it doesn't seem to work that way.
So, I have to set that property manually. But I can't find the Theme property for text color on primary color (named onPrimary in material documentation).
What I found is Theme.of(context).colorScheme.onPrimary but it doesn't seem to work well. It's always returning the same color.
I know I can just get the contrast color for the primary color and set it like this:
theme: ThemeData.from(
colorScheme: ColorScheme(
primary: themeModel.primaryColor
onPrimary: themeModel.onPrimaryColor
...
)
)
But I'm sure Flutter is intelligent enough to autogenerate those properties if you don't pass them... isn't it?
Another thing that I found is Theme.of(context).primaryIconTheme.color. That property is returning the color I'm looking for. But as its name states, it's supposed to be applied only to icons, not text, even when they would probably return the same black variant.
What I would like to know
Is it the way it's supposed to work? Should I write myself the onPrimary property? Is it OK for code quality to use the primaryIconTheme.color for text colors? Or am I missing something?
The default ThemeData is ThemeData.light() which primaryColor and accentColor are set to Colors.blue, Card and Scaffold background are set to Colors.white. There are 3 types of textTheme :
textTheme - Text with a color that contrasts with the card and canvas colors. Basically the theme that is used automatically to all Text widget that is a direct child of Scaffold body or Card. (in this case, Colors.black to contrast Colors.white)
primaryTextTheme - A text theme that contrasts with the primary color. Used automatically to all TextWidget that is a direct child of Widgets that use primaryColor, for example AppBar. (Colors.white to contrast Colors.blue)
accentTextTheme - A text theme that contrasts with the accent color. Used automatically to all TextWidget that is a direct child of Widgets that use accentColor, for example FloatingActionButton. (Colors.white to contrast Colors.blue)
If you change the primaryColor to Colors.white, the primaryTextTheme.color doesn't automatically change value to contrast that.
ThemeData(
primaryColor: Colors.white,
primaryTextTheme: Theme.of(context).primaryTextTheme.apply(
bodyColor: Colors.black,
displayColor: Colors.black,
)
)
Hope this helps you in any way.
I need to create a custom widget that sits alongside of TextFormFields, so I have to style its label to match TextFormField's label style.
This does NOT work:
Theme.of(ctx).inputDecorationTheme.labelStyle
Because, according to its doc:
If null, defaults to a value derived from the base [TextStyle] for the
input field and the current [Theme].
The problem is, I don't know how to get that base style.
You are getting the base style correctly. In my case labelStyle and hintStyle from InputDecorationTheme can not be updated by hot reload, but they work when rerunning the app.
Probably not a complete answer but I have found the color is based on the primarySwatch color. If you are using light theme, just define the primarySwatch and it will do it. For dark theme:
ThemeData(
brightness: Brightness.dark,
colorScheme: ColorScheme.fromSwatch(
primarySwatch: Colors.lightBlue, // use your material color
brightness: Brightness.dark)
);