Flutter:如何在运行时更改MaterialApp主题

Flu*_*ery 3 flutter

我有一个MaterialAppWidget,theme可以为应用程序中的所有小部件设置.我想在运行时从没有任何直接引用其父级的子窗口小部件更改MaterialApps themeMaterialApp.

看来这应该是可能的,因为它ThemeData是由一个提供的InheritedWidget,但我无法弄清楚如何改变theme批发.有谁知道如何做到这一点?

这是MaterialApp拥有应用程序其余部分的:

new MaterialApp(
    title: 'App Name',
    theme: initialTheme,
    routes: <String, WidgetBuilder>{
      '/' : ...,
    },
),
Run Code Online (Sandbox Code Playgroud)

Flu*_*ery 7

根据Dan Field的建议,我得出了以下解决方案.如果有人有改进,请随意加入:

// How to use: Any Widget in the app can access the ThemeChanger
// because it is an InheritedWidget. Then the Widget can call
// themeChanger.theme = [blah] to change the theme. The ThemeChanger
// then accesses AppThemeState by using the _themeGlobalKey, and
// the ThemeChanger switches out the old ThemeData for the new
// ThemeData in the AppThemeState (which causes a re-render).

final _themeGlobalKey = new GlobalKey(debugLabel: 'app_theme');

class AppTheme extends StatefulWidget {

  final child;

  AppTheme({
    this.child,
  }) : super(key: _themeGlobalKey);

  @override
  AppThemeState createState() => new AppThemeState();
}

class AppThemeState extends State<AppTheme> {

  ThemeData _theme = DEV_THEME;

  set theme(newTheme) {
    if (newTheme != _theme) {
      setState(() => _theme = newTheme);
    }
  }

  @override
  Widget build(BuildContext context) {
    return new ThemeChanger(
      appThemeKey: _themeGlobalKey,
      child: new Theme(
        data: _theme,
        child: widget.child,
      ),
    );
  }
}

class ThemeChanger extends InheritedWidget {

  static ThemeChanger of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(ThemeChanger);
  }

  final ThemeData theme;
  final GlobalKey _appThemeKey;

  ThemeChanger({
    appThemeKey,
    this.theme,
    child
  }) : _appThemeKey = appThemeKey, super(child: child);

  set appTheme(AppThemeOption theme) {
    switch (theme) {
      case AppThemeOption.experimental:
        (_appThemeKey.currentState as AppThemeState)?.theme = EXPERIMENT_THEME;
        break;
      case AppThemeOption.dev:
        (_appThemeKey.currentState as AppThemeState)?.theme = DEV_THEME;
        break;
    }
  }

  @override
  bool updateShouldNotify(ThemeChanger oldWidget) {
    return oldWidget.theme == theme;
  }

}
Run Code Online (Sandbox Code Playgroud)


Dan*_*eld 5

这是此处回答的问题的一个具体案例:Force Flutter to redraw all widgets

看看那个问题中提到的股票样本,特别注意:https : //github.com/flutter/flutter/blob/master/examples/stocks/lib/main.dart https://github.com/颤振/颤振/blob/master/examples/stocks/lib/stock_settings.dart

请注意以下事项:

  1. 主题由 指定_configuration,由 更新configurationUpdater
  2. configurationUpdater 传递给需要它的应用程序的孩子
  3. 孩子们可以调用那个 configurationUpdater,它反过来在应用程序的根设置状态,然后使用指定的主题重绘应用程序

  • 您的链接现在指向 404 页面 (3认同)

erl*_*man 5

您也可以使用StreamController.

只需复制并粘贴此代码。这是一个工作样本。你不需要任何图书馆,它超级简单

import 'dart:async';

import 'package:flutter/material.dart';

StreamController<bool> isLightTheme = StreamController();

main() {
  runApp(MainApp());
}

class MainApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<bool>(
        initialData: true,
        stream: isLightTheme.stream,
        builder: (context, snapshot) {
          return MaterialApp(
              theme: snapshot.data ? ThemeData.light() : ThemeData.dark(),
              debugShowCheckedModeBanner: false,
              home: Scaffold(
                  appBar: AppBar(title: Text("Dynamic Theme")),
                  body: SettingPage()));
        });
  }
}

class SettingPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Padding(
        padding: const EdgeInsets.all(16.0),
        child: Center(
            child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <
                Widget>[
          RaisedButton(
              color: Colors.blue,
              child: Text("Light Theme", style: TextStyle(color: Colors.white)),
              onPressed: () {
                isLightTheme.add(true);
              }),
          RaisedButton(
              color: Colors.black,
              child: Text("Dark Theme", style: TextStyle(color: Colors.white)),
              onPressed: () {
                isLightTheme.add(false);
              }),
        ])));
  }
}
Run Code Online (Sandbox Code Playgroud)