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,
);
}
}
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.
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
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
}
)
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
BlocBuilder
is introduced to build only the required widget at time of neccessary.MaterialApp
inside BlocBuilder
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( //👈❗ Highly Dicouraged
themeMode: context.watch<ThemeModeCubit>().state,
routerConfig: _router,
);
},
);
class CounterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocProvider(
create: (_) => AuthenticationBloc(), 👈 Just Create your Bloc here
),
);
}
}
MultiBlocProvider
instead of nesting BlocProvider
.