如何在flutter中实现Dark mode和Light Mode?
How to implement Dark mode and Light Mode in flutter?
我想创建一个 flutter 应用程序,它有 2 个明暗模式主题,通过应用程序内的开关改变,默认主题是默认 android 主题。
我需要将一些自定义颜色传递给其他小部件,我不想只配置 material 主题。
- 如何检测用户设备的默认主题?
- 第二个问题是如何为整个应用提供主题?
- 第三个是如何在运行时间内通过简单的切换来改变主题?
MaterialApp(
theme: ThemeData.light(),
/// theme: ThemeData.dark(),
)
在小部件树下,您只需编写 Theme.of(context)
即可访问 ThemeData。如果你想访问当前的 ThemeData 并为某个字段提供你自己的样式,你可以这样做:
Widget build(BuildContext context) {
var themeData = Theme.of(context).copyWith(scaffoldBackgroundColor: darkBlue)
return Scaffold(
backgroundColor = themeData.scaffoldBackgroundColor,
);
}
但是要处理 ThemeData 状态(更改其值),您需要实施适当的状态管理。
使用 Material 应用程序
MaterialApp(
title: 'App Title',
theme: ThemeData(
brightness: Brightness.light,
/* light theme settings */
),
darkTheme: ThemeData(
brightness: Brightness.dark,
/* dark theme settings */
),
themeMode: ThemeMode.dark,
/* ThemeMode.system to follow system theme,
ThemeMode.light for light theme,
ThemeMode.dark for dark theme
*/
debugShowCheckedModeBanner: false,
home: YourAppHomepage(),
);
使用 CupertinoApp
检测黑暗模式使用,WidgetsBinding.instance?.window.platformBrightness
您可能还需要监听系统的亮度变化,以便使用 WidgetsBindingObserver
和覆盖 didChangePlatformBrightness();
[=16= 进行实时更新]
CupertinoApp 示例:
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
Brightness? _brightness;
@override
void initState() {
WidgetsBinding.instance?.addObserver(this);
_brightness = WidgetsBinding.instance?.window.platformBrightness;
super.initState();
}
@override
void dispose() {
WidgetsBinding.instance?.removeObserver(this);
super.dispose();
}
@override
void didChangePlatformBrightness() {
if (mounted) {
setState(() {
_brightness = WidgetsBinding.instance?.window.platformBrightness;
});
}
super.didChangePlatformBrightness();
}
CupertinoThemeData get _lightTheme =>
CupertinoThemeData(brightness: Brightness.light, /* light theme settings */);
CupertinoThemeData get _darkTheme => CupertinoThemeData(
brightness: Brightness.dark, /* dark theme settings */,
);
@override
Widget build(BuildContext context) {
return CupertinoApp(
title: 'Demo App',
theme: _brightness == Brightness.dark ? _darkTheme : _lightTheme,
home: MyHomePage(title: 'Demo Home Page'),
);
}
}
您可以使用 scoped_model, provider, bloc or get 获得无缝体验。
我认为最简单的方法是使用提供程序来管理您的应用程序的状态,并 shared_preferences 将您的主题偏好保存在文件系统上。按照此过程,您可以保存主题,这样用户就不必每次都切换主题。
输出
您可以轻松地以字符串的形式存储您的主题首选项,然后在您的应用启动时检查文件系统中是否存储了值,如果有则应用该主题,如下所示。
StorageManager.dart
import 'package:shared_preferences/shared_preferences.dart';
class StorageManager {
static void saveData(String key, dynamic value) async {
final prefs = await SharedPreferences.getInstance();
if (value is int) {
prefs.setInt(key, value);
} else if (value is String) {
prefs.setString(key, value);
} else if (value is bool) {
prefs.setBool(key, value);
} else {
print("Invalid Type");
}
}
static Future<dynamic> readData(String key) async {
final prefs = await SharedPreferences.getInstance();
dynamic obj = prefs.get(key);
return obj;
}
static Future<bool> deleteData(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.remove(key);
}
}
在主题变量中定义主题属性,如下所示,并根据存储中的值初始化 _themedata 变量。
ThemeManager.dart
import 'package:flutter/material.dart';
import '../services/storage_manager.dart';
class ThemeNotifier with ChangeNotifier {
final darkTheme = ThemeData(
primarySwatch: Colors.grey,
primaryColor: Colors.black,
brightness: Brightness.dark,
backgroundColor: const Color(0xFF212121),
accentColor: Colors.white,
accentIconTheme: IconThemeData(color: Colors.black),
dividerColor: Colors.black12,
);
final lightTheme = ThemeData(
primarySwatch: Colors.grey,
primaryColor: Colors.white,
brightness: Brightness.light,
backgroundColor: const Color(0xFFE5E5E5),
accentColor: Colors.black,
accentIconTheme: IconThemeData(color: Colors.white),
dividerColor: Colors.white54,
);
ThemeData _themeData;
ThemeData getTheme() => _themeData;
ThemeNotifier() {
StorageManager.readData('themeMode').then((value) {
print('value read from storage: ' + value.toString());
var themeMode = value ?? 'light';
if (themeMode == 'light') {
_themeData = lightTheme;
} else {
print('setting dark theme');
_themeData = darkTheme;
}
notifyListeners();
});
}
void setDarkMode() async {
_themeData = darkTheme;
StorageManager.saveData('themeMode', 'dark');
notifyListeners();
}
void setLightMode() async {
_themeData = lightTheme;
StorageManager.saveData('themeMode', 'light');
notifyListeners();
}
}
使用 themeProvider 包装您的应用,然后使用消费者应用主题。每当您更改主题的值并调用通知侦听器小部件重建以同步更改时这样做。
Main.dart
void main() {
return runApp(ChangeNotifierProvider<ThemeNotifier>(
create: (_) => new ThemeNotifier(),
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<ThemeNotifier>(
builder: (context, theme, _) => MaterialApp(
theme: theme.getTheme(),
home: Scaffold(
appBar: AppBar(
title: Text('Hybrid Theme'),
),
body: Row(
children: [
Container(
child: FlatButton(
onPressed: () => {
print('Set Light Theme'),
theme.setLightMode(),
},
child: Text('Set Light Theme'),
),
),
Container(
child: FlatButton(
onPressed: () => {
print('Set Dark theme'),
theme.setDarkMode(),
},
child: Text('Set Dark theme'),
),
),
],
),
),
),
);
}
}
Here 是 link 到 github 存储库。
让系统处理主题:
runApp(
MaterialApp(
theme: ThemeData.light(), // Provide light theme
darkTheme: ThemeData.dark(), // Provide dark theme
home: HomePage(),
),
);
自己处理主题:
使用provider 以编程方式设置主题。完整代码:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<ThemeModel>(
create: (_) => ThemeModel(),
child: Consumer<ThemeModel>(
builder: (_, model, __) {
return MaterialApp(
theme: ThemeData.light(), // Provide light theme.
darkTheme: ThemeData.dark(), // Provide dark theme.
themeMode: model.mode, // Decides which theme to show.
home: Scaffold(
appBar: AppBar(title: Text('Light/Dark Theme')),
body: ElevatedButton(
onPressed: () => model.toggleMode(),
child: Text('Toggle Theme'),
),
),
);
},
),
);
}
}
class ThemeModel with ChangeNotifier {
ThemeMode _mode;
ThemeMode get mode => _mode;
ThemeModel({ThemeMode mode = ThemeMode.light}) : _mode = mode;
void toggleMode() {
_mode = _mode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light;
notifyListeners();
}
}
回答 OP 问题:
可以使用以下方式找到当前主题:
bool isDarkMode = MediaQuery.of(context).platformBrightness == Brightness.dark;
或
bool isDarkMode = SchedulerBinding.instance.window.platformBrightness == Brightness.dark;
您可以为整个应用提供主题,使用 theme
作为默认主题,使用 darkTheme
作为深色主题(如果系统启用了深色模式或您使用 themeMode
)
您可以使用上面代码中所示的提供程序包。
截图:
如果你不想使用任何第三方包或插件,你可以使用 ValueListenableBuilder
,它是 Flutter 开箱即用的。
完整代码:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final ValueNotifier<ThemeMode> _notifier = ValueNotifier(ThemeMode.light);
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeMode>(
valueListenable: _notifier,
builder: (_, mode, __) {
return MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: mode, // Decides which theme to show, light or dark.
home: Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () => _notifier.value = mode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light,
child: Text('Toggle Theme'),
),
),
),
);
},
);
}
}
您也可以使用可用的插件day_night_theme_flutter
一个Flutter插件,可以帮助你随着日出日落自动更换应用的主题。只需指定要使用的浅色和深色主题,一切就绪。您也可以使用自定义的日出和日落时间。
如何使用?
- 在你的pubspec.yaml
中添加最新版本的包
- 用 DayNightTheme 小部件包装 MaterialApp。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(), // Provide light theme.
darkTheme: ThemeData.dark(), // Provide dark theme.
themeMode: ThemeMode.system,
home: Scaffold(
appBar: AppBar(),
body: Container(),
),
);
}
}
theme: ThemeData.light(), // Provide light theme.
darkTheme: ThemeData.dark(), // Provide dark theme.
themeMode: ThemeMode.system,
//use only these three line for dynamic change theme respect to system theme.
这是一个代码
在此代码中,您已根据我的要求制作了自定义主题,您可以更改它!
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Theme',
debugShowCheckedModeBanner: false,
/* light theme settings */
theme: ThemeData(
primarySwatch: Colors.blue,
primaryColor: Colors.white,
brightness: Brightness.light,
accentColor: Colors.black,
accentIconTheme: IconThemeData(color: Colors.white),
dividerColor: Colors.white54,
scaffoldBackgroundColor: Colors.white,
),
/* Dark theme settings */
darkTheme: ThemeData(
primarySwatch: Colors.blue,
primaryColor: Colors.black,
brightness: Brightness.dark,
accentColor: Colors.white,
accentIconTheme: IconThemeData(color: Colors.black),
dividerColor: Colors.black12,
scaffoldBackgroundColor: Color(0xFF131313),
),
/* ThemeMode.system to follow system theme,
ThemeMode.light for light theme,
ThemeMode.dark for dark theme */
themeMode: ThemeMode.system,
home: MyHomePage(),
);
}
}
以下是实现深色模式的三种方法:
- 总是黑暗模式
- device/platform 控制黑暗模式
- 应用程序控制,运行时间可切换黑暗模式
始终暗模式
至 运行 您的应用仅在深色模式下:
- 在
MaterialApp
中,将ThemeData(...)
替换为ThemeData.dark()
- 重新启动您的应用程序。它现在将 运行ning 在深色模式下使用
ThemeData.dark()
中定义的颜色
旧
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
新
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark(), // default dark theme replaces default light theme
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
设备控制的深色模式
- 仅适用于 Android 10+、iOS 13+(引入深色模式时)
- 要让 device/platform 设置主题,
MaterialApp
需要 3 个参数:
theme: ThemeData()
darkTheme: ThemeData().dark
themeMode: ThemeMode.system
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
darkTheme: ThemeData.dark(), // standard dark theme
themeMode: ThemeMode.system, // device controls theme
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
- (您可以使用自定义主题。为简单起见,以上均为默认主题)
themeMode: ThemeMode.system
告诉 Flutter 使用 device/platform 主题设置
- 在 Android 10+ 或 iOS 13+ 上进行上述设置后,通过设备设置切换暗模式现在会在亮模式和暗模式之间切换您的应用。
- 在Android上:从屏幕顶部向下拖动并单击Dark theme toggle button。
- iOS 物理设备:设置 > 显示和亮度 > 亮或暗。
- iOS: add Dark mode switch to Control Center 为了便于测试
- iOS 模拟器:设置>开发者>深色外观。
- 只要设备主题发生变化,您的应用就会立即反映所选的设备主题
- 要以编程方式获取当前设备主题模式,我们可以检查设备亮度(
Brightness.light
或 Brightness.dark
),它对应于浅色模式和深色模式。通过查询 platformBrightness
来执行此操作:MediaQuery.of(context).platformBrightness
应用程序控制的深色模式
- 我们的应用程序可以 运行 亮或暗模式,由用户控制并在应用程序内 运行 时间自由切换,完全忽略设备的主题设置
- 和以前一样,向
MaterialApp
提供所有三个主题参数:theme:
、darkTheme:
和 themeMode:
,但我们将调整 themeMode:
以使用下面的状态字段
- 要在应用内切换亮/暗模式,我们将在
ThemeMode.light
和 ThemeMode.dark
之间交换 themeMode:
参数并重建 MaterialApp
小部件。
如何重建 MaterialApp 小部件
- 要从任何地方切换我们的应用主题,我们需要从我们应用的任何地方访问
MaterialApp
- 我们可以不使用任何包只使用
StatefulWidget
来做到这一点,或者我们可以使用状态管理包
- 运行使用下面的 StatefulWidget 在应用中任意位置切换时间主题的示例
之前 - 无状态
- 我们从这个开始,但我们将用
StatefulWidget
接下来替换它
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
darkTheme: ThemeData.dark(), // standard dark theme
themeMode: ThemeMode.system, // device controls theme
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
之后 - 有状态
- 这里我们用
StatefulWidget
和它的补充 State
class, _MyAppState
[=184= 替换了 MyApp
StatelessWidget
]
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
darkTheme: ThemeData.dark(), // standard dark theme
themeMode: ThemeMode.system, // device controls theme
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
将静态访问器添加到 StatefulWidget
- 将此静态
of()
方法添加到我们的 StatefulWidget
使其 State
对象可供任何后代小部件访问
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
/// ↓↓ ADDED
/// InheritedWidget style accessor to our State object.
static _MyAppState of(BuildContext context) =>
context.findAncestorStateOfType<_MyAppState>();
}
/// State object hidden ↓. Focusing on ↑ StatefulWidget here.
- 注意我们的
of()
方法的returnType
:_MyAppState
- 我们没有得到
StatefulWidget
,我们得到它的 State
对象:_MyAppState
_MyAppState
将保持我们 ThemeMode
设置的“状态”(在下一步中)。这是控制我们应用程序当前主题的内容。
- 接下来,在我们的
_MyAppState
class 中,我们将添加一个 ThemeMode
“状态”字段和一种更改主题和重建我们的应用程序的方法
_MyAppState
- 下面是我们的
State
class 修改为:
- 一个“状态”字段
_themeMode
MaterialApp
themeMode:
arg 使用 _themeMode
状态字段值
changeTheme
方法
class _MyAppState extends State<MyApp> {
/// 1) our themeMode "state" field
ThemeMode _themeMode = ThemeMode.system;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
darkTheme: ThemeData.dark(),
themeMode: _themeMode, // 2) ← ← ← use "state" field here //////////////
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
/// 3) Call this to change theme from any context using "of" accessor
/// e.g.:
/// MyApp.of(context).changeTheme(ThemeMode.dark);
void changeTheme(ThemeMode themeMode) {
setState(() {
_themeMode = themeMode;
});
}
}
- 接下来,我们将展示如何访问
changeTheme()
以更改我们的主题并重建应用程序
更改主题并重建
- 下面是使用
of()
访问器方法查找我们的 State
对象并从下面的两个按钮调用其 changeTheme 方法的示例:
MyApp.of(context).changeTheme(ThemeMode.light)
MyApp.of(context).changeTheme(ThemeMode.dark)
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({this.title});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Choose your theme:',
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
/// //////////////////////////////////////////////////////
/// Change theme & rebuild to show it using these buttons
ElevatedButton(
onPressed: () => MyApp.of(context).changeTheme(ThemeMode.light),
child: Text('Light')),
ElevatedButton(
onPressed: () => MyApp.of(context).changeTheme(ThemeMode.dark),
child: Text('Dark')),
/// //////////////////////////////////////////////////////
],
),
],
),
),
);
}
}
为了 return 主题控制回到设备的黑暗模式设置,创建第三个按钮来调用将 themeMode:
设置为 ThemeMode.system
:
MyApp.of(context).changeTheme(ThemeMode.system)
运行 此方法会将应用主题的控制委托回设备当前使用的任何深色模式设置。
代码:完整的复制粘贴代码available in this gist.
派对有点晚了,您可以在没有任何第三方状态管理的情况下使用内置 ValueNotifier.This 方法实现它,允许您从应用程序的任何部分更改整个应用程序的主题。
完整的代码示例
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
final darkNotifier = ValueNotifier<bool>(false);
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<bool>(
valueListenable: darkNotifier,
builder: (BuildContext context, bool isDark, Widget? child) {
return MaterialApp(
title: 'Flutter Demo',
themeMode: isDark ? ThemeMode.dark : ThemeMode.light,
theme: ThemeData(primaryColor: Colors.blue),
darkTheme: ThemeData.dark(),
home: MyHomePage(
title: 'Homepage',
),
);
});
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
void dispose() {
// TODO: implement dispose
darkNotifier.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
bool isDark = darkNotifier.value;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
_darkNotifier.value ? 'DarkMode' : 'LightMode',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
isDark = !isDark;
darkNotifier.value = isDark;
},
tooltip: 'Increment',
child: Icon(isDark ? Icons.wb_sunny_outlined : Icons.bubble_chart),
),
);
}
}
用于自定义 dark
主题
根据您的需要使用 darkTheme: ThemeData( use theme properties you need in dark mode)
描述:
如果 dark
模式在你的系统中被 selected 那么 flutter 使用 darkTheme
属性 of MaterialApp
并且如果 light 是 select ed 然后 flutter 使用 theme
属性 of MaterialApp
,下面的代码显示当你 select (在你的手机中尝试)你的系统中的 dark
选项然后你的应用程序将显示 scaffoldBackgroundColor: Colors.red
如果您 select light
那么它将显示 scaffoldBackgroundColor: Colors.amber
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
darkTheme: ThemeData(brightness: Brightness.dark, scaffoldBackgroundColor: Colors.red),
theme: ThemeData(
scaffoldBackgroundColor: Colors.amber,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
完整代码
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
darkTheme: ThemeData(brightness: Brightness.dark, scaffoldBackgroundColor: Colors.red),
// themeMode: ThemeMode.dark,
theme: ThemeData(
scaffoldBackgroundColor: Colors.amber,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
// print("brightness ${ColorScheme.}")
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'increment',
style: Theme.of(context).textTheme.headline4,
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
我从 ITnext 找到了一个非常好的 approach,它不需要第三方包(shared_preferences 或 hive 除外)。这里有一个简短的总结(没有导入和切换器):
// this makes all variables available globally
library config.globals;
// initialize the theme model once
ThemeModel currentTheme = ThemeModel();
// also declare the box
Box? box;
config.dart
class ThemeModel with ChangeNotifier {
// initialize the standard theme here, possible with an elvis to check for the brightness
static bool _isDark = false;
// set a getter just for a more clean approach
bool get isDark => _isDark;
ThemeModel() {
// check for a stored value on initialization
if(box!.containsKey("currentTheme")) {
_isDark = box!.get("currentTheme");
} else {
// if there is no value, apply the standard theme
box!.put("currentTheme", _isDark);
}
}
ThemeMode currentTheme() {
return _isDark ? ThemeMode.dark : ThemeMode.light;
}
void switchTheme() {
// switches the theme by reversing the boolean
_isDark = !_isDark;
// storing the new value
box!.put("currentTheme", _isDark);
// notifies all listeners attached to the theme model
notifyListeners();
}
}
theme_model.dart
void main() async {
// waits for the hive init before running the app
box = await Hive.openBox("theme");
runApp(YourApp());
}
class YourApp extends StatefulWidget {
@override
_YourAppState createState() => _YourAppState();
}
class _YourAppState extends State<YourApp> {
@override
void initState() {
super.initState();
// we are setting a listener to the currentTheme,
// so it gets notified once we toggle it
currentTheme.addListener(() {
setState((){});
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Your title',
theme: ThemeData().light,
darkTheme: ThemeData().dark,
// it will always listen to changes made to currentTheme
themeMode: currentTheme.currentTheme(),
home: HomePage(),
);
}
}
main.dart
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: <Widget>[
Switch(
// looking for the current value and sets the switch state
value: currentTheme.isDark,
onChanged: (value) {
setState(() {
// we then set the state to the new current theme
currentTheme.switchTheme();
});
},
),
// this is just a text next to the switch stating the current theme
Text("${currentTheme.currentTheme().toString().split(".")[1]} mode"),
],
);
);
}
}
homepage.dart
如果您想获得用户 ui 的偏好设置,您可以将默认值设置为 ThemeData.system。您必须调整代码以关注当前亮度,然后根据它的状态设置主题。之后它使用一个开关在暗模式和亮模式之间切换。
Example gif
下面是将主题浅色更改为深色的简单示例
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:theme_mode_switch/notify.dart';
void main() {
runApp(
ChangeNotifierProvider(create: (context) => DarkMode(), child: MyApp()));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final thmode = Provider.of<DarkMode>(context); ///accessing the variable of provider class
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Dark Mode',
theme: ThemeData(
///here the value of darmode var is updationg by switching
brightness: thmode.darkMode ? Brightness.dark : Brightness.light,
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final thmode = Provider.of<DarkMode>(context);
return Scaffold(
appBar: AppBar(
title: Text('Dark Mode'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(thmode.darkMode ? 'Dark' :'Light'),
CupertinoSwitch(
value: thmode.darkMode,
onChanged: (bool val) {
thmode.changemode();
},
),
],
),
));
}
}
class DarkMode with ChangeNotifier {
bool darkMode = true; ///by default it is true
///made a method which will execute while switching
changemode() {
darkMode = !darkMode;
notifyListeners(); ///notify the value or update the widget value
}
}
使用 get 软件包
比您想象的要容易得多
return GetMaterialApp(
themeMode: lightOrDark?ThemeMode.light:ThemeMode.dark,
...
);
多个 Flutter 主题示例(浅色和深色主题)
在 .yaml 文件中添加提供程序
像这样声明 runApp 方法
runApp(ChangeNotifierProvider(
create: (context) => ThemeState(),
child: MyApp(),
));
- 用
ChangeNotitifer
创建 ThemeState
class 和 extend
import 'package:flutter/material.dart';
enum ThemeType { DARK, LIGHT }
class ThemeState extends ChangeNotifier {
bool _isDarkTheme = false;
ThemeState() {
getTheme().then((type) {
_isDarkTheme = type == ThemeType.DARK;
notifyListeners();
});
}
ThemeType get theme => _isDarkTheme ? ThemeType.DARK : ThemeType.LIGHT;
set theme(ThemeType type) => setTheme(type);
void setTheme(ThemeType type) async {
_isDarkTheme = type == ThemeType.DARK;
notifyListeners();
}
Future<ThemeType> getTheme() async {
return _isDarkTheme ? ThemeType.DARK : ThemeType.LIGHT;
}
}
- 在 MyApp 中 class 在 MaterialApp 中像这样声明它
theme: Provider.of<ThemeState>(context).theme == ThemeType.DARK
? ThemeData(
// Define the default brightness and colors.
brightness: Brightness.dark,
primaryColor: Colors.lightBlue[800],
// Define the default font family.
fontFamily: 'Georgia',
// Define the default `TextTheme`. Use this to specify the default
// text styling for headlines, titles, bodies of text, and more.
textTheme: const TextTheme(
headline1:
TextStyle(fontSize: 32.0, fontWeight: FontWeight.bold),
headline6:
TextStyle(fontSize: 16.0, fontStyle: FontStyle.italic),
bodyText2: TextStyle(fontSize: 10.0, fontFamily: 'Hind'),
),
)
: ThemeData(
// Define the default brightness and colors.
brightness: Brightness.light,
primaryColor: Colors.lightGreen[300],
// Define the default font family.
fontFamily: 'Georgia',
// Define the default `TextTheme`. Use this to specify the default
// text styling for headlines, titles, bodies of text, and more.
textTheme: const TextTheme(
headline1:
TextStyle(fontSize: 32.0, fontWeight: FontWeight.normal),
headline6:
TextStyle(fontSize: 16.0, fontStyle: FontStyle.italic),
bodyText2: TextStyle(fontSize: 10.0, fontFamily: 'Hind'),
),
),
我想创建一个 flutter 应用程序,它有 2 个明暗模式主题,通过应用程序内的开关改变,默认主题是默认 android 主题。
我需要将一些自定义颜色传递给其他小部件,我不想只配置 material 主题。
- 如何检测用户设备的默认主题?
- 第二个问题是如何为整个应用提供主题?
- 第三个是如何在运行时间内通过简单的切换来改变主题?
MaterialApp(
theme: ThemeData.light(),
/// theme: ThemeData.dark(),
)
在小部件树下,您只需编写 Theme.of(context)
即可访问 ThemeData。如果你想访问当前的 ThemeData 并为某个字段提供你自己的样式,你可以这样做:
Widget build(BuildContext context) {
var themeData = Theme.of(context).copyWith(scaffoldBackgroundColor: darkBlue)
return Scaffold(
backgroundColor = themeData.scaffoldBackgroundColor,
);
}
但是要处理 ThemeData 状态(更改其值),您需要实施适当的状态管理。
使用 Material 应用程序
MaterialApp(
title: 'App Title',
theme: ThemeData(
brightness: Brightness.light,
/* light theme settings */
),
darkTheme: ThemeData(
brightness: Brightness.dark,
/* dark theme settings */
),
themeMode: ThemeMode.dark,
/* ThemeMode.system to follow system theme,
ThemeMode.light for light theme,
ThemeMode.dark for dark theme
*/
debugShowCheckedModeBanner: false,
home: YourAppHomepage(),
);
使用 CupertinoApp
检测黑暗模式使用,
WidgetsBinding.instance?.window.platformBrightness
您可能还需要监听系统的亮度变化,以便使用
WidgetsBindingObserver
和覆盖didChangePlatformBrightness();
[=16= 进行实时更新]
CupertinoApp 示例:
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
Brightness? _brightness;
@override
void initState() {
WidgetsBinding.instance?.addObserver(this);
_brightness = WidgetsBinding.instance?.window.platformBrightness;
super.initState();
}
@override
void dispose() {
WidgetsBinding.instance?.removeObserver(this);
super.dispose();
}
@override
void didChangePlatformBrightness() {
if (mounted) {
setState(() {
_brightness = WidgetsBinding.instance?.window.platformBrightness;
});
}
super.didChangePlatformBrightness();
}
CupertinoThemeData get _lightTheme =>
CupertinoThemeData(brightness: Brightness.light, /* light theme settings */);
CupertinoThemeData get _darkTheme => CupertinoThemeData(
brightness: Brightness.dark, /* dark theme settings */,
);
@override
Widget build(BuildContext context) {
return CupertinoApp(
title: 'Demo App',
theme: _brightness == Brightness.dark ? _darkTheme : _lightTheme,
home: MyHomePage(title: 'Demo Home Page'),
);
}
}
您可以使用 scoped_model, provider, bloc or get 获得无缝体验。
我认为最简单的方法是使用提供程序来管理您的应用程序的状态,并 shared_preferences 将您的主题偏好保存在文件系统上。按照此过程,您可以保存主题,这样用户就不必每次都切换主题。
输出
您可以轻松地以字符串的形式存储您的主题首选项,然后在您的应用启动时检查文件系统中是否存储了值,如果有则应用该主题,如下所示。
StorageManager.dart
import 'package:shared_preferences/shared_preferences.dart';
class StorageManager {
static void saveData(String key, dynamic value) async {
final prefs = await SharedPreferences.getInstance();
if (value is int) {
prefs.setInt(key, value);
} else if (value is String) {
prefs.setString(key, value);
} else if (value is bool) {
prefs.setBool(key, value);
} else {
print("Invalid Type");
}
}
static Future<dynamic> readData(String key) async {
final prefs = await SharedPreferences.getInstance();
dynamic obj = prefs.get(key);
return obj;
}
static Future<bool> deleteData(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.remove(key);
}
}
在主题变量中定义主题属性,如下所示,并根据存储中的值初始化 _themedata 变量。
ThemeManager.dart
import 'package:flutter/material.dart';
import '../services/storage_manager.dart';
class ThemeNotifier with ChangeNotifier {
final darkTheme = ThemeData(
primarySwatch: Colors.grey,
primaryColor: Colors.black,
brightness: Brightness.dark,
backgroundColor: const Color(0xFF212121),
accentColor: Colors.white,
accentIconTheme: IconThemeData(color: Colors.black),
dividerColor: Colors.black12,
);
final lightTheme = ThemeData(
primarySwatch: Colors.grey,
primaryColor: Colors.white,
brightness: Brightness.light,
backgroundColor: const Color(0xFFE5E5E5),
accentColor: Colors.black,
accentIconTheme: IconThemeData(color: Colors.white),
dividerColor: Colors.white54,
);
ThemeData _themeData;
ThemeData getTheme() => _themeData;
ThemeNotifier() {
StorageManager.readData('themeMode').then((value) {
print('value read from storage: ' + value.toString());
var themeMode = value ?? 'light';
if (themeMode == 'light') {
_themeData = lightTheme;
} else {
print('setting dark theme');
_themeData = darkTheme;
}
notifyListeners();
});
}
void setDarkMode() async {
_themeData = darkTheme;
StorageManager.saveData('themeMode', 'dark');
notifyListeners();
}
void setLightMode() async {
_themeData = lightTheme;
StorageManager.saveData('themeMode', 'light');
notifyListeners();
}
}
使用 themeProvider 包装您的应用,然后使用消费者应用主题。每当您更改主题的值并调用通知侦听器小部件重建以同步更改时这样做。
Main.dart
void main() {
return runApp(ChangeNotifierProvider<ThemeNotifier>(
create: (_) => new ThemeNotifier(),
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<ThemeNotifier>(
builder: (context, theme, _) => MaterialApp(
theme: theme.getTheme(),
home: Scaffold(
appBar: AppBar(
title: Text('Hybrid Theme'),
),
body: Row(
children: [
Container(
child: FlatButton(
onPressed: () => {
print('Set Light Theme'),
theme.setLightMode(),
},
child: Text('Set Light Theme'),
),
),
Container(
child: FlatButton(
onPressed: () => {
print('Set Dark theme'),
theme.setDarkMode(),
},
child: Text('Set Dark theme'),
),
),
],
),
),
),
);
}
}
Here 是 link 到 github 存储库。
让系统处理主题:
runApp(
MaterialApp(
theme: ThemeData.light(), // Provide light theme
darkTheme: ThemeData.dark(), // Provide dark theme
home: HomePage(),
),
);
自己处理主题:
使用provider 以编程方式设置主题。完整代码:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<ThemeModel>(
create: (_) => ThemeModel(),
child: Consumer<ThemeModel>(
builder: (_, model, __) {
return MaterialApp(
theme: ThemeData.light(), // Provide light theme.
darkTheme: ThemeData.dark(), // Provide dark theme.
themeMode: model.mode, // Decides which theme to show.
home: Scaffold(
appBar: AppBar(title: Text('Light/Dark Theme')),
body: ElevatedButton(
onPressed: () => model.toggleMode(),
child: Text('Toggle Theme'),
),
),
);
},
),
);
}
}
class ThemeModel with ChangeNotifier {
ThemeMode _mode;
ThemeMode get mode => _mode;
ThemeModel({ThemeMode mode = ThemeMode.light}) : _mode = mode;
void toggleMode() {
_mode = _mode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light;
notifyListeners();
}
}
回答 OP 问题:
可以使用以下方式找到当前主题:
bool isDarkMode = MediaQuery.of(context).platformBrightness == Brightness.dark;
或
bool isDarkMode = SchedulerBinding.instance.window.platformBrightness == Brightness.dark;
您可以为整个应用提供主题,使用
theme
作为默认主题,使用darkTheme
作为深色主题(如果系统启用了深色模式或您使用themeMode
)您可以使用上面代码中所示的提供程序包。
截图:
如果你不想使用任何第三方包或插件,你可以使用 ValueListenableBuilder
,它是 Flutter 开箱即用的。
完整代码:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final ValueNotifier<ThemeMode> _notifier = ValueNotifier(ThemeMode.light);
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeMode>(
valueListenable: _notifier,
builder: (_, mode, __) {
return MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: mode, // Decides which theme to show, light or dark.
home: Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () => _notifier.value = mode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light,
child: Text('Toggle Theme'),
),
),
),
);
},
);
}
}
您也可以使用可用的插件day_night_theme_flutter
一个Flutter插件,可以帮助你随着日出日落自动更换应用的主题。只需指定要使用的浅色和深色主题,一切就绪。您也可以使用自定义的日出和日落时间。
如何使用?
- 在你的pubspec.yaml 中添加最新版本的包
- 用 DayNightTheme 小部件包装 MaterialApp。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(), // Provide light theme.
darkTheme: ThemeData.dark(), // Provide dark theme.
themeMode: ThemeMode.system,
home: Scaffold(
appBar: AppBar(),
body: Container(),
),
);
}
}
theme: ThemeData.light(), // Provide light theme.
darkTheme: ThemeData.dark(), // Provide dark theme.
themeMode: ThemeMode.system,
//use only these three line for dynamic change theme respect to system theme.
这是一个代码
在此代码中,您已根据我的要求制作了自定义主题,您可以更改它!
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Theme',
debugShowCheckedModeBanner: false,
/* light theme settings */
theme: ThemeData(
primarySwatch: Colors.blue,
primaryColor: Colors.white,
brightness: Brightness.light,
accentColor: Colors.black,
accentIconTheme: IconThemeData(color: Colors.white),
dividerColor: Colors.white54,
scaffoldBackgroundColor: Colors.white,
),
/* Dark theme settings */
darkTheme: ThemeData(
primarySwatch: Colors.blue,
primaryColor: Colors.black,
brightness: Brightness.dark,
accentColor: Colors.white,
accentIconTheme: IconThemeData(color: Colors.black),
dividerColor: Colors.black12,
scaffoldBackgroundColor: Color(0xFF131313),
),
/* ThemeMode.system to follow system theme,
ThemeMode.light for light theme,
ThemeMode.dark for dark theme */
themeMode: ThemeMode.system,
home: MyHomePage(),
);
}
}
以下是实现深色模式的三种方法:
- 总是黑暗模式
- device/platform 控制黑暗模式
- 应用程序控制,运行时间可切换黑暗模式
始终暗模式
至 运行 您的应用仅在深色模式下:
- 在
MaterialApp
中,将ThemeData(...)
替换为ThemeData.dark()
- 重新启动您的应用程序。它现在将 运行ning 在深色模式下使用
ThemeData.dark()
中定义的颜色
旧
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
新
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark(), // default dark theme replaces default light theme
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
设备控制的深色模式
- 仅适用于 Android 10+、iOS 13+(引入深色模式时)
- 要让 device/platform 设置主题,
MaterialApp
需要 3 个参数:theme: ThemeData()
darkTheme: ThemeData().dark
themeMode: ThemeMode.system
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
darkTheme: ThemeData.dark(), // standard dark theme
themeMode: ThemeMode.system, // device controls theme
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
- (您可以使用自定义主题。为简单起见,以上均为默认主题)
themeMode: ThemeMode.system
告诉 Flutter 使用 device/platform 主题设置- 在 Android 10+ 或 iOS 13+ 上进行上述设置后,通过设备设置切换暗模式现在会在亮模式和暗模式之间切换您的应用。
- 在Android上:从屏幕顶部向下拖动并单击Dark theme toggle button。
- iOS 物理设备:设置 > 显示和亮度 > 亮或暗。
- iOS: add Dark mode switch to Control Center 为了便于测试
- iOS 模拟器:设置>开发者>深色外观。
- 只要设备主题发生变化,您的应用就会立即反映所选的设备主题
- 要以编程方式获取当前设备主题模式,我们可以检查设备亮度(
Brightness.light
或Brightness.dark
),它对应于浅色模式和深色模式。通过查询platformBrightness
来执行此操作:MediaQuery.of(context).platformBrightness
应用程序控制的深色模式
- 我们的应用程序可以 运行 亮或暗模式,由用户控制并在应用程序内 运行 时间自由切换,完全忽略设备的主题设置
- 和以前一样,向
MaterialApp
提供所有三个主题参数:theme:
、darkTheme:
和themeMode:
,但我们将调整themeMode:
以使用下面的状态字段 - 要在应用内切换亮/暗模式,我们将在
ThemeMode.light
和ThemeMode.dark
之间交换themeMode:
参数并重建MaterialApp
小部件。
如何重建 MaterialApp 小部件
- 要从任何地方切换我们的应用主题,我们需要从我们应用的任何地方访问
MaterialApp
- 我们可以不使用任何包只使用
StatefulWidget
来做到这一点,或者我们可以使用状态管理包 - 运行使用下面的 StatefulWidget 在应用中任意位置切换时间主题的示例
之前 - 无状态
- 我们从这个开始,但我们将用
StatefulWidget
接下来替换它
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
darkTheme: ThemeData.dark(), // standard dark theme
themeMode: ThemeMode.system, // device controls theme
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
之后 - 有状态
- 这里我们用
StatefulWidget
和它的补充State
class,_MyAppState
[=184= 替换了MyApp
StatelessWidget
]
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
darkTheme: ThemeData.dark(), // standard dark theme
themeMode: ThemeMode.system, // device controls theme
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
将静态访问器添加到 StatefulWidget
- 将此静态
of()
方法添加到我们的StatefulWidget
使其State
对象可供任何后代小部件访问
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
/// ↓↓ ADDED
/// InheritedWidget style accessor to our State object.
static _MyAppState of(BuildContext context) =>
context.findAncestorStateOfType<_MyAppState>();
}
/// State object hidden ↓. Focusing on ↑ StatefulWidget here.
- 注意我们的
of()
方法的returnType
:_MyAppState
- 我们没有得到
StatefulWidget
,我们得到它的State
对象:_MyAppState
_MyAppState
将保持我们ThemeMode
设置的“状态”(在下一步中)。这是控制我们应用程序当前主题的内容。- 接下来,在我们的
_MyAppState
class 中,我们将添加一个ThemeMode
“状态”字段和一种更改主题和重建我们的应用程序的方法
_MyAppState
- 下面是我们的
State
class 修改为:- 一个“状态”字段
_themeMode
MaterialApp
themeMode:
arg 使用_themeMode
状态字段值changeTheme
方法
- 一个“状态”字段
class _MyAppState extends State<MyApp> {
/// 1) our themeMode "state" field
ThemeMode _themeMode = ThemeMode.system;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
darkTheme: ThemeData.dark(),
themeMode: _themeMode, // 2) ← ← ← use "state" field here //////////////
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
/// 3) Call this to change theme from any context using "of" accessor
/// e.g.:
/// MyApp.of(context).changeTheme(ThemeMode.dark);
void changeTheme(ThemeMode themeMode) {
setState(() {
_themeMode = themeMode;
});
}
}
- 接下来,我们将展示如何访问
changeTheme()
以更改我们的主题并重建应用程序
更改主题并重建
- 下面是使用
of()
访问器方法查找我们的State
对象并从下面的两个按钮调用其 changeTheme 方法的示例:MyApp.of(context).changeTheme(ThemeMode.light)
MyApp.of(context).changeTheme(ThemeMode.dark)
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({this.title});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Choose your theme:',
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
/// //////////////////////////////////////////////////////
/// Change theme & rebuild to show it using these buttons
ElevatedButton(
onPressed: () => MyApp.of(context).changeTheme(ThemeMode.light),
child: Text('Light')),
ElevatedButton(
onPressed: () => MyApp.of(context).changeTheme(ThemeMode.dark),
child: Text('Dark')),
/// //////////////////////////////////////////////////////
],
),
],
),
),
);
}
}
为了 return 主题控制回到设备的黑暗模式设置,创建第三个按钮来调用将 themeMode:
设置为 ThemeMode.system
:
MyApp.of(context).changeTheme(ThemeMode.system)
运行 此方法会将应用主题的控制委托回设备当前使用的任何深色模式设置。
代码:完整的复制粘贴代码available in this gist.
派对有点晚了,您可以在没有任何第三方状态管理的情况下使用内置 ValueNotifier.This 方法实现它,允许您从应用程序的任何部分更改整个应用程序的主题。
完整的代码示例
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
final darkNotifier = ValueNotifier<bool>(false);
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<bool>(
valueListenable: darkNotifier,
builder: (BuildContext context, bool isDark, Widget? child) {
return MaterialApp(
title: 'Flutter Demo',
themeMode: isDark ? ThemeMode.dark : ThemeMode.light,
theme: ThemeData(primaryColor: Colors.blue),
darkTheme: ThemeData.dark(),
home: MyHomePage(
title: 'Homepage',
),
);
});
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
void dispose() {
// TODO: implement dispose
darkNotifier.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
bool isDark = darkNotifier.value;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
_darkNotifier.value ? 'DarkMode' : 'LightMode',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
isDark = !isDark;
darkNotifier.value = isDark;
},
tooltip: 'Increment',
child: Icon(isDark ? Icons.wb_sunny_outlined : Icons.bubble_chart),
),
);
}
}
用于自定义 dark
主题
根据您的需要使用 darkTheme: ThemeData( use theme properties you need in dark mode)
描述:
如果 dark
模式在你的系统中被 selected 那么 flutter 使用 darkTheme
属性 of MaterialApp
并且如果 light 是 select ed 然后 flutter 使用 theme
属性 of MaterialApp
,下面的代码显示当你 select (在你的手机中尝试)你的系统中的 dark
选项然后你的应用程序将显示 scaffoldBackgroundColor: Colors.red
如果您 select light
那么它将显示 scaffoldBackgroundColor: Colors.amber
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
darkTheme: ThemeData(brightness: Brightness.dark, scaffoldBackgroundColor: Colors.red),
theme: ThemeData(
scaffoldBackgroundColor: Colors.amber,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
完整代码
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
darkTheme: ThemeData(brightness: Brightness.dark, scaffoldBackgroundColor: Colors.red),
// themeMode: ThemeMode.dark,
theme: ThemeData(
scaffoldBackgroundColor: Colors.amber,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
// print("brightness ${ColorScheme.}")
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'increment',
style: Theme.of(context).textTheme.headline4,
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
我从 ITnext 找到了一个非常好的 approach,它不需要第三方包(shared_preferences 或 hive 除外)。这里有一个简短的总结(没有导入和切换器):
// this makes all variables available globally
library config.globals;
// initialize the theme model once
ThemeModel currentTheme = ThemeModel();
// also declare the box
Box? box;
config.dart
class ThemeModel with ChangeNotifier {
// initialize the standard theme here, possible with an elvis to check for the brightness
static bool _isDark = false;
// set a getter just for a more clean approach
bool get isDark => _isDark;
ThemeModel() {
// check for a stored value on initialization
if(box!.containsKey("currentTheme")) {
_isDark = box!.get("currentTheme");
} else {
// if there is no value, apply the standard theme
box!.put("currentTheme", _isDark);
}
}
ThemeMode currentTheme() {
return _isDark ? ThemeMode.dark : ThemeMode.light;
}
void switchTheme() {
// switches the theme by reversing the boolean
_isDark = !_isDark;
// storing the new value
box!.put("currentTheme", _isDark);
// notifies all listeners attached to the theme model
notifyListeners();
}
}
theme_model.dart
void main() async {
// waits for the hive init before running the app
box = await Hive.openBox("theme");
runApp(YourApp());
}
class YourApp extends StatefulWidget {
@override
_YourAppState createState() => _YourAppState();
}
class _YourAppState extends State<YourApp> {
@override
void initState() {
super.initState();
// we are setting a listener to the currentTheme,
// so it gets notified once we toggle it
currentTheme.addListener(() {
setState((){});
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Your title',
theme: ThemeData().light,
darkTheme: ThemeData().dark,
// it will always listen to changes made to currentTheme
themeMode: currentTheme.currentTheme(),
home: HomePage(),
);
}
}
main.dart
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: <Widget>[
Switch(
// looking for the current value and sets the switch state
value: currentTheme.isDark,
onChanged: (value) {
setState(() {
// we then set the state to the new current theme
currentTheme.switchTheme();
});
},
),
// this is just a text next to the switch stating the current theme
Text("${currentTheme.currentTheme().toString().split(".")[1]} mode"),
],
);
);
}
}
homepage.dart
如果您想获得用户 ui 的偏好设置,您可以将默认值设置为 ThemeData.system。您必须调整代码以关注当前亮度,然后根据它的状态设置主题。之后它使用一个开关在暗模式和亮模式之间切换。
Example gif
下面是将主题浅色更改为深色的简单示例
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:theme_mode_switch/notify.dart';
void main() {
runApp(
ChangeNotifierProvider(create: (context) => DarkMode(), child: MyApp()));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final thmode = Provider.of<DarkMode>(context); ///accessing the variable of provider class
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Dark Mode',
theme: ThemeData(
///here the value of darmode var is updationg by switching
brightness: thmode.darkMode ? Brightness.dark : Brightness.light,
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final thmode = Provider.of<DarkMode>(context);
return Scaffold(
appBar: AppBar(
title: Text('Dark Mode'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(thmode.darkMode ? 'Dark' :'Light'),
CupertinoSwitch(
value: thmode.darkMode,
onChanged: (bool val) {
thmode.changemode();
},
),
],
),
));
}
}
class DarkMode with ChangeNotifier {
bool darkMode = true; ///by default it is true
///made a method which will execute while switching
changemode() {
darkMode = !darkMode;
notifyListeners(); ///notify the value or update the widget value
}
}
使用 get 软件包
比您想象的要容易得多return GetMaterialApp(
themeMode: lightOrDark?ThemeMode.light:ThemeMode.dark,
...
);
多个 Flutter 主题示例(浅色和深色主题)
在 .yaml 文件中添加提供程序
像这样声明 runApp 方法
runApp(ChangeNotifierProvider( create: (context) => ThemeState(), child: MyApp(), ));
- 用
ChangeNotitifer
创建
ThemeState
class 和 extend
import 'package:flutter/material.dart';
enum ThemeType { DARK, LIGHT }
class ThemeState extends ChangeNotifier {
bool _isDarkTheme = false;
ThemeState() {
getTheme().then((type) {
_isDarkTheme = type == ThemeType.DARK;
notifyListeners();
});
}
ThemeType get theme => _isDarkTheme ? ThemeType.DARK : ThemeType.LIGHT;
set theme(ThemeType type) => setTheme(type);
void setTheme(ThemeType type) async {
_isDarkTheme = type == ThemeType.DARK;
notifyListeners();
}
Future<ThemeType> getTheme() async {
return _isDarkTheme ? ThemeType.DARK : ThemeType.LIGHT;
}
}
- 在 MyApp 中 class 在 MaterialApp 中像这样声明它
theme: Provider.of<ThemeState>(context).theme == ThemeType.DARK
? ThemeData(
// Define the default brightness and colors.
brightness: Brightness.dark,
primaryColor: Colors.lightBlue[800],
// Define the default font family.
fontFamily: 'Georgia',
// Define the default `TextTheme`. Use this to specify the default
// text styling for headlines, titles, bodies of text, and more.
textTheme: const TextTheme(
headline1:
TextStyle(fontSize: 32.0, fontWeight: FontWeight.bold),
headline6:
TextStyle(fontSize: 16.0, fontStyle: FontStyle.italic),
bodyText2: TextStyle(fontSize: 10.0, fontFamily: 'Hind'),
),
)
: ThemeData(
// Define the default brightness and colors.
brightness: Brightness.light,
primaryColor: Colors.lightGreen[300],
// Define the default font family.
fontFamily: 'Georgia',
// Define the default `TextTheme`. Use this to specify the default
// text styling for headlines, titles, bodies of text, and more.
textTheme: const TextTheme(
headline1:
TextStyle(fontSize: 32.0, fontWeight: FontWeight.normal),
headline6:
TextStyle(fontSize: 16.0, fontStyle: FontStyle.italic),
bodyText2: TextStyle(fontSize: 10.0, fontFamily: 'Hind'),
),
),