【问题标题】:Flutter Dynamic ThemingFlutter 动态主题
【发布时间】:2019-02-19 01:01:51
【问题描述】:

动态更改 Flutter 应用主题的最佳方式是什么?例如,如果用户将颜色更改为红色,我希望主题立即更改为红色。我在网上找不到任何很有帮助的东西,除了一个人说要使用 BLOC 模式,我不熟悉它。我想听听你们对这个问题的看法。谢谢!

我目前的代码结构:

var themeData = ThemeData(
    fontFamily: 'Raleway',
    primaryColor: Colors.blue,
    brightness: Brightness.light,
    backgroundColor: Colors.white,
    accentColor: Colors.blue);

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: Constants.appName,
      theme: themeData,
      home: CheckAuth(), //CheckAuth returns MyHomePage usually
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title, @required this.uid}) : super(key: key);

  final String title;
  final String uid;

  @override
  _MyHomePageState createState() => _MyHomePageState();
    }

class _MyHomePageState extends State<MyHomePage> {
    ...build and stuff
    }

【问题讨论】:

  • 您希望应用的哪些方面包含在主题中?如果您可以添加一小段代码,对其他人也会有很大帮助。
  • 只是改变颜色和亮度(暗/亮)。没有太多具体的代码可以提供,基本上只是一个超级基本的应用程序。
  • 您基本上需要将此类配置(背景颜色、字体颜色、大小等)转换为变量,并在某些事件上更新它们。很难说根据可用信息你能实现什么以及如何实现,但希望你明白这一点。

标签: dart flutter


【解决方案1】:

如果你愿意,你可以使用InhertedWidget(而不是 BLOC)——基本上它用于访问树中任何地方的父窗口小部件。

所以你应该做的是

  1. 在树顶某处创建InheritedWidget [从您希望主题效果发生的位置]
  2. 将其包裹在 Theme 小部件上
  3. 通过传递要替换的 ThemeData 来公开切换主题的方法。

这里有一些代码:

import 'package:flutter/material.dart';

var themeData = ThemeData(
    fontFamily: 'Raleway',
    primaryColor: Colors.blue,
    brightness: Brightness.light,
    backgroundColor: Colors.white,
    accentColor: Colors.blue
);

void main() {
  runApp(
    ThemeSwitcherWidget(
      initialTheme: themeData,
      child: MyApp(),
    ),
  );
}

class ThemeSwitcher extends InheritedWidget {
  final _ThemeSwitcherWidgetState data;

  const ThemeSwitcher({
    Key key,
    @required this.data,
    @required Widget child,
  })  : assert(child != null),
        super(key: key, child: child);

  static _ThemeSwitcherWidgetState of(BuildContext context) {
    return (context. dependOnInheritedWidgetOfExactType(ThemeSwitcher)
            as ThemeSwitcher)
        .data;
  }

  @override
  bool updateShouldNotify(ThemeSwitcher old) {
    return this != old;
  }
}

class ThemeSwitcherWidget extends StatefulWidget {
  final ThemeData initialTheme;
  final Widget child;

  ThemeSwitcherWidget({Key key, this.initialTheme, this.child})
      : assert(initialTheme != null),
        assert(child != null),
        super(key: key);

  @override
  _ThemeSwitcherWidgetState createState() => _ThemeSwitcherWidgetState();
}

class _ThemeSwitcherWidgetState extends State<ThemeSwitcherWidget> {
  ThemeData themeData;

  void switchTheme(ThemeData theme) {
    setState(() {
      themeData = theme;
    });
  }

  @override
  Widget build(BuildContext context) {
    themeData = themeData ?? widget.initialTheme;
    return ThemeSwitcher(
      data: this,
      child: widget.child,
    );
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeSwitcher.of(context).themeData,
      home: CheckAuth(),
    );
  }
}

我已将 ThemeSwitcherWidget 包裹在 MaterialApp 周围,因此效果会贯穿整个应用程序(即使您使用 Navigator 推送新路线也是如此)。

在 ThemeSwithcerWidget 下方的任意位置使用 ThemeSwitcher.of(context).switchTheme(themeData) 更改主题。

在有问题的情况下,它应该调用 ThemeSwitcher.of(context).switchTheme(Theme.of(context).copyWith(primaryColor: Colors.red)) 以在整个应用程序中将原色切换为红色,例如。在某些按钮上单击

编辑:替换 inheritFromWidgetOfExactType -> dependOnInheritedWidgetOfExactType,因为它已被弃用 - 正如 cmets 中的 Phoca 所指出的那样。

【讨论】:

  • 希望这就是你要找的。​​span>
  • 谢谢,但我不确定如何实现这是我当前的应用程序。我用我当前的代码结构更新了我的问题。
  • 我添加了一些。希望这可能会有所帮助。
  • 出现错误。 “尝试调用:switchTheme('ThemeData'的实例)”,“NoSuchMethodError:方法'switchTheme'在null上被调用。”
  • Hello from 2021: inheritFromWidgetOfExactType 不再存在,在 Flutter 2 中被 dependOnInheritedWidgetOfExactType 取代
【解决方案2】:

使用provider 包:
theme_changer.dart

var darkTheme = ThemeData.dark();
var lightTheme= ThemeData.light();

class ThemeChanger extends ChangeNotifier {
  ThemeData _themeData;
  ThemeChanger(this._themeData);

  get getTheme => _themeData;
  void setTheme(ThemeData theme) {
    _themeData = theme;
    notifyListeners();
  }
}

main.dart

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => ThemeChanger(lightTheme)),
      ],
      child: MaterialAppWithTheme(),
    );
  }
}

class MaterialAppWithTheme extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final theme = Provider.of<ThemeChanger>(context);
    return MaterialApp(
      theme: theme.getTheme,
      home: FirstScreen(),
    );
  }

first_screen.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './theme_changer.dart'

class FirstScreen extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    var _themeProvider=Provider.of<ThemeChanger>(context);
    return Scaffold(
      appBar: AppBar(title:Text("First Screen"),),
      body:Container(width:MediaQuery.of(context).size.width,
           height:MediaQuery.of(context).size.height,
           child:Center(
                child:FlatButton(child:Text("Press me"). onPressed:(){
                  _themeProvider.setTheme(_themeProvider.getTheme==lightTheme?darkTheme:lightTheme);
                })
           ),
      ),
    );
  }
}

【讨论】:

    【解决方案3】:

    这是在您的应用中实现动态主题更改的方法:

    1.您应该将您的MyApp 更改为Stateful 小部件,以使该类在颜色更改时再次重建:

     var _primary = Colors.blue ; // This will hold the value of the app main color
    
     var themeData = ThemeData(
     fontFamily: 'Raleway',
     primaryColor: _primary, // so when the rebuilds the color changes take effect
     brightness: Brightness.light,
     backgroundColor: Colors.white,
     accentColor: Colors.blue);
    
     void main() => runApp(new App());
    
     class App extends StatefulWidget {
     App({Key key,}) :
    
     super(key: key);
    
      @override
      _AppState createState() => new _AppState();
    
      static void setTheme(BuildContext context, Color newColor) {
      _AppState state = context.ancestorStateOfType(TypeMatcher<_AppState>());
       state.setState(() {
         state._primary = newColor;
        });
      }
    }
    

    2.静态方法setTheme将负责颜色变化:

      class _AppState extends State<App> {
    
      @override
      Widget build(BuildContext context) {
      return MaterialApp(
      title: Constants.appName,
      theme: themeData,
      home: CheckAuth(), //CheckAuth returns MyHomePage usually
      );
     }
    }   
    

    3.当您想从代码中的任何位置更改主题颜色时,请调用此方法:

      App.setTheme(context, Colors.blue);  
    

    【讨论】:

      【解决方案4】:

      我使用了get插件并使用了Get.changeThemeMode(ThemeMode.(dark/system/light));它非常适合我

      首先你必须按照安装指南添加get plugin

      然后在主要变化中

      class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
      return GetMaterialApp(
        title: 'Add Your Title',
        debugShowCheckedModeBanner: false,
        theme:_lightTheme,
        darkTheme: _darkTheme,
        home: login(),
        );
       }
      }
      

      开启点击功能

      import 'package:get/get.dart';
      
      
                   
      
        onTap: () {
             Get.changeThemeMode(ThemeMode.dark);
                   setState(() async {
                        Navigator.pushReplacement(
                           context,
                           MaterialPageRoute(builder: (BuildContext context) => super.widget));
                      });
                    }
      

      我有两个主题和 onTap 按钮,我刚刚添加了一行

      Get.changeThemeMode(ThemeMode.dark) 用于暗模式, Get.changeThemeMode(ThemeMode.dark) 为浅色模式

      【讨论】:

      • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
      猜你喜欢
      • 2022-01-21
      • 2021-06-25
      • 2021-12-17
      • 2020-12-19
      • 2020-09-02
      • 2018-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多