Dart the operator [] isn't defined for 'Future<Map<String,Object>> Function(String)' - flutter

I'm trying to set up Auth0 for my Flutter app from this demo, but I ran into this issue. I've only been using Flutter for a little while, and I fixed a few errors, but can't fix this one. I checked for other answers here, and tried the documentation too. The error message is;
The operator [] isn't defined for 'Future<Map<String,Object>> Function(String)'
This is the method with the error;
Future<void> loginAction() async {
setState(() {
isBusy = true;
errorMessage = '';
});
try {
final AuthorizationTokenResponse? result =
await appAuth.authorizeAndExchangeCode(
AuthorizationTokenRequest(
AUTH0_CLIENT_ID,
AUTH0_REDIRECT_URI,
issuer: 'https://$AUTH0_DOMAIN',
scopes: ['openid', 'profile', 'offline_access'],
promptValues: ['login']
),
);
Map<String, Object> parseIdToken(String idToken) {
final parts = idToken.split(r'.');
assert(parts.length == 3);
return jsonDecode(
utf8.decode(base64Url.decode(base64Url.normalize(parts[1]))));
}
Future<Map<String, Object>> getUserDetails(String accessToken) async {
final url = 'https://$AUTH0_DOMAIN/userinfo';
final response = await http.get(Uri.parse(
url,
));
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to get user details');
}
}
await secureStorage.write(
key: 'refresh_token', value: result!.refreshToken);
setState(() {
isBusy = false;
isLoggedIn = true;
name = parseIdToken['name'];
picture = getUserDetails['picture'];
});
} catch (e, s) {
print('login error: $e - stack: $s');
setState(() {
isBusy = false;
isLoggedIn = false;
errorMessage = e.toString();
});
}
}
And the lines that are erroring are;
name = parseIdToken['name'];
picture = getUserDetails['picture'];
Can this be fixed?

getUserDetails is a function, and so it doesn't have a [] operator.
Instead, try: var userDetails = await getUserDetails("token..."); picture = userDetails["picture"];

Related

How to request refresh token called once on multiple api calls

I have a function that refreshes a token if the previous API response returns an error code of 1000. However, when multiple API calls are made at the same time, it results in multiple refresh token requests. I want to ensure that the refresh token is only called once.
Here is my code
requestGet(String endPoint, Map<String, dynamic> params, [bool isUsingToken = false]) async {
String sign = getSign(timestamp + jsonEncode(params));
String deviceId = await SharedPrefsService().getDeviceId();
String token = await SharedPrefsService().getToken();;
final response = await httpGet(endPoint, params, sign, token, deviceId, isUsingToken);
dynamic result = response;
var isRenewed = await renewTokenIfNeeded(deviceId, result, endPoint);
if (isRenewed) {
token = await SharedPrefsService().getToken();
final renewedResponse = await httpGet(endPoint, params, sign, token, deviceId, isUsingToken);
result = renewedResponse;
}
return result;
}
Future<bool> renewTokenIfNeeded(String deviceId, result) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool renewingToken = prefs.getBool('renewingToken') ?? false;
if (result['error_code'] == '1000') {
prefs.setBool('renewingToken', true);
try {
if (renewingToken) {
return true;
}
var isRenewed = await requestRenewToken(deviceId);
if (isRenewed) {
prefs.setBool('renewingToken', false);
return true;
}
} finally {
prefs.setBool('renewingToken', false);
}
}
return false;
}
requestRenewToken(String deviceId) async {
var refresh = await AuthenticationService().refreshToken();
if (refresh.errorCode == '9999') {
SharedPrefsService().clearAllData();
return false; // then back to sign in
}
if (refresh.errorCode == '0000') {
SharedPrefsService().saveTokenData(refresh.token!, refresh.userName!, deviceId);
return true;
}
return false;
}
I have tried using synchronized and mutex packages, but they do not seem to work and I prefer to minimize the use of external packages. Can you please suggest a solution? Thank you!

Pull to refresh package in flutter for pagination purpose

Hello Readers I am new in flutter and i want to do pagination, for pagination I am using one package which name is "pull to refersh".
Problems :
I have total 6 post and per page limits are 3.
1)When I reached at the end of list then api will call and set current page variable value is 2 and it will load all data of page 2 as a new list, but i want to merge page 2 data into same list... (Pagination like facebook instagram etc).
2)My another problem is when i pull for refersh, page is refersh perfectly and it will go to the first page but problem is, when i again go at the end of list it shows me no more data(which means page 2 api is not working)
I have one condition like if else:- "hasNextPage" this variable getting from api and the response is 1 or 2, if response is 1 then there further page after current page and if is 0 then there is no page after current page.
I am posting my code and api link also can you please help me.
Method for get data from API
int currentPage = 1;
bool isRefersh = false;
final RefreshController refreshController = RefreshController();
Future<UserPost> getUserPost() async {
var url =
"LINK=$currentPage";
var response = await http.get(Uri.parse(url));
var jsondata = jsonDecode(response.body.toString());
var _apiData = UserPost.fromJson(jsondata);
if (response.statusCode == 200) {
print("******getUserPost API");
print("current page****$currentPage");
print("hasnext page ${_apiData.hasNextPage}");
print(jsondata);
if(isRefersh == true){
setState((){
//currentPage = 1;
isRefersh = false;
});
refreshController.refreshCompleted();
return UserPost.fromJson(jsondata);
}
else{
print("//////////////// has next page");
print(_apiData.hasNextPage.toString());
if(_apiData.hasNextPage == 0){
refreshController.loadNoData();
return UserPost.fromJson(jsondata);
}else{
}
return UserPost.fromJson(jsondata);
}
} else {
return UserPost.fromJson(jsondata);
}
}
Method for pull to Refersh
onRefresh: () async{
await Future.delayed(Duration(milliseconds: 1000));
setState(() {
isRefersh = true;
currentPage = 1;
});
},
Method for Pagination
onLoading: () async {
if(snapshot.data!.hasNextPage == 0){
refreshController.loadNoData();
}else{
setState(() {
currentPage++;
});
await Future.delayed(Duration(milliseconds: 1000));
refreshController.loadComplete();
}
},
I Hope it's help you
try this way :-
final RefreshController refreshController =
RefreshController(initialRefresh: true);
Future<bool> getPassengerData({bool isRefresh = false}) async {
if (isRefresh) {
currentPage = 1;
} else {
if (currentPage >= totalPages) {
refreshController.loadNoData();
return false;
}
}
final Uri uri = Uri.parse(
"api url=$currentPage&size=10");
final response = await http.get(uri);
if (response.statusCode == 200) {
final result = passengersDataFromJson(response.body);
if (isRefresh) {
passengers = result.data;
}else{
passengers.addAll(result.data);
}
currentPage++;
totalPages = result.totalPages;
print(response.body);
setState(() {});
return true;
} else {
return false;
}
}
Method for pull to Refersh
onRefresh: () async {
final result = await getPassengerData(isRefresh: true);
if (result) {
refreshController.refreshCompleted();
} else {
refreshController.refreshFailed();
}
},
Method for onLoading:
onLoading: () async {
final result = await getPassengerData();
if (result) {
refreshController.loadComplete();
} else {
refreshController.loadFailed();
}
},
Try this way.
when you get the response in second page just create new list with previous list.
i.e var newData = [...?dummyData.data, ...?_apiData.data];
than return this same list.
UserPostModel dummyData = UserPostModel();
Future<UserPostModel> getUserPost() async {
var url =
"*****?page=$currentPage";
var response = await http.get(Uri.parse(url));
var jsondata = jsonDecode(response.body.toString());
var _apiData = UserPostModel.fromJson(jsondata);
var newData = [...?dummyData.data, ...?_apiData.data];
//totalPage = _apiData.totalPages as int?;
if (response.statusCode == 200) {
if (isRefersh == true) {
setState(() {
isRefersh = false;
});
refreshController.refreshCompleted();
} else {
if (_apiData.hasNextPage == 0) {
refreshController.loadNoData();
} else {
refreshController.loadComplete();
}
}
dummyData.data = newData;
return dummyData;
} else {
return dummyData;
}
}

Flutter Try correcting the name to one that is defined, or defining the name

I made a function to post some data using http.post, when using the function i pass as parameter the id, now im using this function in other places but it says that it has 1 positional argument but 0 found.
Future<Response?> postLocationId(String id) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? authorization = prefs.getString('authorization');
var url = 'https://dev.api.wurk.skyver.co/api/$id/workingTimeframes';
try {
response = await http.post(
Uri.parse(url),
headers: <String, String>{
'authorization': authorization ?? basicAuth.toString(),
"Content-Type": "application/json"
},
);
} catch (er) {}
return response;
}
void initState() {
super.initState();
futureData = fetchWorkingLocationData();
futureDataForAccount = fetchAccountData();
futureDataForStatus = postLocationId(id); <<<< getting error here
}
ElevatedButton(
onPressed: () async {
final WorkingLocationData locationData =
workingData!.firstWhere(
(some) => some.id ==
chooseLocation);
await postLocationId(locationData.id); <<<
The main problem is because you are passed a parameter (id == null) that doesn’t exist before initState(),
If you try and change the id word for a '1' string, this work, but doesnt the better solution.
You need to modify your logic in your initState() because you pass an argument that is null you have a lot of options:
void initState() {
super.initState();
getLocationData():
}
void getLocationData() async {
final locationData = {...} // This return location data information
postLocationId(locationData.id)
}
or You can omit the end line in your initState()
void initState() {
super.initState();
futureData = fetchWorkingLocationData();
futureDataForAccount = fetchAccountData();
// futureDataForStatus = postLocationId(id);
}
Edit your logic inside your onPressed function to this:
final locationData =
workingData!.firstWhere((some) => some.id == chooseLocation);
if (locationData != null) {
await postLocationId(locationData.id);
setState(() {
_flag = !_flag;
});
} else {
print('locationData is null);
}

How to return catch exception in flutter

I working on error handling of api's. i want if api is crashed then it display a message of "Server is down" something like this, in UI.
I created a class where i'm creating methods of api, here in getBooks method if i modify the api url then it is printing this Exception, and i want it in UI. The problem is getBooks return type is List<Book>> so we can't return this Exception, any solution how to do this?
Exception
E/flutter (12924): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Exception
here is my api code
class BooksApi {
static Future<List<Book>> getBooks(String query) async {
try {
final url = Uri.parse(
'https://gist.githubusercontent.com/JohannesMilke/d53fbbe9a1b7e7ca2645db13b995dc6f/raw/eace0e20f86cdde3352b2d92f699b6e9dedd8c70/books.json');
final response = await http.get(url);
if (response.statusCode == 200) {
final List books = json.decode(response.body);
return books.map((json) => Book.fromJson(json)).where((book) {
final titleLower = book.title.toLowerCase();
final authorLower = book.author.toLowerCase();
final searchLower = query.toLowerCase();
return titleLower.contains(searchLower) ||
authorLower.contains(searchLower);
}).toList();
} else {
throw Exception;
}
} catch (e) {
print("e");
print(e);
}
throw Exception;
}
}
and calling it like
Future init() async {
setState(() {
isLoading = true;
});
var books = await BooksApi.getBooks(query); //this
var response = await obj.getProduct();
print(response);
setState(() => this.books = books);
setState(() {
isLoading = false;
});
}
You could handle errors with then and onError :
await BooksApi.getBooks(query).then((books) async {
setState(() => {
this.books = books;
this.isLoading = false;
})
}, onError: (error) {
// do something with error
});
or a simple try-catch (you can write try-catch clauses the same way you would in synchronous code).
See handling errors.
You can also use catchError id you don't use async/await :
BooksApi.getBooks(query).then((books) {
setState(() => {
this.books = books;
this.isLoading = false;
})
}).catchError((error, stackTrace) {
print("error is: $error");
});
See futures error handling.
Try to wrap 'var books = await BooksApi.getBooks(query)' with try and catch.
...
try {
var books = await BooksApi.getBooks(query);
} catch (e) {
// To do for UI
}
...
For api, you need to make something like this:
APIModel{
final int code;
// or a success flag
// final bool success;
final String message;
final List<Book> data;
APIModel({this.code,this.message,this.data});
}
It means, every api have its own code,message,and data filed.
When you request, you can check your code or success:
var response = await request(params);
isLoading = false;
if(response.code == 0){}
// or
if(response.success){
// do what you want
}
else {
Toast.show(response.message);
}
You can use build_runner and json_serializable.

Display Loading spinner waitint for request to complete while using provider package

I am using a provider package. I want to display a loading spinner while waiting for a request to complete. The pattern below is too verbose. Please help me make it less verbose. Here is my code
class APIService with ChangeNotifier {
// Check for working API backend
bool isWorking = false;
bool isLoading = false;
set _isLoading(bool value) {
isLoading = value; <--
notifyListeners();
}
Future<bool> selectAPI(String input) async {
_isLoading = true; <-- 1
final uri = Uri.tryParse('https://$input$url')!;
final response = await http.get(uri);
if (response.statusCode == 200) {
final body = jsonDecode(response.body) as Map<String, dynamic>;
bool isTrue = body['info']['title'] == 'SamFetch';
_isLoading = false; <-- 2
notifyListeners();
return isWorking = isTrue;
}
_isLoading = false; <-- 3
throw response;
}
}
Here is my UI code
IconButton(
icon: apiService.isLoading
? CircularProgressIndicator()
: Icon(Icons.done),
onPressed: () async {
await addAPI(apiService, cache);
}),
}
Below is addAPI() method
Future<void> addAPI(APIService apiService, Cache cache) async {
if (api != null) {
try {
await apiService.selectAPI(api!);
if (apiService.isWorking) {
await cache.saveAppName(api!);
}
} on SocketException catch (e) {
print(e);
} catch (e) {
await cache.clearCache();
}
}
}
Is setState the final solution?
You can use Future Builder and set your Future Function in future attribute. You can control the visible widget based on the status of your function. So you dont have to use isloading variable.