How can I read the string value from the _storageToken.read() call? - flutter

I have this construction in my homepage widget which is suppose to get the saved token. Now this returns a Future. So How can I read the token string? I tried storedToken.toString() but it returns Future<String?> not what I expected. I am expecting a String value like "fhsdjkfhs.djkfhsdjkfhsdjk"
void initState(){
const _storageToken = FlutterSecureStorage();
storedToken = _storageToken.read(key: 'jwt');
print(storedToken);
}

_storageToken.read() returns a Future, so you have to await it in order to get the value.
The problem is, you can't perform async/await action directly on the initState.
If you still want to call that _storageToken.read() on the initState, you can use this workaround.
void initState(){
/// Initialize Flutter Secure Storage
const _storageToken = FlutterSecureStorage();
/// Await your Future here (This function only called once after the layout is Complete)
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) async {
final storedToken = await _storageToken.read(key: 'jwt');
print(storedToken);
});
}

Related

flutter X firebase: how to correctly initialize fields with data in different branches in firebase realtime database

I have a piece of code trying to initialize the fields with data from two different users in the firebase realtime database, I tried various ways but all of them don't work and the field is not initialized error keeps popping up.
Here is the code:
class _PartnerProfilePageState extends State<PartnerProfilePage> {
final userUID = FirebaseAuth.instance.currentUser!.uid;
final database = FirebaseDatabase.instance.ref();
late final partnerUID;
late final profilePath;
late final partnerName;
late final birthday;
late final en;
#override
initState() {
super.initState();
initInfo();
}
initInfo() async {
database.child(userUID).onValue.listen((event) {
final data = Map<dynamic, dynamic>.from(event.snapshot.value as Map);
setState(() {
partnerUID = data['partner'];
en = data['language'].toString().startsWith('en');
initPartnerInfo();
});
});
}
Future initPartnerInfo() async {
final ppsnapshot =
await database.child(partnerUID).child('profilePath').get();
profilePath = ppsnapshot.value.toString();
final nsnapshot = await database.child(partnerUID).child('username').get();
partnerName = nsnapshot.value.toString();
final bsnapshot = await database.child(partnerUID).child('birtday').get();
birthday = bsnapshot.value.toString();
}
//rest of other unrelated stuff like build down there
}
(My firebase realtime database has no 'user' branch but directly save every user in the root with their userid).
I think there is a problem with the async initializing. The build method can try to build before you initialize the last variable(Because initState method can not async). You can easily check my theory.Delete the 'method call'(the initInfo) inside the initState(). Just create a button in the screen, give it a anonymous async function, inside the function call your init method and try to call your variables like this:
() async{
await initInfo();
print('partnerUID');
}
Easy way to checking out initialization process. Hope it helps.

Why am I getting 'Future<dynamic>' instead of the return value in the function?

I'm trying to get the return value in my function but the output is 'Instance of Future' instead of the value of school field name in the database
#override
void initState() {
userId = _auth.currentUser!.uid;
publisherSchool =
getName(widget.postInfo['publisher-Id'], 'school').toString();
super.initState();
}
Future getName(String publisherUid, String fieldname) async {
DocumentSnapshot publisherSnapshot = await FirebaseFirestore.instance
.collection('users')
.doc(publisherUid)
.get();
print(publisherSnapshot.get(fieldname));
return publisherSnapshot.get(fieldname);
}
but whenever i'm printing the publisherSnapshop.get(fieldname) i'm getting the correct value from the database
There are 2 ways to do it, you can create a Future method and call it inside the initState like below:
#override
void initState() {
initial();
super.initState();
}
Future<void> initial() async {
userId = _auth.currentUser!.uid;
// Remember using `()` to wrap the `await` to get it result
publisherSchool = (await getName(widget.postInfo['publisher-Id'], 'school')).toString();
}
Or you can use .then to call it directly inside the initState:
#override
void initState() {
userId = _auth.currentUser!.uid;
getName(widget.postInfo['publisher-Id'], 'school').then((value) {
publisherSchool = value.toString();
});
super.initState();
}
When you declare the getName() function, specify the return type as Future<String>, and then when you call getName(), you need to await the result e.g. publisherSchool = await getName(widget.postInfo['publisher-Id'], 'school').toString();
The reason why you are not getting the correct response is because whenever you are working with Futures it takes some time to finish and return the results. Meanwhile it is fetching the result you have to make it await so that the program will continue once that future function is complete since await/then is nowhere to be found in your code hence the issues.
To solve this make this change:
Change
publisherSchool =
getName(widget.postInfo['publisher-Id'], 'school').toString();
To
getName(widget.postInfo['publisher-Id'],
'school').then((value){
publisherSchool=value.toString()});

How do I resolve a Future<File>, transform it to File, and ensure it's available before runApp starts?

My apologies for what I assume is rather basic question, but I'm struggling to understand this. I'm aware of What is a Future and how do I use it? but I don't think that applies in this case, or if it does I'm even more confused than I thought!
I'm attempting to use FileOutput in the Logger package to log to device storage. This requires a File object as a parameter.
To obtain the correct path I'm using getApplicationDocumentsDirectory() from the path_provider package. This returns a Future which I can manipulate to a Future in an async function with await.
I'm unclear though how to extract a File from this and how to make sure that these objects are available to the logger before they are needed. Does this need to be done before I call runApp()? I assume I don't need to and shouldn't push async up to the main()?
This is where I am. x2() is a test function I can call successfully out of main() after invoking runApp() and gives me the correct results.
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
print("directory: $directory");
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
print("path: $path");
return File('$path/logger.out');
}
var logger2 = Logger(
output: MultiOutput([
ConsoleOutput(),
FileOutput(
file: _localFile,
overrideExisting: true,
),
]),
printer: PrettyPrinter(
printBox: false,
printTime: true,
),
filter: ProductionFilter(),
);
void x2() async {
var f = await _localFile;
print("_localFile: $f");
}
You should be able to set the Logger before the runApp but you don't need to await for it you can create a logger then access with a provider or a singleton instance and you can simple have a getLogger method that checks if the instance is available and if not calls the await overthere
this way you only call the future once and cache the instance in the class.
You may initialise your variable in x2() function.
Also, don't forget to await x2() as it will assure you that your instance has been created.
class Logger{
Logger({required this.number});
int number;
}
Future<int> get someNumber => Future.delayed(Duration(seconds:1),()=>5);
var logger;
Future<void> x2() async {
logger=Logger(number: await someNumber);
print(logger.number);
}
void main()async{
await x2();
print("main :: "+ logger.number.toString());
}
edit:
Also as #ahmetakil suggested, use provider or inherited widget if you need this instance down the widget tree

How can I return a Future from a stream listen callback?

I have below code in flutter:
getData() {
final linksStream = getLinksStream().listen((String uri) async {
return uri;
});
}
In getData method, I want to return the value of uri which is from a stream listener. Since this value is generated at a later time, I am thinking to response a Future object in getData method. But I don't know how I can pass the uri as the value of Future.
In javascript, I can simply create a promise and resolve the value uri. How can I achieve it in dart?
In your code 'return uri' is not returning from getData but returning from anonymous function which is parameter of listen.
Correct code is like:
Future<String> getData() {
final Completer<String> c = new Completer<String>();
final linksStream = getLinksStream().listen((String uri) {
c.complete(uri);
});
return c.future;
}
Try this
Future<String> getData() async{
final linksStream = await getLinksStream().toList();
return linksStream[0].toString();
}

How to convert Future List instance to List String in flutter

I am saving strings list in shared procedure and fetching that like below
Future<List<String>> getList() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getStringList("key");
}
Issue is that I need to send that list to server but facing issue as I need to convert that future list to simple List
How can I do that? or is there any other way as I need to send list of ids save by user to server.
When you mark a function as async it will return a future.
If you dont wait for the future you will get 'Future instance' this means your future(data) is not available yet.
If you want to wait for the future(data) to be resolved you need to use the await keyword.
So in your case you can create a List<String> myList; then create a function to wait for the future and assign the data to the previous List.
List<String> myList;
void getStringList() async {
var tempList = await getList();
// Or use setState to assign the tempList to myList
myList = tempList;
}
Or use Then:
getList().then(List<String> myList {
// TODO: Send myList to server.
});
Hope this helpe!!
When you work with async data you should "wait" while data will not completely loaded. You can use await word in async methods like that:
foo() async {
final Future<List<dynamic>> futureList = fetchSomeFutureList();
final list = await futureList;
}
or use Future's then() method to delegate some work.
You also can wait for futures in widget tree using FutureBuilder.
Check Dart Docs page for see details.