Flutter как получить яркость без MediaQuery?

Моя цель - создать приложение, в котором пользователь сможет выбрать предпочтительную тему. Я сохраняю выбор пользователя с общими настройками, чтобы я мог загрузить его при следующем запуске приложения. Пользователь может выбрать: - Темный режим (независимо от настроек ОС) - Светлый режим (независимо от настроек ОС) - Система (переключение между темным режимом и светлым режимом в зависимости от настроек ОС) С помощью BLoC я почти добился чего хочу. Но проблема в том, что мне нужно передать яркость внутри моего события Bloc. И чтобы получить яркость системы (ОС), мне нужно использовать

MediaQuery.of(context).platformBrightness

Но Bloc запускается раньше, чем MaterialApp, поэтому MediaQuery недоступен. Конечно, я могу передать яркость позже (из дочернего виджета MaterialApp), но затем (например, если у пользователя активирован темный режим) она переходит от светлого к темному, но видна в течение очень короткого времени для пользователя (потому что внутри InitialState Прошел в лёгком режиме).

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());
  }
}

person Just A Question    schedule 23.02.2020    source источник


Ответы (1)


Если вам абсолютно необходимо сделать это таким образом, вы можете получить MediaQuery данные непосредственно из низкоуровневого оконного объекта следующим образом:

final brightness = MediaQueryData.fromWindow(WidgetsBinding.instance.window).platformBrightness;

Однако я настоятельно рекомендую вам учитывать, что если вам нужен доступ к MediaQuery из вашего блока, вам следует вместо этого переместить свой BlocProvider для создания экземпляра после вашего MaterialApp, чтобы вы могли получить доступ к MediaQuery в обычном режиме.

person Abion47    schedule 23.02.2020
comment
Это работает! Но как я могу изменить всю тему приложения, если мой ThemeBloc создается после MaterialApp? - person Just A Question; 23.02.2020
comment
@JustAQuestion Это зависит от того, как вы переходите на разные страницы в приложении. Самый простой способ - использовать onGenerateRoute, чтобы обернуть каждую страницу, к которой осуществляется переход, в BlocProvider и сохранить каноническую ссылку на блок на уровне класса, а не создавать его вместе с поставщиком, чтобы вы не воссоздали блок снова и снова. - person Abion47; 23.02.2020
comment
Ладно спасибо, попробую. И последний вопрос, почему бы вам не порекомендовать низкоуровневый оконный объект? - person Just A Question; 23.02.2020
comment
@JustAQuestion Если вы извлекаете данные непосредственно из низкоуровневого объекта, ваше приложение не будет получать уведомления о каких-либо системных изменениях. Например, если ваше приложение настроено на соблюдение системной темы, а пользователь переходит в настройки и изменяет системную тему, ваше приложение не отреагирует на это изменение. Виджет MediaQuery слушает системные изменения, поэтому, если вы используете MediaQuery.of, ваше приложение будет автоматически перестроено, отражая изменения в системной теме. - person Abion47; 23.02.2020