我有一个MaterialApp
窗口小部件,可以为应用内的所有窗口小部件设置theme
。我想在运行时更改MaterialApp
s theme
值,该子窗口小部件没有直接引用其父MaterialApp
。
似乎这应该是可能的,因为ThemeData
由InheritedWidget
提供,但我无法弄清楚如何更改theme
批发。有谁知道怎么做?
以下是拥有应用其余部分的MaterialApp
:
new MaterialApp(
title: 'App Name',
theme: initialTheme,
routes: <String, WidgetBuilder>{
'/' : ...,
},
),
答案 0 :(得分:6)
根据Dan Field的建议,我得出了以下解决方案。如果有人有改进,请随意加入:
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorAccent">@android:color/white</item>
<!-- colorPrimary is used for the default action bar background -->
<item name="colorPrimary">@color/primaryDark</item>
<!-- colorPrimaryDark is used for the status bar -->
<item name="colorPrimaryDark">@color/primaryDark</item>
</style>
答案 1 :(得分:4)
这是这里回答的问题的具体情况:Force Flutter to redraw all widgets
查看该问题中提到的Stocks样本,特别注意: https://github.com/flutter/flutter/blob/master/examples/stocks/lib/main.dart https://github.com/flutter/flutter/blob/master/examples/stocks/lib/stock_settings.dart
请注意以下事项:
_configuration
指定的,由configurationUpdater
configurationUpdater
会传递给需要它的应用的子女答案 2 :(得分:2)
您也可以使用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);
}),
])));
}
}
答案 3 :(得分:0)
在尝试使用BLoC模式进行各种尝试之后,我不知道这是否是一种好方法,但它似乎没有问题:
应用主题模型:
class MyTheme {
Brightness brightness;
Color backgroundColor;
Color scaffoldBackgroundColor;
Color primaryColor;
Brightness primaryColorBrightness;
Color accentColor;
MyTheme({
this.brightness,
this.backgroundColor,
this.scaffoldBackgroundColor,
this.primaryColor,
this.primaryColorBrightness,
this.accentColor
});
}
class AppTheme {
String name;
MyTheme theme;
AppTheme(this.name, this.theme);
}
List<AppTheme> myThemes = [
AppTheme(
'Default',
MyTheme(
brightness: Brightness.light,
backgroundColor: Colors.blue[50],
scaffoldBackgroundColor: Colors.blue[50],
primaryColor: Colors.blue,
primaryColorBrightness: Brightness.dark,
accentColor: Colors.blue[50],
)),
AppTheme(
'Teal',
MyTheme(
brightness: Brightness.light,
backgroundColor: Colors.teal[50],
scaffoldBackgroundColor: Colors.teal[50],
primaryColor: Colors.teal[600],
primaryColorBrightness: Brightness.dark,
accentColor: Colors.teal[50],
),
),
];
应用BLoC类。在这里,我使用了RxDart的BehaviorSubject。
class AppBloc {
final _theme = BehaviorSubject<AppTheme>();
Function(AppTheme) get inTheme => _theme.sink.add;
Stream<AppTheme> get outTheme => _theme.stream;
AppBloc() {
print('-------APP BLOC INIT--------');
// Send to stream the initial theme
inTheme(myThemes[0]);
}
dispose() {
print('---------APP BLOC DISPOSE-----------');
_theme.close();
}
}
在应用程序的设置页面中,我使用_theme流设置带有主题列表的下拉菜单的当前主题。使用onChanged处理程序,当用户单击主题时,它将发送到流:
StreamBuilder(
stream: widget.bloc.outTheme,
builder: (context, AsyncSnapshot<AppTheme> snapshot) {
return snapshot.hasData
? DropdownButton<AppTheme>(
hint: Text("Status"),
value: snapshot.data,
items: myThemes.map((AppTheme appTheme) {
return DropdownMenuItem<AppTheme>(
value: appTheme,
child: Text(appTheme.name),
);
}).toList(),
onChanged: widget.bloc.inTheme,
)
: Container();
}),
最后在主页中,使用StreamBuilder,我使用_theme流来设置所选的ThemeData:
StreamBuilder(
stream: _bloc.outTheme,
builder: (context, AsyncSnapshot<AppTheme> snapshot) {
return MaterialApp(
theme: snapshot.hasData ? _buildThemeData(snapshot.data) : ThemeData(),
home: HomePage());
}),
_BuildThemeData方法从主题模型获取ThemeData:
_buildThemeData(AppTheme appTheme) {
return ThemeData(
brightness: appTheme.theme.brightness,
backgroundColor: appTheme.theme.backgroundColor,
scaffoldBackgroundColor: appTheme.theme.scaffoldBackgroundColor,
primaryColor: appTheme.theme.primaryColor,
primaryColorBrightness: appTheme.theme.primaryColorBrightness,
accentColor: appTheme.theme.accentColor
);
}
我希望这对您有用。
答案 4 :(得分:0)
您可以结合使用ChangeNotifierProvider后继包和Consumer软件包中的provider / ChangeNotifier。
/// Theme manager
class ThemeManager extends ChangeNotifier {
ThemeManager([ThemeData initialTheme]) : _themeData = initialTheme ?? lightTheme;
ThemeData _themeData;
/// Returns the current theme
ThemeData get themeData => _themeData;
/// Sets the current theme
set themeData(ThemeData value) {
_themeData = value;
notifyListeners();
}
/// Dark mode theme
static ThemeData lightTheme = ThemeData();
/// Light mode theme
static ThemeData darkTheme = ThemeData();
}
/// Application
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => ThemeManager(),
child: Consumer<ThemeManager>(
builder: (_, manager, __) {
return MaterialApp(
title: 'Flutter Demo',
theme: manager.themeData,
home: HomePage(),
);
},
),
);
}
}
// Somewhere in GUI
FlatButton(
child: Text(isDarkMode ? 'Light Mode' : 'Dark Mode'),
onPressed() {
Provider.of<ThemeManager>(context, listen:false)
.themeData = isDarkMode ? ThemeManager.darkTheme : ThemeManager.lightTheme;
},
),
答案 5 :(得分:0)
您可以只使用MaterialApp小部件的themeMode参数,如下所示:
theme: yourLightTheme,
darkTheme: yourDarkTheme,
themeMode: settings.useSystemTheme
? ThemeMode.system
: settings.useDarkTheme ? ThemeMode.dark : ThemeMode.light,
home: Home(),
它很干净,可以在Android和iOS上使用。您只需要在您的状态中拥有这两个布尔值,useSystemTheme和useDarkTheme即可。
答案 6 :(得分:0)
在遵循@SuperDeclarative答案后执行此操作
在main.dart上制作材质应用程序
MaterialApp(
builder: (context, child) {
return new AppTheme(
child: YourAppWidget())
})
在您要更改主题的其他任何班级
setState(() {
ThemeChanger.of(context).appTheme = appThemeLight;
});
我的提示:
答案 7 :(得分:0)
您可以使用 Provider 来改变这一点。
1- 您必须在 pubspec.yaml 文件中添加 Provider
dependencies:
flutter:
sdk: flutter
provider: ^4.3.2+2
2- 从 ChangeNotifier 扩展一个类以更改主题并保持当前主题
import 'package:flutter/material.dart';
var darkTheme = ThemeData.dark();
var lightTheme= ThemeData.light();
enum ThemeType { Light, Dark }
class ThemeModel extends ChangeNotifier {
ThemeData currentTheme = darkTheme;
ThemeType _themeType = ThemeType.Dark;
toggleTheme() {
if (_themeType == ThemeType.Dark) {
currentTheme = lightTheme;
_themeType = ThemeType.Light;
return notifyListeners();
}
if (_themeType == ThemeType.Light) {
currentTheme = darkTheme;
_themeType = ThemeType.Dark;
return notifyListeners();
}
}
}
3- 添加 ChangeNotifierProvider 作为 runApp 的子项
void main() {
runApp(
ChangeNotifierProvider<ThemeModel>(
create: (context) => ThemeModel(),
child: MyApp(),
),
);
}
4- 在启动应用时获取当前主题
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'MyApp',
initialRoute: '/',
theme: Provider.of<ThemeModel>(context).currentTheme,
routes: {
'/': (context) => FirstPage(),
'/SecondPage': (context) => SecondPage(),
},
);
}
5- 在其他班级中切换您的主题
onTap: () {Provider.of<ThemeModel>(context,listen: false).toggleTheme();},
答案 8 :(得分:0)
此复制/粘贴示例在运行时使用 StatefulWidget
在浅色/深色主题之间更改应用主题。
(这是从 Android Studio 自动生成的 Flutter 示例应用,已修改。)
MyApp
从 StatelessWidget
更改为 StatefulWidget
(MyStatefulApp
)of(context)
方法添加到 MyStatefulApp
(从后代中找到我们的 State
对象)changeTheme()
方法添加到我们的 State
对象_incrementCounter
的 FAB 按钮调用将 setState
重建委托给 MyStatefulApp.of(context).changeTheme()
。无需在此处致电 setState
。import 'package:flutter/material.dart';
void main() {
runApp(MyStatefulApp());
}
/// Change MyApp from StatelessWidget to StatefulWidget
class MyStatefulApp extends StatefulWidget {
@override
_MyStatefulAppState createState() => _MyStatefulAppState();
/// Add an InheritedWidget-style static accessor so we can
/// find our State object from any descendant & call changeTheme
/// from anywhere.
static _MyStatefulAppState of(BuildContext context) =>
context.findAncestorStateOfType<_MyStatefulAppState>();
}
class _MyStatefulAppState extends State<MyStatefulApp> {
// define a state field for theme
ThemeData _theme = ThemeData();
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App Themes',
theme: _theme, // use theme field here
home: MyHomePage(title: 'Change App Theme at Runtime'),
);
}
/// Call changeTheme to rebuild app with a new theme
void changeTheme({ThemeData theme}) {
setState(() {
_theme = theme;
});
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
_counter++;
// alternate light / dark themes with each FAB press, for illustration
ThemeData _theme = _counter.isOdd ? ThemeData.dark() : ThemeData();
/// Find the State object and change the theme, can be done anywhere with
/// a context
MyStatefulApp.of(context).changeTheme(theme: _theme);
// we're rebuilding with changeTheme, so don't duplicate setState call
/*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(
'You switched themes this many times, happy yet?:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
darkTheme
提供 themeMode
和 MaterialApp
参数,并在 themeMode
和 {{ 之间更改 ThemeMode.light
1}} 而不是每次都更改 ThemeMode.dark
参数。从 iOS 13 / Android 10 开始,使用 theme
将支持设备范围的暗模式。上面的示例是按原样完成的,目的是尽可能简单/直接地回答问题,但对于此特定用例并不理想。