How to set ThemeMode in splash screen using value stored in sqflite FLUTTER - flutter

I have a Flutter Application where an sqflite database stored the user preference of ThemeMode (viz Dark, Light and System). I have created a splash screen using flutter_native_splash which supports dark mode too.
The Problem is this that I want the splash screen to follow the users stored value for theme mode. Currently, the code I am using is as follows:
class MyRoot extends StatefulWidget {
// const MyRoot({Key? key}) : super(key: key);
static ValueNotifier<ThemeMode> themeNotifier = ValueNotifier(ThemeMode.system);
#override
State<MyRoot> createState() => _MyRootState();
}
class _MyRootState extends State<MyRoot> {
DatabaseHelper? databaseHelper = DatabaseHelper.dhInstance;
ThemeMode? tmSaved;
#override
void initState() {
Future.delayed(Duration.zero, () async => await loadData());
super.initState();
}
#override
Widget build(BuildContext context) {
//to prevent auto rotation of the app
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
return ValueListenableBuilder<ThemeMode>(
valueListenable: MyRoot.themeNotifier,
builder: (_, ThemeMode currentMode, __) {
return Sizer(
builder: (context, orientation, deviceType) {
return MaterialApp(
title: 'My Application',
theme: themeLight, //dart file for theme
darkTheme: themeDark, //dart file for theme
themeMode: tmSaved ?? currentMode,
initialRoute: // my initial root
routes: {
// my routes
.
.
.
// my routes
},
);
},
);
},
);
}
Future<void> loadData() async {
if (databaseHelper != null) {
ThemeMode? themeMode= await databaseHelper?.selectStoredTheme(); // function retrieving sqflite stored value and returning ThemeMode value
if (themeMode != null) {
MyRoot.themeNotifier.value = themeMode;
return;
}
}
MyRoot.themeNotifier.value = ThemeMode.system;
}
}
Currently, this shows a light theme splash screen loading, then converts it into dark with a visible flicker.
ValueListenableBuilder<ThemeMode>(... is to enable real time theme change from settings page in my app which working as intended (taken from A Goodman's article: "Flutter: 2 Ways to Make a Dark/Light Mode Toggle".
main.dart has the below code:
void main() {
runApp(MyRoot());
}

Have you tried loading the setting from sqflite in main() before runApp? If you can manage to do so, you should be able to pass the setting as argument to MyRoot and then the widgets would be loaded from the start with the correct theme. I'm speaking in theory, I can't test what I'm suggesting right now.
Something like:
void main() async {
ThemeMode? themeMode= await databaseHelper?.selectStoredTheme(); // function retrieving sqflite stored value and returning ThemeMode value
runApp(MyRoot(themeMode));
}
[...]
class MyRoot extends StatefulWidget {
ThemeMode? themeMode;
const MyRoot(this.themeMode, {Key? key}) : super(key: key);
static ValueNotifier<ThemeMode> themeNotifier = ValueNotifier(ThemeMode.system);
#override
State<MyRoot> createState() => _MyRootState();
}
EDIT
Regarding the nullable value you mentioned in comments, you can change the main like this:
void main() async {
ThemeMode? themeMode= await databaseHelper?.selectStoredTheme(); // function retrieving sqflite stored value and returning ThemeMode value
themeMode ??= ThemeMode.system;
runApp(MyRoot(themeMode!));
}
which makes themeMode non-nullable, and so you can change MyRoot in this way:
class MyRoot extends StatefulWidget {
ThemeMode themeMode;
const MyRoot(required this.themeMode, {Key? key}) : super(key: key);
[...]
}
Regarding the functionality of ValueNotifier, I simply thought of widget.themeMode as the initial value of your tmSaved property in your state, not as a value to be reused in the state logic. Something like this:
class _MyRootState extends State<MyRoot> {
DatabaseHelper? databaseHelper = DatabaseHelper.dhInstance;
late ThemeMode tmSaved;
#override
void initState() {
tmSaved = widget.themeMode;
super.initState();
}
[...]
}
so that your widgets would already have the saved value at the first build.
PS the code in this edit, as well as in the original part, isn't meant to be working by simply pasting it. Some things might need adjustments, like adding final to themeMode in MyRoot.

Make your splashscreen. A main widget which get data from sqlflite
And make splashscreen widget go to the your home widget with remove it using navigation pop-up
for example :
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'ToDo',
color: // color of background
theme: // theme light ,
darkTheme: // darktheme
themeMode: // choose default theme light - dark - system
home: Splashscreen(),// here create an your own widget of splash screen contains futurebuilder to fecth data and return the mainWidget ( home screen for example)
);
}
}
class Splashscreen extends StatelessWidget {
Future<bool> getData()async{
// get info
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getData(),
builder: (context,snapshot){
// if you want test snapshot
//like this
if(snapshot.hasData) {
return Home();
} else {
return Container(color: /* background color as same as theme's color */);
}
}
);
}
}

Related

How to theme with Hive and Riverpod | Flutter

I am trying to implement my very own Theming system that for now consists of only the color palette. This palette is different based on which Theme is selected.
The logic: When the app first starts or the user did not change the AppSettings yet, the App should use the light or dark theme based on the system settings. The indicator for "use system settings" is that no value is provided for the key theme in Hive.
Whenever the user changes the settings from "use system settings" to "light", "dark" or "christmas", the value should be 1, 2 or 3 respectively. When switching back to system settings, the value gets deleted and the system theme should be get.
First I created an abstract class that defines the colors that can be used and will be implemented by the different theme classes:
abstract class CustomTheme {
Color get backgroundColor;
Color get secondaryBackgroundColor;
}
class LightTheme implements CustomTheme {
#override
Color get backgroundColor => Palette.white;
#override
Color get secondaryBackgroundColor => Palette.gray200;
}
class DarkTheme implements CustomTheme {
#override
Color get backgroundColor => Palette.gray900;
#override
Color get secondaryBackgroundColor => Palette.gray800;
}
class ChristmasTheme implements CustomTheme {
#override
Color get backgroundColor => Palette.green700;
#override
Color get secondaryBackgroundColor => Palette.green600;
}
I found out that with the help of a StatefulClass I am able to override didChangePlatformBrightness which is called whenever the system theme changes.
I tried creating a class that later on can be used to wrap around MaterialApp.
class ThemeBuilder extends StatefulWidget {
const ThemeBuilder({
super.key,
required this.child
});
final Widget child;
#override
State<ThemeBuilder> createState() => _ThemeBuilderState();
}
class _ThemeBuilderState extends State<ThemeBuilder> with WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this); // TODO: Needed?
// TODO: Start listening to changes from Hive.box('themeBox')
// If value changes to NULL, get system theme
// Otherwise set to theme based on value
// 1=Light, 2=Dark, 3=Christmas
}
#override
void didChangePlatformBrightness() {
super.didChangePlatformBrightness();
bool systemIsDarkMode = SchedulerBinding.instance.platformDispatcher.platformBrightness == Brightness.dark;
// TODO: If value in Hive is NULL, set the system theme mode
// Otherwise do nothing
}
#override
Widget build(BuildContext context) {
return widget.child;
}
}
This should also listen to changes, because whenever another theme is selected in Hive, the UI should change immediately.
I can not find any way to implement this behavior so that I can simply do something like CustomTheme.backgroundColor for the color parameters.
The Box will be open since the start of the app:
void main() async {
...
await Hive.openBox('themeBox');
...
}
PS: I don't want to make use of the default theme parameters inside the MaterialApp constructor.
You could always create a ThemeProvider class that houses all the code needed to store and save theme data via hive and then listen to the provider in your material app widget and use it as a theme.
Also I would recommend using Enums if your colors are constants
enum CustomTheme {
lightTheme(
backgroundColor: Palette.white,
secondaryBackgroundColor: Palette.gray200,
),
darkTheme(
backgroundColor: Palette.gray900,
secondaryBackgroundColor: Palette.gray800,
)
christmasTheme(
backgroundColor: Palette.green700,
secondaryBackgroundColor: Palette.green600,
);
const CustomTheme({
required this.backgroundColor,
required this.secondaryBackgroundColor,
});
final Color backgroundColor;
final Color secondaryBackgroundColor;
}
final themeProvider = ChangeNotifierProvider((_) => ThemeProvider());
/// Instance of ThemeProvider
class ThemeProvider extends ChangeNotifier {
CustomTheme _currentTheme = CustomTheme.lightTheme;
CustomTheme get currentTheme => _currentTheme;
set currentTheme(Color val) {
_currentTheme = val;
notifyListeners();
}
...
/// Use hive to save theme data as hex strings or something similar
Future<void> saveTheme(){}
/// Use hive to load saved theme data from hex or something similar
Future<void> loadTheme(){}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const ProviderScope(
child: ProviderScopeApp(),
);
}
}
class ProviderScopeApp extends HookConsumerWidget {
const ProviderScopeApp({super.key});
#override
Widget build(BuildContext context, ref) {
final currentTheme = ref.watch(themeVM.select(it) => it.currentTheme);
return MaterialApp(
title: 'Your app',
debugShowCheckedModeBanner: false,
theme: ThemeData(), /// override the default values here with values from `currentTheme`
...
);
}
}

StateNotifierProvider not keeping state between app restarts

Using flutter_riverpod: ^0.12.4 and testing in the android emulator as well as on a physical device.
What am I doing wrong that the Sign In screen state value does not persist in the StateNotifierProvider after a restart of the app?
The accountSetupProvider's state defaults to the Intro Screen. After the Intro Screen's onPressed button is clicked the state is updated to the Sign In screen and it triggers correctly a rebuild to display the Sign In screen.
However, after a flutter hot restart or opening/closing the app, the Intro Screen, rather than the Sign In screen displays. Shouldn't the state, which now is set to the Sign In screen after clicking onPressed in the Intro Screen persist between restarts and cause the Intro Screen to be skipped and the Sign In screen to display?
As you can see below main.dart has an initial AppRoutes.root route. In app_router.dart, this "root" screen opens root_screen.dart, which is a ConsumerWidget that is watch(ing) my StateNotifierProvider called "accountSetupProvider" in account_setup_provider.dart.
main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: AppRoutes.root,
onGenerateRoute: (settings) => AppRouter.onGenerateRoute(settings),
);
}
}
app_router.dart
class AppRoutes {
static const String root = RootScreen.id;
static const String intro = IntroScreen.id;
static const String signIn = SignInScreen.id;
}
class AppRouter {
static Route<dynamic> onGenerateRoute(RouteSettings settings) {
final _args = settings.arguments;
switch (settings.name) {
case AppRoutes.root:
return MaterialPageRoute<dynamic>(
builder: (_) => RootScreen(),
settings: settings,
);
case AppRoutes.intro:
return MaterialPageRoute<dynamic>(
builder: (_) => IntroScreen(),
settings: settings,
);
case AppRoutes.signIn:
return MaterialPageRoute<dynamic>(
builder: (_) => SignInScreen(),
settings: settings,
);
}
}
}
root_screen.dart
class RootScreen extends ConsumerWidget {
const RootScreen({Key key}) : super(key: key);
static const String id = 'root_screen';
#override
Widget build(BuildContext context, ScopedReader watch) {
final screen = watch(accountSetupProvider.state);
if (screen == AppRoutes.signIn) {
return SignInScreen();
} else if (screen == AppRoutes.intro) {
return IntroScreen();
}
}
}
intro_screen.dart (only including the onPressed portion of intro screen, which I'm expecting to set the state to the new screen, even after a flutter hot restart or app restart.)
onPressed: () {
context
.read(accountSetupProvider) // see accountSetupProvider StateNotifierProvider below.
.setScreen(AppRoutes.signIn);
},
account_setup_provider.dart (inits to the AppRoutes.intro screen.)
class AccountSetupNotifier extends StateNotifier<String> {
AccountSetupNotifier() : super(AppRoutes.intro);
void setScreen(String screen) {
state = screen;
}
}
final accountSetupProvider = StateNotifierProvider<AccountSetupNotifier>((ref) {
return AccountSetupNotifier();
});
Without even looking at your code, and looking only at your subject line, it is not at all surprising to me. "Hot Restart" resets all variables. How could there be any state preserved? Are you instead looking for "Hot Reload"?

Flutter - Using GetIt with BuildContext

I'm using Localizations in my app based on the flutter documentation.
See here: https://flutter.dev/docs/development/accessibility-and-localization/internationalization
I use get_it package (version 4.0.4) to retrieve singleton objects like the Localization delegate. Unfortunately it needs a BuildContext property. Sometimes in my app I don't have the context reference so it would be nice if it would work like this: GetIt.I<AppLocalizations>() instead of this: AppLocalizations.of(context). It still can be achieved without a problem if you setup get_it like this: GetIt.I.registerLazySingleton(() => AppLocalizations.of(context)); The problem is that you need the context at least once to make it work. Moreover if you would like to display a localized text instantly in your initial route it's more difficult to get a properly initialized BuildContext at a time when you need it.
It's a little hard for me to explain it properly so I recreated the issue in a minimal example.
I commented out some code that would cause compile time errors, but it shows how I imagined it to be done.
main.dart
GetIt getIt = GetIt.instance;
void setupGetIt() {
// How to get BuildContext properly if no context is available yet?
// Compile time error.
// getIt.registerLazySingleton(() => AppLocalizations.of(context));
}
void main() {
setupGetIt();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
// The above line also won't work. It has BuildContext but Applocalizations.of(context) won't work
// because it's above in the Widget tree and not yet setted up.
getIt.registerLazySingleton(() => AppLocalizations.of(context));
return MaterialApp(
supportedLocales: const [
Locale('en', 'US'),
Locale('hu', 'HU'),
],
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
localeResolutionCallback: (locale, supportedLocales) {
// check if locale is supported
for (final supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale?.languageCode &&
supportedLocale.countryCode == locale?.countryCode) {
return supportedLocale;
}
}
// if locale is not supported then return the first (default) one
return supportedLocales.first;
},
// You may pass the BuildContext here for Page1 in it's constructor
// but in a more advanced routing case it's not a maintanable solution.
home: Page1(),
);
}
}
Initial route
class PageBase extends StatelessWidget {
final String title;
final Widget content;
PageBase(this.title, this.content);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: content,
);
}
}
class Page1 extends PageBase {
// It won't run because I need the context but clearly I don't have it.
// And in a real app you also don't want to pass the context all over the place
if you have many routes to manage.
Page1(String title)
: super(AppLocalizations.of(context).title, Center(child: Text('Hello')));
// Intended solution
// I don't know how to properly initialize getIt AppLocalizations singleton by the time
// it tries to retrieve it
Page1.withGetIt(String title)
: super(getIt<AppLocalizations>().title, Center(child: Text('Hello')));
}
locales.dart
String globalLocaleName;
class AppLocalizations {
//AppLocalizations(this.localeName);
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate();
static Future<AppLocalizations> load(Locale locale) async {
final String name =
locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
final String localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) {
globalLocaleName = localeName;
return AppLocalizations();
});
}
String get title => Intl.message(
'This is the title.',
name: 'title',
);
}
class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
// This delegate instance will never change (it doesn't even have fields!)
// It can provide a constant constructor.
const _AppLocalizationsDelegate();
#override
bool isSupported(Locale locale) {
return ['en', 'hu'].contains(locale.languageCode);
}
#override
Future<AppLocalizations> load(Locale locale) => AppLocalizations.load(locale);
#override
bool shouldReload(_AppLocalizationsDelegate old) => false;
}
And some intl generated dart code and .arb files that is not so important to illustrate the problem.
So all in all, how can I achive to use my AppLocalizations class as a singleton without using a context for example in a situation like this? Maybe my initial approach is bad and it can be done in other ways that I represented. Please let me know if you have a solution.
Thank you.
To achieve what you have described you need to first make the navigation service using get_it. Follow these steps to achieve the result :
1. Create a navigation service
import 'package:flutter/material.dart';
class NavigationService {
final GlobalKey<NavigatorState> navigatorKey =
new GlobalKey<NavigatorState>();
Future<dynamic> navigateTo(String routeName) {
return navigatorKey.currentState!
.push(routeName);
}
goBack() {
return navigatorKey.currentState!.pop();
}
}
This allows you to navigate anywhere from any point throughout the app without build context. This navigator key is what you can use to achieve the AppLocalization instance for the current context.
Refer to the FilledStacks tutorials for this method of navigating without build context.
https://www.filledstacks.com/post/navigate-without-build-context-in-flutter-using-a-navigation-service/
2. Register
GetIt locator = GetIt.instance;
void setupLocator() {
...
locator.registerLazySingleton(() => NavigationService());
...
}
3. Assign the navigator key in the material app
return MaterialApp(
...
navigatorKey: navigationService.navigatorKey,
...
),
3. Create an instance for the AppLocalizations and import it wherever you want to use
localeInstance() => AppLocalizations.of(locator<NavigationService>().navigatorKey.currentContext!)!;
3. The actual use case
import 'package:{your_app_name}/{location_to_this_instace}/{file_name}.dart';
localeInstance().your_localization_variable
You can add a builder to your MaterialApp and setup the service locator inside it with the context available. Example:
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, widget) {
setUpServiceLocator(context);
return FutureBuilder(
future: getIt.allReady(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return widget;
} else {
return Container(color: Colors.white);
}
});
},
);
}
Service Locator Setup:
void setUpServiceLocator(BuildContext context) {
getIt.registerSingleton<AppLocalizations>(AppLocalizations.of(context));
}
You could use some non-localizable splash screen with FutureBuilder and getIt.allReady().
Something like:
class SplashScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder<void>(
future: getIt.allReady(),
builder: (context, snapshot) {
if (snapshot.hasData) {
// Navigate to main page (with replace)
} else if (snapshot.hasError) {
// Error handling
} else {
// Some pretty loading indicator
}
},
);
}
I'd like to recommend the injectable package for dealing with get_it also.

Check constraint and return the body accordingly

I want to show onboarding screen only for the first time user opens the application, so at the final page of Onboarding screen I put OnBoardingStatus value to be "Done" and move to the main screen. But when user opens the application for the next time this code flash the Onboarding screen for few milliseconds and then opens the mainScreen.
Here is my code
class App2 extends StatefulWidget {
App2({Key key}) : super(key: key);
#override
_App2State createState() => _App2State();
}
class _App2State extends State<App2> {
String onBoardingStatus;
#override
void initState() {
// TODO: implement initState
getOnBoardingStatus();
super.initState();
}
Future<void> getOnBoardingStatus() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
var onboardingstatus = prefs.getString('OnBoardingStatus');
setState(() {
onBoardingStatus = onboardingstatus;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: onBoardingStatus != null
? MainScreen()
: OnboardingScreen());
}
}
Currently you have no way to know if onBoardingStatus is null because the SharedPreferences instance hasn't been retrieved yet, or because the OnBoardingStatus really is empty. You can work around this with a FutureBuilder:
class App2 extends StatelessWidget {
App2({Key key}) : super(key: key);
Future<String> getOnBoardingStatus() async =>
(await SharedPreferences.getInstance()).getString('OnBoardingStatus');
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getOnBoardingStatus(),
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
//TODO: Return a widget that indicates loading
}
return Scaffold(
body: snapshot.data != null
? MainScreen()
: OnboardingScreen());
},
);
}
}
However I don't think it's the best solution. For starters, App2 should get the status from an outer source - this way if you ever decide to change your storage solution you wouldn't need to touch App2.

Flutter how to get brightness without MediaQuery?

My goal is to create an app where the user can choose his preferred theme.
I'm saving the user's choice with shared preferences so I can load it the next app start.
The user can either select:
- Dark Mode (Independent from the OS Settings)
- Light Mode (Independent from the OS Settings)
- System (Changes between Dark Mode and Light mode depending on the OS settings)
With the help of BLoC, I almost achieved what I want. But the problem is that I need to pass the brightness inside my Bloc event. And to get the system (OS) brightness I need to make use of
MediaQuery.of(context).platformBrightness
But the Bloc gets initiated before MaterialApp so that MediaQuery is unavailable. Sure I can pass the brightness later(from a child widget of MaterialApp) but then (for example, if the user has dark mode activated) it goes from light to dark but visible for a really short time for the user(Because inside the InitialState I passed in light mode).
class MyApp extends StatelessWidget {
final RecipeRepository recipeRepository;
MyApp({Key key, #required this.recipeRepository})
: assert(recipeRepository != null),
super(key: key);
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<ThemeBloc>(create: (context) =>
ThemeBloc(),),
],
child: BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, state){
return MaterialApp(
theme: state.themeData,
title: 'Flutter Weather',
localizationsDelegates: [
FlutterI18nDelegate(fallbackFile: 'en',),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
supportedLocales: [
const Locale("en"),
const Locale("de"),
],
home: Home(recipeRepository: recipeRepository),
);
},
),
);
}
}
ThemeBloc:
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
#override
ThemeState get initialState =>
ThemeState(themeData: appThemeData[AppTheme.Bright]);
#override
Stream<ThemeState> mapEventToState(
ThemeEvent event,
) async* {
if (event is LoadLastTheme) {
ThemeData themeData = await _loadLastTheme(event.brightness);
yield ThemeState(themeData: themeData);
}
if (event is ThemeChanged) {
await _saveAppTheme(event.theme);
yield ThemeState(themeData: appThemeData[event.theme]);
}
}
Future<ThemeData> _loadLastTheme(Brightness brightness) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
String themeString = prefs.getString(SharedPrefKeys.appThemeKey);
print("saved theme: $themeString");
if ((prefs.getString(SharedPrefKeys.appThemeKey) != null) &&
themeString != "AppTheme.System") {
switch (themeString) {
case "AppTheme.Bright":
{
return appThemeData[AppTheme.Bright];
}
break;
///Selected dark mode
case "AppTheme.Dark":
{
return appThemeData[AppTheme.Dark];
}
break;
}
}
print("brightness: $brightness");
if (brightness == Brightness.dark) {
return appThemeData[AppTheme.Dark];
} else {
return appThemeData[AppTheme.Bright];
}
}
Future<void> _saveAppTheme(AppTheme appTheme) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString(SharedPrefKeys.appThemeKey, appTheme.toString());
}
}
If you absolutely must do it like this, you can get MediaQuery data directly from the low-level window object like this:
final brightness = MediaQueryData.fromWindow(WidgetsBinding.instance.window).platformBrightness;
However, I would strongly recommend you consider that if you need access to MediaQuery from within your bloc, you should instead move your BlocProvider to get instantiated after your MaterialApp so you can access MediaQuery normally.