How to set onChanged value of Switch - flutter

I have a switch which toggle the app theme but there is some errors on the code
Switch(value: AppStyleMode.isSwitched, onChanged: (value)=> AppStyleMode.switchMode())
My Theme file
import 'package:flutter/material.dart';
class AppStyleMode extends ChangeNotifier {
bool isSwitched = true;
Color primaryBG = Colors.white;
Color appBarBG = Colors.yellow;
switchMode() {
if (isSwitched == true) {
primaryBG = Colors.black];
appBarBG = Colors.grey[400];
isSwitched = false;
} else {
//if it is dark mode currently switch to light
primaryBG = Colors.white;
appBarBG = Colors.yellow;
isSwitched = true;
}
notifyListeners();
}
}
Errors on Switch :

If you are not using any particular state management packages,
First, you would have to create an instance of your AppStyleMode class, so that you can use values from it and also listen to it's changes.
Assuming you have a StatefulWidget,
first define your AppStyleMode in it's State,
class MyState extends State<MyStatefulWidget> {
late AppStyleMode appStyleMode;
Then in your initState, initialise it and add a listener to it.
void initState () {
super.initState();
appStyleMode = AppStyleMode();
// Also add a listener to it and call setState to rebuild your StatefulWidget
appStleMode.addListener(() => {
setState(() {});
});
}
Then you can use it in your build by using your variable appStyleMode like this,
Switch(value: appStyleMode.isSwitched, onChanged: (value) => appStyleMode.switchMode())
I would rather suggest you look into a State management solution like provider or Getx. But it is not compulsory.

Related

Pass value of variables to another widget Flutter

I'm trying to pass a value of a variable from a StatefulWiget to another StatefulWidget
class InputFieldCheckTick extends StatefulWidget {
double timbreFiscaleFournisseur = 0.000;
bool exoTVA = false;
.
.
.
value: isTicked,
onChanged: (value) async {
await setState(() {
isTicked = value;
if (isTicked == false) {
widget.exoTVA = false;
} else {
widget.exoTVA = true;
}
});
.
.
.
value: isTicked,
onChanged: (value) async {
await setState(() {
isTicked = value;
if (isTicked == false) {
widget.exoTVA = false;
} else {
widget.exoTVA = true;
}
});
and i'm trying to pass the values of exoTVA and timbreFiscaleFournisseur here :
setState(() {
future = ajoutFournisseur(
numeroFournisseur.text,
addressFournisseur.text,
matriculeFiscaleFournisseur.text,
raisonSocialeFournisseur.text,
paysFournisseur.text,
villeFournisseur.text,
InputFieldCheckTick()
.timbreFiscaleFournisseur,
InputFieldCheckTick().exoTVA);
});
I think you are creating an StatefulWidget which contains a Final property called exoTVA, something like:
class MyWidget extends StatefulWidget {
final bool exoTVA;
[...]
Because you are calling widget.exoTVA which refers to stateful related widget, and there is your mistake. You can't change widget.exoTVA for the way widgets are build. What you can do is to do this:
class _MyWidgetState extends State<MyWidget> {
// Here you designate an instance value for your widget property
bool? _exoTVA;
// On initState you define from parent widget, I do this all the time
#override
initState((){
super.initState();
_exoTVA = widget.exoTVA;
});
[...] //Continue with the rest of your widget
Also when you call those changes in setState, change widget.exoTVA to _exoTVA, and to finish you can call that var like this:
setState(() {
future = ajoutFournisseur(
numeroFournisseur.text,
addressFournisseur.text,
matriculeFiscaleFournisseur.text,
raisonSocialeFournisseur.text,
paysFournisseur.text,
villeFournisseur.text,
InputFieldCheckTick()
.timbreFiscaleFournisseur,
_exoTVA);//Changed this line
});
For your reference, check out StatefulWidget class

Flutter Getx builder not updating UI

I'm trying to use a GetX Builder in combination with a bool in the controller to display a loading spinner while fetching data and then the data afterwards.
Printing the update to the bool shows it finishes loading and changes the variable but the UI is never updated to reflect this.
Controller
class AuthController extends GetxController {
//final RxBool isLoading = true.obs;
//var isLoading = true.obs;
final Rx<bool> isLoading = Rx<bool>(true);
setLoading(bool status) {
isLoading.value = status;
print(isLoading.value);
update();
}
fetchUserData() async {
setLoading(true);
_firebaseUser.bindStream(_auth.authStateChanges());
if (_auth.currentUser != null) {
bool profileRetrieved =
await FirestoreDB().getUserProfile(_auth.currentUser!.uid).then((_) {
return true;
});
setLoading(!profileRetrieved);
}
}
}
Profile Page
class ProfileCard extends StatelessWidget {
const ProfileCard({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetBuilder<AuthController>(
init: AuthController(),
initState: (_) => AuthController().fetchUserData(),
builder: (controller) {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
return Container(...);
},
);
}
}
That widget is called within another as part of the wider UI. Let me know if you'd need to see that and/or anything else.
As you can see I've tried different ways of setting up the bool and none of them seem to trigger a change of UI in the builder.
Probably doing something stupid but I've tried a few different approaches and looked around for people having similar problems but not been able to crack it.
Thanks in advance.
You are using isLoading as a Rx<bool>. In that case you need to change GetBuilder into GetX. And no need to call update().
GetBuilder is for non-reactive, non-Rx, simple state management
GetX is for reactive, Rx state management

Flutter: Remove ChangeNotifier Boilerplate `notifyListeners()`

Is there something like #annotation to remove the boilerplate when creating a model class that extend ChangeNotifier?
Boilerplate
class MyModel extends ChangeNotifier{
bool _isLoading = true;
bool get isLoading => _isLoading;
set isLoading(bool value) {
_isLoading = value;
notifyListeners();
}
}
Using something like #annotation
class MyModel extends ChangeNotifier{
#Notify
bool _isLoading = true;
}
You can try Get for your state management: https://pub.dev/packages/get
Reactive programming with Get is as easy as using setState.
First you can create a new controller:
import 'package:get/get.dart';
class FormController extends GetxController {
FormController();
Rx<bool> _isLoading = false.obs;
set setLoading(value) => this._isLoading.value = value;
bool get loading => this._isLoading.value;
}
And in the UI, when you want to show that value and update the screen whenever the values changes, simply do this:
Obx(
() => formController.loading
? Center(
child: CircularProgressIndicator(),
)
: ElevatedButton(
onPressed: formController.googleLoading ||
formController.anonymousLoading
? null
: () async {
formController.setLoading = true;
if (_formKey.currentState!.validate()) {
await userController
.loginWithEmailAndPassword(
formController.email,
formController.password);
}
formController.setLoading = false;
},
child: Text('login'.tr),
),
),
See an more in-depth explanation of state management here. There you will see more examples and also the difference between the simple state manager and the reactive state manager
You will get a good idea of GetX power.

Dart variable not final warning

So I have written this code below to make an Icon Stateful inside a Stateless widget.
class IconState extends StatefulWidget {
final bool isSelected;
IconState({
this.isSelected,
});
_IconState state; // this is not final because I need to assign it below
void toggle() {
state.change();
}
#override
_IconState createState() => state = new _IconState(
isSelected: this.isSelected,
);
}
class _IconState extends State<IconState> {
_IconState({
this.isSelected,
});
bool isSelected = false;
Widget _unSelected = Icon(
null,
);
Widget _selected = Icon(
Icons.check_outlined,
color: Colors.red,
);
void change() {
setState(() {
this.isSelected = this.isSelected == true ? false : true;
});
}
Icon evaluate() {
if (isSelected) {
return _selected;
}
return _unSelected;
}
#override
Widget build(BuildContext context) {
return evaluate();
}
}
To update the state of the Icon, I call the toggle() method from my Stateless widget.
Dart is giving me a non-final instance warning inside an #immutable class, but I am unable to find a workaround for this.
I have tried following:
final _IconState state = new _IconState(
isSelected: this.isSelected, // throws an error => Invalid reference to 'this' expression.
);
also this, but doesn't work either
final _IconState state;
IconState({this.isSelected}) {
this.state = new _IconState(
isSelected: this.isSelected,
);
};
Is there a workaround?
I would put the isSelected boolean inside an external state management class, then you can return 2 separate widgets in response to the change. Otherwise you would have to change the state inside of the widget where the icon will be displayed. Something like this:
class IconState extends ChangeNotifier{
bool _isSelected;
//any other needed state
bool get isSelected => _isSelected;
void changeIsSelected(bool selected) {
_isSelected = selected;
notifyListeners();
}
}
Then use ChangeNotifierProvider to to inject the state and call the change method.
final iconStateProvider = ChangeNotifierProvider((ref) => IconState());
Now, you can use iconStateProvider to access the state and methods. You will need a Builder or Consumer widget to listen for changes to the state.
Consumer( builder: (context, watch, child) {
final iconState = watch(iconStateProvider);
if (iconState.isSelected) {
return Icon();
} else {
return null;
}
This is using the Riverpod library, which is only 1 of many external state management libraries. I recommend watching tutorials on YouTube on different libraries and pick one that best suits you.

Provider is used after being disposed

i have a flutter provider that that is in charge of app settings like this,
class AppProvider extends ChangeNotifier {
dynamic _appSettinngs;
AppProvider() {
_loadAppSettings();
}
void _loadAppSettings() {
dynamic tempSttings = {"biometrics": false, "showBalance": false};
_localStorage.getAll('security').then((value) {
if (value == null) {
_appSettinngs = tempSttings;
notifyListeners();
_localStorage.save("security", tempSttings);
} else {
_appSettinngs = value;
notifyListeners();
}
});
}
void updateOptions(String option, bool value) {
_appSettinngs[option] = value;
_localStorage.save("security", _appSettinngs);
_loadAppSettings();
}
}
so basically i'm trying to update the app settings using a switcher widget like this
Switch(
value: Provider.of<AppProvider>(context)
.appOptions['showBalance'],
onChanged: (value) =>
Provider.of<AppProvider>(context, listen: false)
.updateOptions("showBalance", value),
),
but when i try to toggle the setting, i get this error
Unhandled Exception: A AppProvider was used after being disposed.
what am i getting wrong?
so the problem was that i used a singleton instance while creating my provider so all i had to do was not use a singleton and it took care of the error