Provider is used after being disposed - flutter

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

Related

How do I fix the error about a widget being unmounted so the State no longer has a context in Flutter?

I get an error. Here is the debug console:
════════ Exception caught by scheduler library ═════════════════════════════════
This widget has been unmounted, so the State no longer has a context (and should be considered defunct).
════════════════════════════════════════════════════════════════════════════════
I am using the flutter_riverpod package. I'm using it for a search field and when searching for something it shows this error. Also, search doesn't work. Here is some code:
User interface code:
TextFormField(
…
onChanged: (search) => controller.updateSearch(search),
onSaved: (search) {
search == null ? null : controller.updateSearch(search);
},
),
itemBuilder: (context, index) {
if (mounted) {
return name.contains(state.search) ? ListTile(title: Text(name)) : Container();
}
return Container();
},
Controller code:
class Controller extends StateNotifier<State> {
Controller() : super(State());
void updateSearch(String search) => state = state.copyWith(search: search);
}
final controllerProvider = StateNotifierProvider.autoDispose< Controller, State>((ref) {
return Controller();
});
State code:
class State {
State({this.search = "", this.value = const AsyncValue.data(null)});
final String search;
final AsyncValue<void> value;
bool get isLoading => value.isLoading;
State copyWith({String? search, AsyncValue<void>? value}) {
return State(search: search ?? this.search, value: value ?? this.value);
}
}
Feel free to comment if you need more information!
How to fix this error? I would appreciate any help. Thank you in advance!
Try with below code snippet.
if (mounted) {
setState(() {
// To do
});
}

How to set onChanged value of Switch

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.

onFieldSubmitted of TextFormFeild repetitive call upon calling future function - Flutter

Scenario:
I have a TextFormFeild in which user write product name to search. If the searched product is not available in the database then I want to store that searched product in the database. To do that I have the function storeUserSearchedValue() which is calling future function inside it.
After looking googling this problem I have found that calling future during flutter's build phase causes repetitive invocations.
How can I store the value of the searched product in the database on the onFieldSubmitted function without facing non-stop repetirive invocation of the function?
Code
Class Displaying textfeild and and it's onFieldSubmitted function.
class SearchProductView extends StatefulWidget {
#override
_SearchProductViewState createState() => _SearchProductViewState();
}
class _SearchProductViewState extends State<SearchProductView> {
storeUserSearchedValue(String _userSearchedProduct,List<Product> _productsData) {
print("I N S I D E storeUserSearchedValue: " + _userSearchedProduct.toString());
int _noOfResultsOfUserProducts = 0;
for (int i = 0; i < _productsData.length; i++) {
if (_productsData[i].name.contains(_userSearchedProduct))
_noOfResultsOfUserProducts++;
}
if (_noOfResultsOfUserProducts == 0)
SearchviewModel().addCustomerSearchedProducts(_userSearchedProduct);
}
#override
Widget build(BuildContext context) {
final _allProductsData = Provider.of<List<Product>>(context);
return Scaffold(
............
TextFormField(
controller: _searchedProductText,
onFieldSubmitted:
storeUserSearchedValue(
_searchedProductText.text,
_allProductsData,
),
onChanged: searchOperation,
),
)
.........
}
Actual logic of storing data in the database
class SearchviewModel extends BaseViewModel {
// -------- Below Function are adding searched product in `customer` collection
addCustomerSearchedProducts(String searchedProductText) async {
String _currentUser = FirebaseAuth.instance.currentUser;
Map<String, dynamic> custdataMap = {
"custSearchedProducts": [searchedProductText, DateTime.now()]
};
await updateCustData(custdataMap, _currentUser.uid);
}
Future<bool> updateCustData(Map<String, dynamic> dataMap, String custID) async {
// - Dynamically adding data in the db
dataMap.forEach(
(key, value) async {
await FirebaseFirestore.instance.collection('customer').doc(custID).set(
{
key: value,
},SetOptions(merge: true),
);
},
);
return true;
}
}
Video representaion of problem
Simply adding this (_)=> (not this ()=>) with onFieldSubmitted property of textformfeild while calling storeUserSearchedValue function stop repetative invocations.
onFieldSubmitted: (_)=> storeUserSearchedValue(_searchedProductText.text,_allProductsData),
The above code snippet worked fine!
I'm not quite sure what is the reason behind it though

How do I change the variable from across page if the target page has no context?

'databaseFunctions.dart'
String path = 'path' // a variable needed to be modified across page
Future queryALL() async {
Database db = await openDatabase(path);
return await db.query('all'); }
'main.dart'
// inside a stateful widget
DropdownButton(
value: currentValue,
onChanged: (String newValue) {
setState(() {
currentValue = newValue;
>> path = newValue << ;}} // How can I accomplish this?
'few other pages'
// call queryALL() to build dataTable
Provider, Navigator didn't work since the page var x is in has no Widget, therefore no explicit entrance for any context.
'import' didn't work, since it only initializes var x.
Any ideas?
I am using state management Getx for this situations. Import like this
dependencies:
get: ^3.8.0
Define controller like this
class DatabaseController extends GetxController{
RxString path = 'path'.obs;
Future queryALL() async {
Database db = await openDatabase(path);
return await db.query('all');
}
}
If you will use this controller anywhere you should initiate when program starts. I recommend you do it in your main.dart
DatabaseController dbController = Get.put(DatabaseController());
Then you always can access this controller like this
DatabaseController dbController = Get.find();
You just need to call like this
DropdownButton(
value: currentValue,
onChanged: (String newValue) {
setState(() {
currentValue = newValue;
dbController.path.value = newValue;
dbController.queryAll();
}
}

In Flutter How to use Providers with AMQP?

in Flutter -which I just recently begin to use-, I am trying to use an AMQP stream using dart_amqp: ^0.1.4 and use providers provider: ^3.1.0+1 to make the data available throughout the app.
Only after logging in I start the AMQP service.
The AMQP part works without any issues, I get the data but I never manage to use it with Providers.
main.dart
class BigBrother extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<EventsModel>(create: (_) => EventsModel()),
ChangeNotifierProxyProvider<EventsModel, DeviceState>(
create: (_) => new DeviceState(),
update: (context, eModel, deviceState) {
deviceState.updateFromEvent(eModel.value);
},
),
],
My models in models.dart
(As seen in below code, I also tried to used StreamProvider and commented it out)
// Global data model
class DeviceState with ChangeNotifier {
Map<String, Map<String, dynamic>> state = {};
DeviceState() {
this.state['xxx'] = {};
this.state['yyy'] = {};
}
updateFromEvent(EventsItemModel event) {
if (event != null && event.type != null) {
switch (event.type) {
case 'heartbeat':
this.state[event.device][event.type] = event.createdAt;
break;
case 'metrics':
this.state[event.device][event.type] = {}
..addAll(this.state[event.device][event.type])
..addAll(event.message);
break;
}
notifyListeners();
}
}
}
class EventsModel with ChangeNotifier {
EventsItemModel value;
bool isSubscribed = false;
AMQPModel _amqp = new AMQPModel();
// final _controller = StreamController<EventsItemModel>();
EventsModel();
// Stream<EventsItemModel> get stream => _controller.stream;
_set(String jsonString) {
value = new EventsItemModel.fromJson(jsonString);
// _controller.sink.add(value); // send data to stream
// Provider.of<DeviceState>(context, listen: false).updateFromEvent(value);
notifyListeners();
}
subscribe() {
if (!this.isSubscribed) {
this.isSubscribed = true;
this._amqp.subscribeEvents(_set); // start AMQP service after login
}
}
}
So on the login.dart view, on button pressed and validating the login, I start the AMQP stream:
onPressed: () {
if (_formKey.currentState.validate()) {
print("Login button onPressed");
Provider.of<EventsModel>(context, listen: false)
.subscribe();
Navigator.pushReplacementNamed(context, Routes.live);
}
And lastly the view after successful login:
class _LivePageState extends State<LivePage> {
#override
Widget build(BuildContext context) {
DeviceState deviceState = Provider.of<DeviceState>(context);
print('#### Device state updated');
print(deviceState.state['xxx']);
In the above code, deviceState is always null.
So after trying many combination of various Providers, I am still unable to make this work.
Would be glad to have someone's insight on this.
Best regards!