Quelle est la meilleure façon de changer dynamiquement le thème d'une application Flutter? Par exemple, si l'utilisateur change la couleur en rouge, je veux que le thème soit instantanément changé en rouge. Je ne trouve rien de très utile en ligne, à l'exception d'un gars qui a dit utiliser le modèle BLOC, que je ne connais pas. J'aimerais entendre votre opinion sur la question. Merci!
Ma structure de code actuelle:
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
}
3 Réponses :
Vous pouvez utiliser InhertedWidget si vous le souhaitez (au lieu de BLOC) - Fondamentalement, il est utilisé pour accéder au widget parent n'importe où dans l'arborescence.
Donc, ce que vous devez faire est
InheritedWidget , quelque part en haut de l'arborescence [d'où vous voulez que l'effet du thème se produise] Thème Voici un peu de code:
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.inheritFromWidgetOfExactType(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(),
);
}
}
J'ai enveloppé ThemeSwitcherWidget autour de MaterialApp pour que l'effet se fasse dans toute l'application (même lorsque vous envoyez une nouvelle route avec Navigator) .
Utilisez ThemeSwitcher.of (context) .switchTheme (themeData) n'importe où sous ThemeSwithcerWidget pour changer le thème.
Dans le cas de la question, il devrait appeler ThemeSwitcher.of (context) .switchTheme (Theme.of (context) .copyWith (primaryColor: Colors.red)) pour changer la couleur primaire en rouge à travers l'application, par exemple. sur un bouton, cliquez
j'espère que c'est ce que vous recherchez.
Merci, mais je ne suis pas sûr de savoir comment mettre en œuvre c'est mon application actuelle. J'ai mis à jour ma question avec ma structure de code actuelle.
J'en ai ajouté. J'espère que cela pourrait aider.
Obtenir des erreurs. "J'ai essayé d'appeler: switchTheme (Instance de 'ThemeData')", "NoSuchMethodError: La méthode 'switchTheme' a été appelée sur null."
Je ne sais pas non plus quoi transmettre en tant que `` données ''
pour le premier commentaire - cela signifie que vous appelez ThemeSwitcher.of (context) .switchTheme (..) au-dessus de ThemeSwitcherWidget dans l'arborescence. si vous utilisez mon exemple d'extrait de code. vous ne devriez appeler cela que sous le widget CheckAuth. Si vous le faites déjà, vous passez un mauvais contexte à la méthode ThemeSwitcher.of () [Je suppose que vous pourriez passer le contexte de la méthode de construction MyApp, vous devriez passer le contexte du widget CheckAuth]
pour le commentaire seconf - si vous utilisez ThemeSwitcherWidget , il transmettra des données (c'est-à-dire l'état du widget à ThemeSwitcher), vous n'avez pas à vous en soucier.
J'ai rencontré le problème des widgets enfants qui ne portent pas le thème personnalisé. Par exemple, mon thème de page principale a l'air bien mais si je lance une autre page Navigator.push (context, MaterialPageRoute (builder: (context) => SettingsScreen ()),); La nouvelle page vient de le thème par défaut. Je suppose qu'il existe une solution simple.
Code édité. correction: encapsulation de ThemeSwitcherWidget autour de MaterialApp à la place à l'intérieur, pour que le thème prenne effet après que la nouvelle route ait été poussée dans Navigator. Modification de l'API ThemeSwithcerWidget - ajout de initialTheme qu'il est obligatoire de fournir
pour que cela prenne effet après la fermeture et la réouverture de l'application, vous devrez utiliser le plugin shared_prefernce pour stocker la dernière configuration de thème et la charger lorsque runApp est appelé.
J'utilise votre extrait de code, mais il est réglé et arrive à la première page d'accueil après avoir changé de thème
@BhargavSejpal Je pense que c'est parce que lorsque vous changez de thème, l'enfant est en cours de reconstruction. Difficile de trouver quel est le problème.
Voici comment implémenter le changement de thème dynamique dans votre application:
1.Vous devez changer votre MyApp en widget Stateful pour permettre à la classe de se reconstruire à nouveau lorsque la couleur change:
App.setTheme(context, Colors.blue);
2. La méthode statique setTheme sera celle responsable du changement de couleur:
class _AppState extends State<App> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: Constants.appName,
theme: themeData,
home: CheckAuth(), //CheckAuth returns MyHomePage usually
);
}
}
3.Lorsque vous souhaitez modifier la couleur du thème de n'importe où dans votre code, appelez cette méthode:
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;
});
}
}
Utilisation du package provider :
theme_changer.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);
})
),
),
);
}
}
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(),
);
}
premier_écran .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();
}
}
Quels aspects de l'application souhaitez-vous inclure dans le thème? De plus, cela serait d'une grande aide pour les autres si vous pouviez ajouter un petit morceau de code que vous avez.
Il suffit de changer la couleur et la luminosité (sombre / clair). Vous n'avez pas beaucoup de code spécifique à offrir, juste une application super basique.
Vous devrez essentiellement transformer une telle configuration (couleur bg, couleur de police, taille, etc.) en variables et les mettre à jour sur certains événements. Difficile de dire quoi et comment pourriez-vous réaliser en fonction des informations disponibles, mais j'espère que vous comprendrez.