I am toggling application theme inside Scaffold
drawer. Whenever I switch themes drawer immediately closes. Is there any way to keep drawer form closing on parent rebuild due to theme change?
return Drawer(
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
const DrawerHeader(
decoration: BoxDecoration(color: Color(0x4D4971FF)),
//https://gist.github.com/lopspower/03fb1cc0ac9f32ef38f4
child: Text(
'Header',
style: style,
),
),
ListTile(
leading: const Icon(Icons.brush),
title: const Text(
'Dark Mode',
style: style,
),
trailing: Switch(
value: context.read<ThemeModeCubit>().state == ThemeMode.dark,
onChanged: (value) {
context.read<ThemeModeCubit>().toggleBrightness();
},
),
),
]
),
);
Here is the Builder that uses Cubit.
StackOverflow says
It looks like your post is mostly code; please add some more
details.
class AppView extends StatelessWidget {
const AppView({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, authState) {
final _router = AppRouter(
status: authState.status,
initialMessage: null,
).router;
return MaterialApp.router(
themeMode: context.watch<ThemeModeCubit>().state,
debugShowCheckedModeBanner: false,
routerConfig: _router,
);
},
);
}
}
Here is the Cubit
class ThemeModeCubit extends HydratedCubit<ThemeMode> {
ThemeModeCubit() : super(ThemeMode.system);
void toggleBrightness() {
emit(state == ThemeMode.light ? ThemeMode.dark : ThemeMode.light);
}
@override
ThemeMode? fromJson(Map<String, dynamic> json) {
return ThemeMode.values[json['themeMode'] as int];
}
@override
Map<String, dynamic>? toJson(ThemeMode state) {
return <String, int>{'themeMode': state.index};
}
}
UPDATE
class AppView extends StatelessWidget {
const AppView({super.key});
@override
Widget build(BuildContext context) {
final _router = AppRouter(
status: context.watch<AuthenticationBloc>().state.status,
initialMessage: null,
).router;
return MaterialApp.router(
theme: AppTheme.of(context).light(),
darkTheme: AppTheme.of(context).dark(),
themeMode: context.watch<ThemeModeCubit>().state,
debugShowCheckedModeBanner: false,
routerConfig: _router,
);
}
}
2
Answers
Normally this doesn’t suppose to do that. As Flutter only rerender the widget which are connected to changed value. Are you sure you are using the BlocBuilder in the right place?
Also check that in either BlocBuilder or inside the
toggleBrightness
method of the Cubit you are not updating any other state or value which is updating something else.This can also happen if you are using
BlocListener
and setting some value inside the listen method of it like..You can test my sample code from this url https://dartpad.dev/?id=c79010cc6c1f27b0c1846191603e4dc9 and check if you have implemented the basic structure like this.
If this not solve your problem then you might have to update your question with more code sample & detail.
Updated after code
Here you are changing the widgets when changing theme but inside that another builder for auth is creating a new router.
Try switching it like this.
You are facing this because
BlocBuilder
builds every time thestate
changes, the moment you change the state tolight
the state is changed , the wholeWidget
under theBlocBuilder
is rebuild.What is the best way to handle this ?
To use
buildWhen
prop of theBlocBuilder
. Here try specifying your condition and exclude the condition where your state changes from light to dark or vice-versaCode Structure:
Alternatively
Remove the BlocBuilder as a whole as you are changing just the state to toggle
colorTheme
.Drawer's
scope should be outside the scope ofBlocBuilder
if you are not usingbuildWhen
to prevent drawer from re-renderingSuggestions
BlocBuilder
is introduced to build only the required widget at time of neccessary.MaterialApp
insideBlocBuilder
is heavily discouraged. And it shouldn’t be used at all. Because for every state change, your complete app is rebuilding.MultiBlocProvider
instead of nestingBlocProvider
.