Get Shared Preferences Value using Provider State Management - flutter

I try to Get Value from SharedPrefereces but I get a null value if I try to get Data in Main Page, I try to get the data when the state on the main page is created but I sometimes get null like this Available URL: http://169.172.70.108:8008/api/v1/iksk/self?idtraining=null
but after hot reload I managed to get the result like this
Available URL: http://169.172.70.208:8008/api/v1/iksk/self?idtraining=2021-01-21
this is my code
#override
void initState() {
// get pelatihan
MySharedPreferences.instance
.getStringValue(key: 'namaPelatihan')
.then((value) {
namaPelatihan = value;
// get nama Peserta
MySharedPreferences.instance
.getStringValue(key: 'namaPeserta')
.then((value) {
namaPeserta = value;
});
});
how do I get real-time results (get results when redirecting to the main page) using provider state management?

first create a function then use async and await with it
then fellow this code
_transitionToNextPageAfterSplash() async {
final auth = await SharedPreferences.getInstance()
.then((value) => value.getBool('auth') ?? false);
}

Related

SetState and Shared Preferences returning null

I am having trouble identifying as to why setState() is returning null on my shared preferences getString method.
I am calling an async function outside of the initState and the constantly printing null when i check the return string of getString on page load.
I have checked that setString is saving the correct string to the key.
Here is my code:
void initState() {
super.initState();
retrieve();
}
retrieve() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
name = prefs.getString("displayName");
print(name);
});
}
Here is where I setString(in the register user page):
try {
UserCredential result =
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: _emailController.text,
password: _passwordController.text,
);
final user = FirebaseAuth.instance.currentUser;
final name = _usernameController.text;
// if (user != null) {
await user?.updateDisplayName(name);
Navigator.pushNamed(context, homeRoute);
print(name);
You should set ,username via shared preference before accessing it.
final prefs = await SharedPreferences.getInstance();
In register page.(where you want to set name via SharedPreference before accessing it)
await prefs.setString('displayName', 'name');
After setting it in registeration you can access like above
name = prefs.getString("displayName");
When SharedPreferences return null value it means you have not set value with your key yet. So firstly set something like this:
final prefs = await SharedPreferences.getInstance().reload();
await prefs.setString('displayName', 'Mark');
then try to call it.
Thanks to all who have spent the time to respond to help. I hope this answer might help some one else down the track.
I found that Shared Preferences was not saving the data - my code was fine after all. I simply just closed the app and restarted the X code build from scratch and it is now working.....

retrieving firebase data in init state

I want to fetch a simple document from firebase and store it in a map as soon as the screen loads,
I'm trying to do this through the init state, i.e. calling the fetching function in the init state, but my map is still null.
Here is some code :
Map<String, int> minDelivery;
void minimumDelivery() async{
await FirebaseFirestore.instance
.collection("minimumDelivery")
.limit(1)
.get()
.then((QuerySnapshot qs) {
qs.docs.forEach((doc) {
int amount = doc["min_amount"];
int charge = doc["charge"];
minDelivery = {
"amount":amount,
"charge":charge
};
});
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
minimumDelivery();
}
But the map is null when I am trying to access its data.
I don't see where the problem is.I just want the map to have the required data at the start of screen.

Riverpod StateNotifierProvider depend on a FutureProvider

I have a StateNotifierProvider that depends on a FutureProvider. Currently they look like below.
final catalogProvider = StateNotifierProvider<CatalogNotifier, CatalogState>((ref) {
final network = ref.watch(networkProvider.future); // future provider
return CatalogNotifier(network: network);
});
this makes my CatalogNotifier accept a Future<NetworkProvider> instead of NetworkProvider and requires me to do things like below.
await (await network).doGet(...)
What's the best way to avoid having to await multiple and allow CatalogNotifier to accept a bare NetworkProvider so I can write like await network.doGet(...) ?
for completeness as requested, below is the other related providers
final networkProvider = FutureProvider<Network>((ref) async {
final cache = await ref.watch(cacheProvider.future);
return Network(cacheManager: cache);
});
final cacheProvider = FutureProvider<CacheManager>((ref) async {
final info = await ref.watch(packageInfoProvider.future);
final key = 'cache-${info.buildNumber}';
return CacheManager(Config(
key,
stalePeriod: const Duration(days: 30),
maxNrOfCacheObjects: 100,
));
I'm sure I can take my cache provider as a future into the network provider, so it doesn't have to be a FutureProvider, but I'm interested in how to solve the issue above, since in another scenario, if I depend on say 3 or 4 FutureProviders, this may not be an option.
this makes my CatalogNotifier accept a Future instead of >NetworkProvider and requires me to do things like below.
I can't think of a way to get your desired result.
Could you not just accept an AsyncValue and handle it in the statenotifier?
final catalogProvider = StateNotifierProvider<CatalogNotifier, CatalogState>((ref) {
final network = ref.watch(networkProvider); // future provider
return CatalogNotifier(network: network);
});
Then you can:
void someFunction() async {
network.maybeWhen(
data: (network) => AsyncData(await network.doGet(...)),
orElse: () => state = AsyncLoading(),
);
}
with riverpod v2 and its codegen features this has become much easier since you no longer have to decide the type of the provider. (unless you want to)
StateNotifier in riverpod 2
#riverpod
Future<CatalogController> catalog(CatalogRef ref) async {
final network = await ref.watch(networkProvider.future);
return CatalogController(network: network);
}
Alternative approch in Riverpod 2
Quite often you want to have a value calculated and have a way to explicitely redo that calculation from UI. Like a list from network, but with a refresh button in UI. This can be modelled as below in riverpod 2.
#riverpod
Future<CatalogState> myFeed(MyFeedRef ref) async {
final json = await loadData('url');
return CatalogState(json);
}
// and when you want to refresh this from your UI, or from another provider
ref.invalidate(myFeedProvider);
// if you want to also get the new value in that location right after refreshing
final newValue = await ref.refresh(myFeedProvider);
Riverpod 2 also has loading and error properties for the providers. You can use these to show the UI accordingly. Though if you want to show the last result from the provider while your feed is loading or in an error state, you have to model this yourself with a provider that returns a stream/BehaviorSubject, caches the last value .etc.
you can make AsyncValue a subtype of StateNotifier, I use the Todo list as an example.
as follows:
class TodoNotifier extends StateNotifier<AsyncValue<List<Todo>>> {
TodoNotifier(this._ref) : super(const AsyncValue.loading()) {
_fetchData();
}
final Ref _ref;
Future<void> _fetchData() async {
state = const AsyncValue.loading();
// todoListProvider is of type FutureProvider
_ref.read(todoListProvider).when(data: (data) {
state = AsyncValue.data(data);
}, error: (err, stackTrace) {
state = AsyncValue.error(err, stackTrace: stackTrace);
}, loading: () {
state = const AsyncValue.loading();
});
}
void addTodo(Todo todo) {
if (state.hasValue) {
final todoList = state.value ?? [];
state = AsyncValue.data(List.from(todoList)..add(todo));
}
}
....
}

Added data is only showing after reloading in flutter

here is a popup screen to add the transaction to the app, as you can see here
and when the add button pressed the data will add to database and also to the dislpay , here is the code
ElevatedButton(
//on pressed
onPressed: () async {
final _categoryName = _nameEditingController.text;
if (_categoryName.isEmpty) {
return;
}
final _type = selectedCategoryNotifier.value;
//sending the data to model class
final _category = CategoryModel(
id: DateTime.fromMillisecondsSinceEpoch.toString(),
name: _categoryName,
type: _type,
);
//inserting the data to database
await CategoryDb.instance.insertCategory(_category);
//refreshing the ui
await CategoryDb.instance.refreshUI();
//and quitting the popup screen
Navigator.of(ctx).pop();
},
child: const Text('Add'),
),
and in this code you can see that I called 2 functions that for insert data and also refresh the UI, in the refresh UI function I added the function that to get all data from database to screen, here the code of all functions for CRUD operatins
const databaseName = 'category-database';
abstract class CategoryDbFunctions {
Future<List<CategoryModel>> getCategories();
Future<void> insertCategory(CategoryModel value);
}
//CRUD operations code
class CategoryDb implements CategoryDbFunctions {
CategoryDb._internal();
static CategoryDb instance = CategoryDb._internal();
factory CategoryDb() {
return instance;
}
ValueNotifier<List<CategoryModel>> incomeCategoryListListener =
ValueNotifier([]);
ValueNotifier<List<CategoryModel>> expenseCategoryListListener =
ValueNotifier([]);
#override
Future<void> insertCategory(CategoryModel value) async {
final _categoryDB = await Hive.openBox<CategoryModel>(databaseName);
await _categoryDB.add(value);
await refreshUI();
}
#override
Future<List<CategoryModel>> getCategories() async {
final _categoryDB = await Hive.openBox<CategoryModel>(databaseName);
return _categoryDB.values.toList();
}
Future<void> refreshUI() async {
final _allCategories = await getCategories();
incomeCategoryListListener.value.clear();
expenseCategoryListListener.value.clear();
await Future.forEach(
_allCategories,
(CategoryModel category) {
if (category.type == CategoryType.income) {
incomeCategoryListListener.value.add(category);
} else {
expenseCategoryListListener.value.add(category);
}
},
);
}
}
so I checked the all things , but I couldn't find where I'm missing parts,
and here is the main part, it is adding to the database also displaying after I refresh the UI or change the tab here you can see what I mean by 'changing the tab'
this is the problem I'm trying to fix this for 2 day, i couldn't find any solution or mistake in my code
There many ways you can handle this problem.
but I dont see where you notify youre ui that the data has been changed, flutter does only update the ui when you use setState etc.. these functions help flutter updating the ui where the data changed.
i would recommend you to use setState in the place you invoke youre dialog.
onTap:(){
setState(){
await dialogStuff();
}
}

Flutter/Dart - Use something like ChangeNotifier or setState within Future?

I'm using SocialProvider with ChangeNotifier. I want it to update its Consumer listener widgets once the user logs in and after posting to a database on the web.
Currently, I'm calling a Future method which inserts the new values to a Shared Preferences file upon a successful http post, but this won't update the UI until a call is made to a page which loads shared preferences.
In order to see an instant update, is there any way to access something like the ChangeNotifier or other kind of setState type function from within a Future? Here's the future;
Future<void> postSocialData(String email) async {
final url = "http://example.com/example.php?currentemail=$email"
final response = await http.get(url);
String currentuserid;
if (response.statusCode == 200) {
currentuserid = response.body;
setSharedPreferences(
currentavatar: "http://example.com/" + response.body + "-user.jpg",
currentemail: email,
currentlogged: true,
currentuserid: currentuserid,
);
print(response.body);
} else {
throw Exception('We were not able to successfully post social data.');
}
}
Any ideas on how to get an instant update from a Future method?
Turns out I was able to insert the Future<void> postSocialData within the scope of the class SocialProvider with ChangeNotifier itself and hence use The ChangeNotifier within the Future to alert the Consumers/listeners.