我想创建一个具有2个明暗模式主题的flutter应用程序,该主题可以通过应用程序内的开关更改,默认主题为默认android主题。
我需要将一些自定义颜色传递给同一个窗口小部件,并且我不想仅配置材质主题。
答案 0 :(得分:16)
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(),
);
您可以使用scoped_model获得无缝体验。
答案 1 :(得分:8)
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(),
),
);
}
}
答案 2 :(得分:7)
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状态(更改其值),您需要实施适当的状态管理。
答案 3 :(得分:6)
我认为最简单的方法是使用提供程序来管理应用程序的状态,并使用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是指向github存储库的链接。
答案 4 :(得分:4)
以下是实现暗模式的三种方式:
仅在深色模式下运行您的应用:
MaterialApp
中,将ThemeData(...)
替换为ThemeData.dark()
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'),
);
}
}
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 使用设备/平台主题设置Brightness.light
或 Brightness.dark
)。为此,请使用以下内容查询 platformBrightness
:MediaQuery.of(context).platformBrightness
MaterialApp
提供所有三个主题参数:theme:
、darkTheme:
和 themeMode:
,但我们将调整 themeMode:
以使用状态下面的字段themeMode:
和 ThemeMode.light
之间交换 ThemeMode.dark
参数并重建 MaterialApp
小部件。如何重建 MaterialApp 小部件
MaterialApp
StatefulWidget
来完成此操作,或者我们可以使用状态管理包之前 - 无状态
StatefulWidget
next 替换它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'),
);
}
}
之后 - 有状态
MyApp
StatelessWidget
替换为 StatefulWidget
及其互补的 State
类 _MyAppState
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.
Type
方法的返回 of()
:_MyAppState
StatefulWidget
,我们得到的是它的 State
对象:_MyAppState
_MyAppState
将保存我们 ThemeMode
设置的“状态”(在下一步中)。这是控制我们应用当前主题的东西。_MyAppState
类中,我们将添加一个 ThemeMode
“状态”字段和一个更改主题和重建我们的应用程序的方法_MyAppState
State
类修改为:
_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')),
/// //////////////////////////////////////////////////////
],
),
],
),
),
);
}
}
要将主题控制返回到设备的暗模式设置,请创建第三个按钮来调用将 themeMode:
设置为 ThemeMode.system
:
MyApp.of(context).changeTheme(ThemeMode.system)
运行此方法会将应用主题的控制权委托给设备当前使用的任何暗模式设置。
代码:完整的复制粘贴代码available in this gist。
答案 5 :(得分:2)
截屏:
如果您不想使用任何第三方软件包或插件,则可以使用Flutter随附的ValueListenableBuilder
。
完整代码:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final _notifier = ValueNotifier<ThemeModel>(ThemeModel(ThemeMode.light));
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeModel>(
valueListenable: _notifier,
builder: (_, model, __) {
final mode = model.mode;
return MaterialApp(
theme: ThemeData.light(), // Provide light theme.
darkTheme: ThemeData.dark(), // Provide dark theme.
themeMode: mode, // Decides which theme to show.
home: Scaffold(
appBar: AppBar(title: Text('Light/Dark Theme')),
body: RaisedButton(
onPressed: () => _notifier.value = ThemeModel(mode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light),
child: Text('Toggle Theme'),
),
),
);
},
);
}
}
class ThemeModel with ChangeNotifier {
final ThemeMode _mode;
ThemeMode get mode => _mode;
ThemeModel(this._mode);
}
答案 6 :(得分:1)
截屏:
您可以使用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: RaisedButton(
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
启用了深色模式,则可以为整个应用提供主题)
您可以使用上面的代码中所示的提供程序包。
答案 7 :(得分:1)
您也可以使用可用的插件 day_night_theme_flutter
一个 Flutter 插件,可帮助您根据日出和日落自动更改应用程序的主题。只需指定要使用的明暗主题,就可以了。您也可以使用自定义的日出和日落时间。
如何使用?
答案 8 :(得分:1)
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.
答案 9 :(得分:0)
这是一个代码
在这段代码中,您根据我的要求制作了自定义主题,您可以更改它!!
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(),
);
}
}
答案 10 :(得分:0)
您可以使用内置的 ValueNotifier 在没有任何第三方状态管理的情况下实现它。这种方法允许您从应用的任何部分更改整个应用的主题。
这里是 dartpad demo
完整的代码示例
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),
),
);
}
}