Flutter 动态主题

Jar*_*red 6 dart flutter

动态更改 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
    }
Run Code Online (Sandbox Code Playgroud)

Har*_*dia 10

如果您愿意,您可以使用InhertedWidget(而不是 BLOC) - 基本上它用于从树中的任何位置访问父小部件。

所以你应该做的是

  1. create 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(),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

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

使用ThemeSwitcher.of(context).switchTheme(themeData)ThemeSwithcerWidget 下面的任何地方来更改主题。

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

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


Bor*_*ayi 6

使用提供程序包:
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();
  }
}

Run Code Online (Sandbox Code Playgroud)

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(),
    );
  }
Run Code Online (Sandbox Code Playgroud)

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);
                })
           ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)