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,
);
}
}
CodePudding user response:
You are facing this because BlocBuilder
builds every time the state
changes, the moment you change the state to light
the state is changed , the whole Widget
under the BlocBuilder
is rebuild.
What is the best way to handle this ?
To use buildWhen
prop of the BlocBuilder
. Here try specifying your condition and exclude the condition where your state changes from light to dark or vice-versa
Code Structure:
BlocBuilder<BlocA, BlocAState>(
buildWhen: (previous, current) {
// return true/false to determine whether or not
// to rebuild the widget with state
},
builder: (context, state) {
// return widget here based on BlocA's state
}
)
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 of BlocBuilder
if you are not using buildWhen
to prevent drawer from re-rendering
Suggestions
- Foremost, your Code architecture needs fine tuning because
BlocBuilder
is introduced to build only the required widget at time of neccessary. - Wrapping the whole
MaterialApp
insideBlocBuilder
is heavily discouraged. And it shouldn't be used at all. Because for every state change, your complete app is rebuilding.
BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, authState) {
final _router = AppRouter(
status: authState.status,
initialMessage: null,
).router;
return MaterialApp.router( //