How to change theme to dark on click on IconButton? - flutter

In my application, in the appBar, there is a button that should change the theme to dark. I need to create functionality Provider. How can this be implemented? I just need to change the Scaffold color to black and the text color to white.
My main.dart:
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
textTheme: const TextTheme(
headline1: TextStyle(fontSize: 50.0, fontWeight: FontWeight.bold),
headline5: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
subtitle2: TextStyle(fontSize: 10.0, color: Colors.black),
bodyText1: TextStyle(fontSize: 14.0, color: Colors.black),
),
),
home: const HomeScreen(),
);
}
My switch button:
appBar: AppBar(
title: const Text('Flutter theme config'),
centerTitle: true,
actions: [
IconButton(
onPressed: () {
},
icon: const Icon(Icons.dark_mode),
)
],
),
Theme provider:
class ThemeProvider extends ChangeNotifier {
}

You can try something like this :
First we provide our Provider globally for the whole application, and then in the attribute theme: we listen for the change.
** main **
void main() async {
runApp(
MultiProvider( // create the provider
providers: [
ChangeNotifierProvider(
create: (_) => ThemeProvider(),
)
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Material App',
initialRoute: HomeScreen.routerName,
routes: {
},
theme: Provider.of<ThemeProvider>(context).currentTheme, // listen to the current theme
);
}
}
In the provider we will only have two functions, one to switch to LightMode and the other to DarkMode, then we add it to the currentTheme variable which is the one that listens in the main
** ThemeProvider **
class ThemeProvider extends ChangeNotifier {
ThemeData? currentTheme;
setLightMode() {
currentTheme = ThemeData(
brightness: Brightness.light, // LightMode
scaffoldBackgroundColor: Colors.red,
[...] // more attributes
);
notifyListeners();
}
setDarkmode() {
currentTheme = ThemeData(
brightness: Brightness.dark, // DarkMode
scaffoldBackgroundColor: Colors.green,
[...] // more attributes
);
notifyListeners();
}
}
Finally we create a StatefulWidget to change the isDarkMode variable to call the provider
** Button Home **
class _HomeScreenState extends State<SettingsScreen> {
bool isDarkmode = false; // new variable
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Settings"),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: IconButton(
onPressed: () {
final themeProvider =
Provider.of<ThemeProvider>(context, listen: false); // get the provider, listen false is necessary cause is in a function
setState(() {
isDarkmode = !isDarkmode;
}); // change the variable
isDarkmode // call the functions
? themeProvider.setDarkmode()
: themeProvider.setLightMode();
},
icon: const Icon(Icons.dark_mode),
),
),
);
}
}

Related

Flutter Theme Dark/Lights Theme toggle doesn't work

there is a dark/light theme toggle function in this snap of code. There is no error when I compile, debuging this code. But It doesn't work.
For example, I never define scaffoldbackgroundColor(0xfffafafa) and What I define color in dark, light theme is Color(0xFFE7626C) and Color(0xFF232B55). But ios simulator only show me backgroundcolor(0xfffafafa).
When I click toggle button, the screen never changes. What's wrong with my code...
import 'package:flutter/material.dart';
import 'package:pomodoro_app/widget/sidebar_widget.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
static final ValueNotifier<ThemeMode> themeNotifier =
ValueNotifier(ThemeMode.dark);
#override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeMode>(
valueListenable: themeNotifier,
builder: (_, ThemeMode currentMode, __) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.light,
scaffoldBackgroundColor: const Color(0xFFE7626C),
textTheme: const TextTheme(
displayLarge: TextStyle(
color: Color(0xFF232B55),
),
displaySmall: TextStyle(
color: Color(0xFF232B55),
),
),
cardColor: const Color(0xFFF4EDDB),
),
darkTheme: ThemeData(
brightness: Brightness.dark,
scaffoldBackgroundColor: const Color(
0xFF232B55,
),
cardColor: const Color(0xFFF4EDDB),
textTheme: const TextTheme(
displayLarge: TextStyle(
color: Color(0xFFE7626C),
),
displaySmall: TextStyle(
color: Colors.white,
),
),
),
themeMode: currentMode,
home: Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
elevation: 0,
leading: Builder(
builder: (context) => IconButton(
onPressed: () => Scaffold.of(context).openDrawer(),
icon: const Icon(
Icons.menu_rounded,
color: Color(0xFFF4EDDB),
),
),
),
actions: [
IconButton(
icon: Icon(themeNotifier.value == ThemeMode.light
? Icons.dark_mode
: Icons.light_mode),
onPressed: () {
setState(
() {
themeNotifier.value =
themeNotifier.value == ThemeMode.light
? ThemeMode.dark
: ThemeMode.light;
},
);
},
),
],
),
drawer: const SideBar(),
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
"Hello",
)
],
),
),
);
},
);
}
}
That's because the context you are using with
Theme.of(context);
comes from the build method of _HomeScreenState.
So this context is above the MaterialApp which has the Theme.
You have to use a context that is bellow the MaterialApp.
One way to do it is to add a Builder in between your MaterialApp and Scaffold:
return MaterialApp(
title: 'Flutter Demo',
theme: // ...
darkTheme: // ...
themeMode: currentMode,
home: Builder( // <- Insert a builder here.
builder: (context) { // <- Now you'll be using this `context` which is below the `MaterialApp` (and your themes) in the widget tree, so you'll be able to use them.
return Scaffold(
// ...
);
},
);

FLUTTER - How to properly upgrade this code to work with RiverPod 1.0+?

I added the libraries to pubspec.yaml. In the current project I am not using hooks, but used the package because I want to use this to build a skeleton starter project.
pubspec.yaml:
flutter_hooks: ^0.18.2+1
hooks_riverpod: ^1.0.3
Then I modified the code to the latest RiverPod syntax. This involved changing things to Consumer widgets and using the ref parameter. The app now runs but just is not switching from dark mode to light mode and vice versa!
main.dart:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class AppThemeState extends StateNotifier<bool> {
AppThemeState(): super(false);
void setLightTheme() => state = false;
void setDarkTheme() => state = true;
}
// Theme
final appThemeStateNotifier = StateNotifierProvider((ref) => AppThemeState());
void main() {
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends HookConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
final darkModeEnabled = ref.watch(appThemeStateNotifier);
return MaterialApp(
title: 'Flutter Demo',
theme: AppTheme.lightTheme,
darkTheme: AppTheme.darkTheme,
themeMode: darkModeEnabled != null ? ThemeMode.dark : ThemeMode.light,
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 2.0,
title: Text("Flutter Theme Riverpod Demo"),
),
body: Column(
children: [
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Light Mode"),
DarkModeSwitch(),
Text("Dark Mode"),
],
),
),
],
),
);
}
}
class DarkModeSwitch extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
final darkModeEnabled = ref.read(appThemeStateNotifier) == true;
return //Text('$darkModeEnabled');
Switch(
value: darkModeEnabled,
onChanged: (enabled) {
if (enabled) {
ref.read(appThemeStateNotifier.notifier).setDarkTheme();
} else {
ref.read(appThemeStateNotifier.notifier).setLightTheme();
}
},
);
}
}
class AppTheme {
// Private Constructor
AppTheme._();
static final lightTheme = ThemeData(
scaffoldBackgroundColor: Colors.white,
appBarTheme: AppBarTheme(
color: Colors.teal,
iconTheme: IconThemeData(
color: Colors.white,
),
),
textTheme: TextTheme(
bodyText2: TextStyle(
color: Colors.black,
),
),
);
static final darkTheme = ThemeData(
scaffoldBackgroundColor: Colors.black,
appBarTheme: AppBarTheme(
color: Colors.black,
iconTheme: IconThemeData(
color: Colors.white,
),
),
textTheme: TextTheme(
bodyText2: TextStyle(
color: Colors.white,
),
),
);
}
The problem is at this line:
themeMode: darkModeEnabled != null ? ThemeMode.dark : ThemeMode.light,
darkModeEnabled can't be null.
Use this instead:
themeMode: darkModeEnabled ? ThemeMode.dark : ThemeMode.light,

Save a theme (Flutter/Dart)

Good morning,
Here I have two buttons that change the theme of my application (light and dark). When I reload my app the theme is not the one I selected last. I would like the application to back up the last theme used locally. You may need to save just a number that indicates which theme used the last one. . . But I don’t know at all how to do this?
Here’s the code: main.dart
import 'package:flutter/material.dart';
import 'package:animated_splash_screen/animated_splash_screen.dart';
import 'package:watch/nav.dart';
import 'package:page_transition/page_transition.dart';
import 'package:watch/blocs/theme.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<ThemeChanger>(
builder: (_) => ThemeChanger(ThemeData.dark()),
child: MaterialAppWithTheme(),
);
}
}
class MaterialAppWithTheme extends StatelessWidget {
#override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeChanger>(context);
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: theme.getTheme(),
home: AnimatedSplashScreen(
duration: 3000,
splash: "",
splashTransition: SplashTransition.slideTransition,
pageTransitionType: PageTransitionType.downToUp,
nextScreen: Nav(),
),
);
}
}
settings.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:watch/blocs/theme.dart';
import 'package:watch/constants.dart';
class Parametres extends StatelessWidget {
#override
Widget build(BuildContext context) {
ThemeChanger _themeChanger = Provider.of<ThemeChanger>(context);
return Scaffold(
appBar: AppBar(
title: Text('Paramètres', style: kAppBarStyle,),
elevation: 0,
automaticallyImplyLeading: false,
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(
Icons.arrow_back,
),
),
),
body: Container(
child: Column(
children: <Widget>[
FlatButton(
onPressed: () => _themeChanger.setTheme(
ThemeData(
bottomNavigationBarTheme: bNavBar,
scaffoldBackgroundColor: kBlackMedium,
brightness: Brightness.dark,
iconTheme: bIcons,
)),
child: Text('Dark Theme')),
FlatButton(
onPressed: () => _themeChanger.setTheme(
ThemeData(
bottomNavigationBarTheme: lNavBar,
scaffoldBackgroundColor: Colors.white,
brightness: Brightness.light,
iconTheme: lIcons,
primaryColor: kWhite,
)),
child: Text('Light Theme')),
],
),
),
);
}
}
Thank you
Shared preference is best option for it. Since I don't know about your ThemeChanger class I add here my theme class first:
class MyThemeModel extends ChangeNotifier{
ThemeData _themedata;
MyThemeModel(bool isActive){
if(isActive == null){
getThemeData;
}
else{
if(isActive){
_themedata = sleepTheme;
}
else{
_themedata = morningTheme;
}
}
}
ThemeData get getThemeData => _themedata;
void setThemeData(ThemeData data){
_themedata = data;
notifyListeners();
}
}
In main.dart
void main() async{
var isSleepActive;
if(SharedPrefHelper.prefInstance.checkContains(SharedPrefKeys.ISMORNING)){
isSleepActive = SharedPrefHelper.prefInstance.getBool(SharedPrefKeys.ISMORNING);
}
else{
isSleepActive = false;
}
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(
builder: (context) => MyThemeModel(isSleepActive),
)
],
child: MyApp(),
)
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: Provider.of<MyThemeModel>(context).getThemeData,
title: 'Theme App',
home: AnimatedSplashScreen(
duration: 3000,
splash: "",
splashTransition: SplashTransition.slideTransition,
pageTransitionType: PageTransitionType.downToUp,
nextScreen: Nav(),
),
debugShowCheckedModeBanner: false,
);
}
In order to change theme with flat button:
FlatButton(
onPressed: () => myThemeModel.setThemeData(
ThemeData(
bottomNavigationBarTheme: lNavBar,
scaffoldBackgroundColor: Colors.white,
brightness: Brightness.light,
iconTheme: lIcons,
primaryColor: kWhite,
)),
child: Text('Light Theme')),
Use the Shared Preference package and there you can store simple values as key pair values.Load that data in the init of the initial screen so that you can display the screen according to the theme
You should use local memory to save theme.
You can use shared preference or hive db or sqflite or other database system.
About changing theme you can use Cubit,Bloc,Provider or etc or even if ValueNotifier.
However you should wrap your MaterialApp or CupertinoApp with "your state management widget"
And add some Logic
OR you can use some library
Library to change theme

Flutter How to force Dark mode on specific screens

I'm building a news app using flutter, the app has 2 theme modes already dark and light debends on phone settings and works really great, but I need on some screens on my app to be dark whatever such as videos section or video page ...etc
I googled this and all the results about the normal theming which I did already.
I don't think there's any code I can put here to help, but if there please let me know!
You can override the current theme at any time simply by placing the desired widget in a Theme class.
I don't know if you are using Scaffold or not, but let's say you are then all you would need to do is:
// declare theme data if you don't have it already
final ThemeData specialThemeData = ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.yellow[700],
// and so on...
);
#override
Widget build(BuildContext context) {
// this the point of interest, return a Theme with desired Theme Data
return Theme(
data: specialThemeData,
child: Scaffold(
//...
It doesn't have to be Scaffold, it will work on any widegt.
Here is a fully functional example you can try out yourself:
import 'package:flutter/material.dart';
final ThemeData specialThemeData = ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.yellow[700],
accentColor: Colors.orange[500],
textTheme: TextTheme(
headline1: TextStyle(fontSize: 48.0, fontWeight: FontWeight.bold),
headline6: TextStyle(fontSize: 24.0, fontStyle: FontStyle.italic),
bodyText2: TextStyle(fontSize: 18.0),
),
);
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Home Page default 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> {
void _goToSpecialPage() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MySpecialPage()
)
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Your homepage, using default theme.',),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _goToSpecialPage,
tooltip: 'Go to special page',
child: Icon(Icons.navigate_next),
),
);
}
}
class MySpecialPage extends StatefulWidget {
MySpecialPage({Key key}) : super(key: key);
#override
_MySpecialPageState createState() => _MySpecialPageState();
}
class _MySpecialPageState extends State<MySpecialPage> {
void _backToHomePage(){
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
// this the point of interest, return a Theme with desired Theme Data
return Theme(
data: specialThemeData,
child: Scaffold(
appBar: AppBar(
title: Text('Special theme page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Your special page that uses a different theme.',),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _backToHomePage,
tooltip: 'Go back to home page',
child: Icon(Icons.navigate_before),
),
),
);
}
}

Declaring one or more custom Themes

I'm trying to create a custom theme inside a flutter project.
I've created a separate file (mycolors.dart) where i defined some colors (const myPrimaryColor = const Color(0xFFFF3900); etc etc)
Then, in main.dart i'm referring to these colors and a custom font but inside the Widget build...
How can I isolate the theme data (colors and font/text styles), let's say "separately", and to refer to it inside the class?
Can I also define 2 different themes and use them later in the project?
Many thanks.
import 'package:flutter/material.dart';
import 'package:my_repository/mycolors.dart';
import 'package:flutter_statusbarcolor/flutter_statusbarcolor.dart';
void main() {
runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
FlutterStatusbarcolor.setStatusBarColor(myPrimaryColor);
return MaterialApp(
theme: ThemeData(
fontFamily: 'Raleway',
primaryColor: myPrimaryColor,
accentColor: myAccentColor,
scaffoldBackgroundColor: myBackgroundColor,
cardColor: mySurfaceColor,
textSelectionColor: myPrimaryColor,
errorColor: myErrorColor,
),
home: Scaffold( ....
You can define your themes in a class and then call ThemeName().theme.
here is how I have a theme file in my app:
class MuskTheme {
...
ThemeData get theme => ThemeData(
brightness: Brightness.dark,
primarySwatch: musk,
accentColor: accentColor,
fontFamily: fontFamily,
backgroundColor: musk,
canvasColor:canvasColor,
textTheme: _textTheme,
iconTheme: _iconTheme,
cardColor: Color(0xff313A49),
appBarTheme: AppBarTheme(color: canvasColor,elevation: 0),
dialogBackgroundColor: canvasColor,
snackBarTheme: SnackBarThemeData(
backgroundColor: musk[800],
actionTextColor: accentColor,
),
);
...
}
for changing your theme during runtime you need to rebuild the MaterialApp widget by implementing a stateful widget that is higher than MaterialApp and can rebuild it upon request.
example implementation:
class ThemeChanger extends StatefulWidget {
final ThemeData initialTheme;
final MaterialApp Function(BuildContext context, ThemeData theme)
materialAppBuilder;
const ThemeChanger({Key key, this.initialTheme, this.materialAppBuilder})
: super(key: key);
#override
_ThemeChangerState createState() => _ThemeChangerState();
static void setTheme(BuildContext context, ThemeData theme) {
var state = context.ancestorStateOfType(TypeMatcher<_ThemeChangerState>())
as _ThemeChangerState;
state.setState(() {
state.theme = theme;
});
}
}
class _ThemeChangerState extends State<ThemeChanger> {
ThemeData theme;
#override
void initState() {
super.initState();
theme = widget.initialTheme;
}
#override
Widget build(BuildContext context) {
return widget.materialAppBuilder(context, theme);
}
}
then you'll need to build your MaterialApp with it:
class ThemeChangingApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ThemeChanger(
initialTheme: ThemeData(
primarySwatch: Colors.blue,
),
materialAppBuilder: (context, theme) {
return MaterialApp(
theme: theme,
home: StartingPage(),
);
},
);
}
}
and in your app you can change the theme like this:
class StartingPage extends StatefulWidget {
#override
_StartingPageState createState() => _StartingPageState();
}
class _StartingPageState extends State<StartingPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: Center(
child: FlatButton(
child: Text('change theme to orange'),
onPressed: () {
ThemeChanger.setTheme(
context,
ThemeData(
primarySwatch: Colors.orange,
));
},
),
),
),
);
}
}
this package does a similar thing.
You can build a scaffold with a different theme just by warpping it with a Theme widget:
class StartingPage extends StatefulWidget {
#override
_StartingPageState createState() => _StartingPageState();
}
class _StartingPageState extends State<StartingPage> {
#override
Widget build(BuildContext context) {
return Theme(
data: ThemeData.dark(),
child: Scaffold(
appBar: AppBar(),
body: Container(
child: Center(
child: Text('test'),
),
),
),
);
}
}
final ThemeData customTheme = _buildcustomTheme();
ThemeData _buildcustomTheme() {
return customThemeFoundation;
}
ThemeData customThemeFoundation =ThemeData(
brightness: Brightness.dark,
primaryColor: Color.fromRGBO(40, 204, 86, 1),
accentColor: Colors.cyan[600],
fontFamily: 'Georgia',
textTheme: TextTheme(
headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
headline6: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'),
),
and in main.dart just call it by
import 'theme.dart';
and just relpace theme:{.....} with theme: customTheme,