Flutter Get.changeTheme() does not change the app theme (misuse?) - flutter

Here described the simplest/lasiest way to change a color theme of the app. It states:
Please do not use any higher level widget than GetMaterialApp in order to update it.
It appears my understanding of how to use the Get package is not correct.
Tried 2 variants of the code (with or without the commented out line) - the theme does not change.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'ThemeDemo',
// UPDATE: Added 2 lines
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: Get.isDarkMode ? ThemeMode.light : ThemeMode.dark,
home: const MyHomePage(
title: 'Theme Demo',
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
PopupMenuButton<String>(
onSelected: handleClick,
itemBuilder: (BuildContext context) {
return {'Logout', 'Theme'}.map((String choice) {
return PopupMenuItem<String>(
value: choice,
child: Text(choice),
);
}).toList();
},
),
],
),
);
}
void handleClick(String value) {
switch (value) {
case 'Logout':
break;
case 'Theme':
// UPDATE: Uncommented the control
Get.changeTheme(Get.isDarkMode ? ThemeData.light() : ThemeData.dark());
break;
}
}
}
Please, help me to understand the problem and how to use Get to change the theme.
I am very beginner in Flutter, so any constructive critics is welcome!

you should specify the Theme and darkTheme in GetMaterialApp then the themeMode,
like this:
GetMaterialApp(
// your other properties
theme: YourDefaultTHemeHere,
darkTheme: YourDarkThemeHere,
themeMode: Get.isDarkMode ? ThemeMode.light : ThemeMode.dark,
)

Related

Flutter app resizing on different devices

I installed my app on 2 different devices, the problem is both devices show different widget sizes. Both devices have different screen sizes.
The expected result ->
image1 from Moto One Fusion+
image2 from OnePlus 6pro
main.dart code ->
void main() {
runApp(BmiApp());
}
class BmiApp extends StatelessWidget {
const BmiApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
'/input': (context) => InputPage(),
'/calculate': (context) => CalculatedResult(),
},
theme: ThemeData.dark().copyWith(
appBarTheme: AppBarTheme(
color: Color(0xFF0A1234),
),
scaffoldBackgroundColor: Color(0xFF0A1234),
),
initialRoute: '/input',
);
}
}
input_page.dart code ->
class InputPage extends StatefulWidget {
const InputPage({Key? key}) : super(key: key);
#override
State<InputPage> createState() => _InputPageState();
}
class _InputPageState extends State<InputPage> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('BMI Calculator'),
centerTitle: true,
),
body:Column(),
),
);
}
}
Inside column I have stacked all the widgets.
Github link for complete code

Using riverpod for simple state management is not reading state changes at UI

I'm moving to Riverpod and starting with what should be a very simple boolean state management to switch the app theme. I've read many "how to" to do this exact thing, but none focus on the basics itself, are using outdated API, use different notifiers and providers, and add more features than needed. So now I'm lost.
versions:
shared_preferences: ^2.0.13
flutter_riverpod: ^1.0.3
Simple story:
A model class that extends StateNotifier and has accessor and mutators in it for 1 single boolean value
Very basic app + stateful widget
Problem: I can see, via prints, the state is being altered, but the UI is not changing to when the state is changed. Am I not using StateNotifier and StateProvider properly for a simple boolean state?
Thank you for any insight and guidance! Code follows:
===
model class with accessors / mutators
class RiverThemeDarkModel extends StateNotifier<bool> {
RiverThemeDarkModel() : super(false) {}
bool get isDark {
print("model asked for dark status: ${state}");
return state;
}
toggleDark() {
print("toggler has listeners: ${hasListeners}");
state = !state;
print("toggled to: ${state}");
}
set isDark(bool value) {
state = value;
}
}
Very basic app + stateful widget
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(ProviderScope(child: MyApp()));
}
final themeProvider = StateProvider((ref) => RiverThemeDarkModel());
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final darkValueModel = ref.watch(themeProvider);
print("consumer widget: dark value is ${darkValueModel.state} and listeners: ${darkValueModel.hasListeners}");
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: darkValueModel.isDark? ThemeData.dark() : ThemeData.light(),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends ConsumerStatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
ConsumerState<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends ConsumerState<MyHomePage> {
#override
Widget build(BuildContext context) {
final darkValueModel = ref.watch(themeProvider);
print("build of state");
return Scaffold(
appBar: AppBar(
title: Text(darkValueModel.isDark ? "Dark Mode" : "Light Mode"),
actions: [
IconButton(
icon: Icon(darkValueModel.isDark
? Icons.nightlight_round
: Icons.wb_sunny),
onPressed: () {
ref.read(themeProvider).toggleDark();
print("Got pressed button, after setting is ${ref.read(themeProvider).isDark} \n\n");
})
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Something',
),
],
),
),
);
}
}
From your code snippet, what you did here
final themeProvider = StateProvider((ref) => RiverThemeDarkModel());
was to provide to your UI access to the default state of your class which is false. thus if you change it to true and rerun the app it changes the theme. now what you want to do is expose access to the stateNotifier of your class which listens to the state changes of the class and notifies its listeners. To achieve this you need to use a StateNotifierProvider this way...
final themeProvider = StateNotifierProvider<RiverThemeDarkModel, bool>(
(ref) => RiverThemeDarkModel());
Now the rest of the code to help your implementation is as follows
For your Model.
class RiverThemeDarkModel extends StateNotifier<bool> {
RiverThemeDarkModel() : super(false) {}
//u don't need this 'getter' piece of code
bool get isDark {
print("model asked for dark status: ${state}");
return state;
}
toggleDark() {
print("toggler has listeners: ${hasListeners}");
state = !state;
print("toggled to: ${state}");
}
//neither do you need this
set isDark(bool value) {
state = value;
}
}
For your UI...
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(ProviderScope(child: MyApp()));
}
final themeProvider = StateNotifierProvider<RiverThemeDarkModel, bool>(
(ref) => RiverThemeDarkModel());
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final darkValueModel = ref.watch(themeProvider);
print("consumer widget: dark value is ${darkValueModel.state} and listeners: ${darkValueModel.hasListeners}");
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: darkValueModel? ThemeData.dark() : ThemeData.light(),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends ConsumerStatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
ConsumerState<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends ConsumerState<MyHomePage> {
#override
Widget build(BuildContext context) {
final darkValueModel = ref.watch(themeProvider);
print("build of state");
return Scaffold(
appBar: AppBar(
title: Text(darkValueModel ? "Dark Mode" : "Light Mode"),
actions: [
IconButton(
icon: Icon(darkValueModel
? Icons.nightlight_round
: Icons.wb_sunny),
onPressed: () {
ref.read(themeProvider.notifier).toggleDark();
print("Got pressed button, after setting is ${ref.read(themeProvider)} \n\n");
})
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Something',
),
],
),
),
);
}
}
Check out the documentation too. It clarifies some things you won't get from most tutorials. https://riverpod.dev/docs/concepts/reading
use this
final themeProvider = StateNotifierProvider<RiverThemeDarkModel, bool>(
(ref) => RiverThemeDarkModel());
Instead of this
final themeProvider = StateProvider(
(ref) => RiverThemeDarkModel());
Small Change on your code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(ProviderScope(child: MyApp()));
}
final themeProvider = StateNotifierProvider<RiverThemeDarkModel, bool>(
(ref) => RiverThemeDarkModel());
class MyApp extends ConsumerWidget {
MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final darkValueModel = ref.watch(themeProvider);
print(
"consumer widget: dark value is ${ref.read(themeProvider.notifier).state} and listeners: ${ref.read(themeProvider.notifier).hasListeners}");
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme:darkValueModel
? ThemeData.dark()
: ThemeData.light(),
home: MyHomePage(title: 'Flutter Demo Home Page${ref.read(themeProvider.notifier).isDark}'),
);
}
}
class MyHomePage extends ConsumerStatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
ConsumerState<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends ConsumerState<MyHomePage> {
#override
Widget build(BuildContext context) {
final darkValueModel = ref.watch(themeProvider);
print("build of state");
return Scaffold(
appBar: AppBar(
title: Text(ref.read(themeProvider.notifier).isDark
? "Dark Mode${widget.title}"
: "Light Mode"),
actions: [
IconButton(
icon: Icon(ref.read(themeProvider.notifier).isDark
? Icons.nightlight_round
: Icons.wb_sunny),
onPressed: () {
ref.read(themeProvider.notifier).toggleDark();
print(
"Got pressed button, after setting is ${ref.read(themeProvider.notifier).isDark} \n\n");
})
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Something ${ref.read(themeProvider.notifier).isDark} ",
),
],
),
),
);
}
}
class RiverThemeDarkModel extends StateNotifier<bool> {
RiverThemeDarkModel() : super(false) {}
bool get isDark {
print("model asked for dark status: ${state}");
return state;
}
toggleDark() {
print("toggler has listeners: ${hasListeners}");
state = !state;
print("toggled to: ${state}");
}
set isDark(bool value) {
state = value;
}
}

what is the alternative for accentColor Flutter

I'm new to flutter. working on a chat app. I have created a app bar but the colors I added in main.dart not display. it just display as default blue color. how to correct??
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
//title: 'Profile Section',
theme: ThemeData(
primaryColor: Color(0xff075e54),
accentColor: Color(0xff128C7E)),
home: Homescreen(key: null),
);
}
}
homescreen.dart
import 'package:flutter/material.dart';
class Homescreen extends StatefulWidget {
Homescreen({ Key? key }) : super(key: key);
#override
_HomescreenState createState() => _HomescreenState();
}
class _HomescreenState extends State<Homescreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Whatsapp Clone"),
actions: [
IconButton(icon: Icon(Icons.search), onPressed: () {}),
IconButton(icon: Icon(Icons.more_vert), onPressed: () {}),
],
),
);
}
}
Use this, as your code. It should work.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
final ThemeData theme = ThemeData(); //You need to make a var, that works as ThemeData
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
//title: 'Profile Section',
theme: theme.copyWith(
colorScheme: theme.colorScheme.copyWith(primary: Color(0xff075e54),secondary: Color(0xff128C7E), //Then use it with colorScheme.
),
),
home: Homescreen(key: null),
);
}
}

How to pass data down the widget tree?

I have read and understood a similar question posted here, but am having trouble applying it to my use case. I am new to Flutter and am creating an app that streams audio from a given URL using Ryan Heise's audio_service plugin. Using this plugin I instantiate an audioHandler immediately upon starting my app:
late AudioHandler audioHandler;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final session = await AudioSession.instance;
await session.configure(const AudioSessionConfiguration.music());
audioHandler = await AudioService.init(
builder: () => AudioPlayerHandler(),
config: const AudioServiceConfig(
androidNotificationChannelId: 'com.ryanheise.myapp.channel.audio',
androidNotificationChannelName: 'Channel Name',
androidNotificationOngoing: true,
),
);
runApp(const MyApp());
}
With this audioHandler initialized, I would like to use it in child widgets. The example below demonstrates one such child widget:
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Koradi Radio",
theme: ThemeData(
brightness: Brightness.light,
scaffoldBackgroundColor: Colors.white70,
),
darkTheme: ThemeData(
brightness: Brightness.dark,
),
themeMode: ThemeMode.system,
home: const EnglishHome());
}
}
class EnglishHome extends StatefulWidget {
const EnglishHome({Key? key}) : super(key: key);
#override
_EnglishHomeState createState() => _EnglishHomeState();
}
class _EnglishHomeState extends State<EnglishHome> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('English Radio'),
backgroundColor: Colors.transparent,
),
body: ...
}
}
Note that MyApp currently just routes to EnglishHome(), but I plan on adding additional languages and instead routing MyApp to a page where a user can select their language. How can I pass audioHandler to all descendent widgets from Main() ? (EnglishHome, EspHome, FrenchHome, etc?) Based upon what I have read, I will either be modifying the Key parameter of child widgets or else their BuildContext?
You can use provider package and all you need to do is use Provider.value and then use Provider.of(context) in your EnglishHome, FrenchHome etc classes.
late AudioHandler audioHandler;
Future<void> main() async {
audioHandler = await AudioService.init(...);
runApp(MyApp(audioHandler));
}
class MyApp extends StatelessWidget {
final AudioHandler audioHandler;
const MyApp(this.audioHandler, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Provider.value(
value: audioHandler, // Providing the data above MaterialApp
child: MaterialApp(
home: EnglishHome(),
),
);
}
}
class EnglishHome extends StatelessWidget {
#override
Widget build(BuildContext context) {
// Accessing the data.
final audioHandler = Provider.of<AudioHandler>(context);
return Container();
}
}
The other answers here are also valid solutions, but what I was able to do was add an audioHandler parameter to EnglishHome:
class EnglishHome extends StatefulWidget {
var audioHandler;
EnglishHome({Key? key, this.audioHandler}) : super(key: key);
#override
_EnglishHomeState createState() => _EnglishHomeState();
And then pass the audioHandler in when the Widget was called from my main.dart file:
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Radio",
theme: ThemeData(
brightness: Brightness.light,
scaffoldBackgroundColor: Colors.blue[100],
),
darkTheme: ThemeData(
brightness: Brightness.dark,
),
themeMode: ThemeMode.system,
home: EnglishHome(
audioHandler: audioHandler,
));
}
}

changing theme through button input in Flutter

I made a button, and when that button is pressed, I want to change the color of the theme.
I am trying to modify the color with the value received from the button, but it does not work.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
String themeColors=context.watch<DisplayList>().themeColor;
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.${themeColors}, //How do I fix this part?
),
Or is there another way to change the color?
themeColors variable already contains a string of the color to be changed.
You can't actually have a syntax like this one on Flutter: Colors.${themeColors}
To handle multiples themes, you need to create multiples ThemeData and switch them with a ValueNotifier.
I suggest you to use an already made community package like theme_provider which will help you to switch between themes very easily.
You will have to convert your widget to Stateful and use the setState method
class XYZ extends StatefulWidget {
const XYZ({Key? key}) : super(key: key);
#override
_XYZState createState() => _XYZState();
}
class _XYZState extends State<XYZ> {
var myAppBarThemeColor = Colors.red;
#override
Widget build(BuildContext context) {
print(myAppBarThemeColor);
return MaterialApp(
theme: ThemeData(appBarTheme: AppBarTheme(backgroundColor: myAppBarThemeColor)),
home: Scaffold(
appBar: AppBar(
title: Text('Hello'),
),
body: Center(
child: TextButton(
onPressed: () => setState(() => myAppBarThemeColor = Colors.green),
child: Text('Change AppBar Color'),
),
),
),
);
}
}
You can use findAncestorStateOfType to manage the state of the root widget.
class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
static _AppState? of(BuildContext context) => context.findAncestorStateOfType<_AppState>();
#override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
late bool isDarkMode;
late ThemeModeStorage storage;
void toggleDarkMode() {
setState(() {
isDarkMode = !isDarkMode;
});
storage.writeBool(value: isDarkMode);
}
...
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeConfig(isDarkMode).themeData,
home: HomeScreen(),
);
}
}
So you can call this everywhere in your app.
App.of(context)?.toggleDarkMode();