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

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,

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(
// ...
);
},
);

How to change theme to dark on click on IconButton?

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),
),
),
);
}
}

Why is my switch widget not sliding? I can't seem to change themes

I can't seem to figure out what is preventing the switch from changing states. I know if I set isDark to true, I get my dark theme, and if I set isDark to false I get my light theme. It might be possible that flutter is interpreting isDark as constantly being false. However, I am unsure as to how to fix that if that were the case. Regardless, something is stopping the switch and I can't figure it out. Any help is appreciated thank you.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: ChangeThemeButton(),
theme: _theme,
// theme: MyThemes.lightTheme,
// darkTheme: MyThemes.darkTheme,
// themeMode: ThemeMode.system,
routes: {
'homepage': (context) => HomePage(),
'sign-in': (context) => SignIn(),
'register': (context) => RegisterPage(),
},
);
}
}
class ChangeThemeButton extends StatefulWidget {
#override
_ChangeThemeButtonState createState() => _ChangeThemeButtonState();
}
class _ChangeThemeButtonState extends State<ChangeThemeButton> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Switch(
value: isDark,
onChanged: (val) => {
setState((){
val = isDark;
themeProvider(val);
})
},
),
),
);
}
}
var _theme = (isDark == false) ? MyThemes.lightTheme : MyThemes.darkTheme;
bool val;
bool isDark = false;
bool themeProvider (bool val) {
switch (val) {
case false:
return isDark == true;
break;
case true:
return isDark == false;
break;
}
return null;
}
class MyThemes {
static final lightTheme = ThemeData(
colorScheme: ColorScheme.light(),
iconTheme: IconThemeData(color: Colors.black),
bottomNavigationBarTheme: BottomNavigationBarThemeData(
unselectedItemColor: Colors.white,
selectedItemColor: Color(0xff9D4C8F),
showUnselectedLabels: true,
selectedIconTheme: IconThemeData(
color: Color(0xff9D4C8F),
),
unselectedIconTheme: IconThemeData(
color: Colors.white,
),
),
primaryColor: Colors.deepPurpleAccent[100],
primaryColorDark: Colors.black54,
secondaryHeaderColor: Colors.deepPurpleAccent[50],
scaffoldBackgroundColor: Colors.white,
cardColor: Colors.deepPurpleAccent,
cardTheme: CardTheme(elevation: 5));
static final darkTheme = ThemeData(
colorScheme: ColorScheme.dark(),
iconTheme: IconThemeData(color: Colors.white),
bottomNavigationBarTheme: BottomNavigationBarThemeData(
unselectedItemColor: Colors.white38,
selectedItemColor: Color(0xff9D4C8F),
showUnselectedLabels: true,
selectedIconTheme: IconThemeData(
color: Color(0xff9D4C8F),
),
unselectedIconTheme: IconThemeData(
color: Colors.white,
),
),
primaryColor: Colors.black,
primaryColorDark: Colors.grey,
secondaryHeaderColor: Colors.deepPurpleAccent[50],
scaffoldBackgroundColor: Colors.white38,
cardColor: Colors.deepPurpleAccent,
cardTheme: CardTheme(elevation: 5)
);
}
change val = isDark; to isDark = val, or you could remove that line and change it into the provider, because now you say that even if val changes, you set it right below to isDark, so nothing changes.

how to change theme data in flutter?

class _NavigationBarState extends State<NavigationBar> {
int _currentIndex = 0;
final List<Widget> tabs = [
CustomerAccountPage(),
HomePage(),
AppInforamtionPage(),
CategoriesPage(),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light().copyWith(
primaryColor: Colors.amber,
accentColor: Colors.black
),
debugShowCheckedModeBanner: false,
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
showUnselectedLabels: true,
unselectedItemColor: Theme.of(context).primaryColor,
selectedItemColor: Theme.of(context).accentColor,
currentIndex: _currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("account"),
),
BottomNavigationBarItem(
icon: Icon(Icons.play_for_work),
title: Text("shop"),
),
BottomNavigationBarItem(
icon: Icon(Icons.all_out),
title: Text("more info "),
),
BottomNavigationBarItem(
icon: Icon(Icons.all_out),
title: Text(" categories"),
),
],
onTap: (index) {
setState(() {
_currentIndex = index;
});
}),
),
centerTitle: true,
backgroundColor: Colors.white,
),
body: tabs[_currentIndex],
floatingActionButton: FloatingActionButton(onPressed: null, backgroundColor: Theme.of(context).primaryColor,),
),
);
}
}
I'm using flutter , and trying to set the theme data, but it doesn't work.
I tried to change the theme in this way, but I can't see the result in my app, I don't know what is the problem, can anyone help me!
stackoverflow want from me to explain more, but I haven't anything else to explain, thanks in advance!
The context used in Theme.of(context).primaryColor is not the right context. You need to put the MaterialApp in another widget wrapping the current widget, e.g.,
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light()
.copyWith(primaryColor: Colors.amber, accentColor: Colors.black),
home: NavigationBar(),
);
}
}
And the build method of the _NavigationBarState:
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
showUnselectedLabels: true,
unselectedItemColor: Theme.of(context).primaryColor,
selectedItemColor: Theme.of(context).accentColor,
...
After trying very hard to understand Material Design, I found the following solution which is easy and clean. Here is my complete code.
color_scheme.dart
import 'package:flutter/material.dart';
const lightColorScheme = ColorScheme(
brightness: Brightness.light,
primary: Color(0xFF687DAF),
onPrimary: Color(0xFFFFFFFF),
secondary: Color(0xFFf37b67),
onSecondary: Color(0xFFFFFFFF),
error: Color(0xFFBA1A1A),
onError: Color(0xFFFFFFFF),
background: Color(0xFFFEFFFF),
onBackground: Color(0xFF3b3b3b),
surface: Color(0xFFFEFFFF),
onSurface: Color(0xFF3b3b3b),
);
const darkColorScheme = ColorScheme(
brightness: Brightness.dark,
primary: Color(0xFFADC6FF),
onPrimary: Color(0xFF002E69),
secondary: Color(0xFFBBC6E4),
onSecondary: Color(0xFF253048),
error: Color(0xFFFFB4AB),
onError: Color(0xFF690005),
background: Color(0xFF1B1B1F),
onBackground: Color(0xFFE3E2E6),
surface: Color(0xFF1B1B1F),
onSurface: Color(0xFFE3E2E6),
);
theme.dart
import 'package:first_project/shared/color_schemes.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
final ThemeData lightThemeDataCustom = _buildLightTheme();
ThemeData _buildLightTheme() {
final ThemeData base = ThemeData.light();
return base.copyWith(
colorScheme: lightColorScheme,
primaryColor: lightColorScheme.primary,
scaffoldBackgroundColor: lightColorScheme.background,
textTheme: GoogleFonts.montserratTextTheme(ThemeData.light().textTheme),
);
}
final ThemeData darkThemeDataCustom = _buildDarkTheme();
ThemeData _buildDarkTheme() {
final ThemeData base = ThemeData.dark();
return base.copyWith(
colorScheme: darkColorScheme,
primaryColor: darkColorScheme.primary,
scaffoldBackgroundColor: darkColorScheme.background,
textTheme: GoogleFonts.montserratTextTheme(ThemeData.dark().textTheme),
);
}
main.dart
import 'package:first_project/shared/theme_two.dart';
import 'package:flutter/services.dart';
import '../screens/bottom_bar.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark));
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: lightThemeDataCustom,
darkTheme: darkThemeDataCustom,
home: const BottomBar(),
);
}
}
If you follow this approach, then you don't need to define colors in each and every widget. Just change the color scheme, flutter will automatically change colors according to Light theme and Dark theme.
I do not add textTheme customization code but you can do it in theme.dart file.
Hope this will help someone.

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,