This question already has answers here:
What is a Future and how do I use it?
(6 answers)
Closed 9 months ago.
I am making an api call to receive data. When I make the call, I get back an instance of future. Why does this still return an instance of future instead of waiting for the data?
Here is my networking file
class NetworkHelper {
NetworkHelper(this.url);
final String url;
Future getSecureData(String token) async {
http.Response response = await http.post(
Uri.parse(url),
headers: {
HttpHeaders.authorizationHeader: token,
},
);
var data = response;
return data;
}
Here is my Account Settings file
class AccountSettings extends StatefulWidget {
const AccountSettings({Key? key}) : super(key: key);
static const String id = 'account_settings';
#override
State<AccountSettings> createState() => _AccountSettingsState();
}
class _AccountSettingsState extends State<AccountSettings> {
var userData;
#override
void initState() {
super.initState();
getUserData();
}
Future getUserData() async {
var token = await SecureStorage.getAccessToken();
var jwttoken = 'JWT ' + token!;
NetworkHelper networkHelper =
NetworkHelper('http://localhost:8000/auth/users/me');
userData = await networkHelper.getSecureData(jwttoken);
}
#override
Widget build(BuildContext context) {
print(userData);
return Scaffold(
Future getSecureData(String token) async {
http.Response response = await http.post(
Uri.parse(url),
headers: {
HttpHeaders.authorizationHeader: token,
},
);
var data = response; // This might be off.
var data = jsonDecode(response.body) // Try replacing with this.
return data;
}
Related
I'm trying to make a currency converter app in flutter.
The process I've planned was..
At the initState, get current currency data from API
Get currency data and assign it to 'currentCur'
Calculate converted currency value with 'currentCur' and display the value
But, I got an error that since 'currentCur' is Instance of 'Future<dynamic' so it can't calculated cause it is not subtype of 'num'
How can I get just value from Future in initState?
class _ConverterWidgetState extends State<ConverterWidget> {
late final TextEditingController _current;
late final currentCur;
late final currency;
fetchData() async {
try {
http.Response response = await http.get(
Uri.parse(
'https://quotation-api-cdn.dunamu.com/v1/forex/recent?codes=FRX.KRWUSD',
),
);
String jsonData = response.body;
var basePrice = jsonDecode(jsonData)[0]['basePrice'];
devtools.log(basePrice.toString());
return basePrice;
} catch (e) {
devtools.log(e.toString());
}
}
getCurrency(a) async {
return await Future.value(a);
}
#override
void initState() {
super.initState();
_current = TextEditingController(text: 1000.toString());
currentCur = fetchData();
devtools.log(currentCur.toString());
}
Specify the function is going to return a value with the "Future" keyWord
Future<num> fetchData() async {
var basePrice = 0;
try {
http.Response response = await http.get(
Uri.parse(
'https://quotation-api-cdn.dunamu.com/v1/forex/recent?codes=FRX.KRWUSD',
),
);
String jsonData = response.body;
basePrice = jsonDecode(jsonData)[0]['basePrice'];
devtools.log(basePrice.toString());
} catch (e) {
devtools.log(e.toString());
}
return basePrice;
}
void updateCurrentCur ()async{
var basePrice = await fetchData();
setState(() {
currentCur = basePrice;
});
}
#override
void initState() {
super.initState();
updateCurrentCur()
}
I am retrying my api call if get 401 response but when Retrying I am ending with an following exception
following is my code for retrying multipart I had used http_interceptor package for retrying Api Calls
interceptor.dart
class AuthorizationInterceptor extends InterceptorContract {
#override
Future<BaseRequest> interceptRequest({required BaseRequest request}) async {
final prefs = await SharedPreferences.getInstance();
final extractData =
json.decode(prefs.getString('userData')!) as Map<String, dynamic>;
final Map<String, String> headers = Map.from(request.headers);
headers['Authorization'] = await extractData['accessToken'];
print(
'this is from AuthorizationInterceptor: ${extractData['accessToken']}');
// TODO: implement interceptRequest
return request.copyWith(
headers: headers,
);
}
retry.dart
class ExpiredTokenRetryPolicy extends RetryPolicy {
BuildContext context;
ExpiredTokenRetryPolicy(this.context);
#override
// TODO: implement maxRetryAttempts
int get maxRetryAttempts => 2;
#override
Future<bool> shouldAttemptRetryOnResponse(BaseResponse response) async {
if (response.statusCode == 401) {
print('retry token started');
//perform token refresh,get the new token and update it in the secure storage
await Provider.of<Auth>(context, listen: false).restoreAccessToken();
return true;
}
return false;
}
I am using interceptors in my widget following is my code where I am using interceptors and using retry policy
#override
Widget build(BuildContext context) {
var flutterFunctions = Provider.of<FlutterFunctions>(context);
// print('this is from insert package${token.token}');
ApiCalls repository = ApiCalls(
client: InterceptedClient.build(
retryPolicy: ExpiredTokenRetryPolicy(context),
interceptors: [
AuthorizationInterceptor(),
],
),
);
following is my restore access token method
Future<void> restoreAccessToken() async {
print('restoreAccessToken started');
//print(token);
final url = '${Ninecabsapi().urlHost}${Ninecabsapi().login}/$sessionId';
var response = await http.patch(
Uri.parse(url),
headers: {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': accessToken!
},
body: json.encode(
{"refresh_token": refreshtoken},
),
);
var userDetails = json.decode(response.body);
if (response.statusCode == 401) {
print(userDetails['messages']);
}
sessionId = userDetails['data']['session_id'];
accessToken = userDetails['data']['access_token'];
accessTokenExpiryDate = DateTime.now().add(
Duration(seconds: userDetails['data']['access_token_expiry']),
);
refreshToken = userDetails['data']['refresh_token'];
refreshTokenExpiryDate = DateTime.now().add(
Duration(seconds: userDetails['data']['refresh_token_expiry']),
);
final userData = json.encode({
'sessionId': sessionId,
'refreshToken': refreshToken,
'refreshExpiry': refreshTokenExpiryDate!.toIso8601String(),
'accessToken': accessToken,
'accessTokenExpiry': accessTokenExpiryDate!.toIso8601String()
});
//print(userDetails);
notifyListeners();
final prefs = await SharedPreferences.getInstance();
prefs.setString('userData', userData);
print("this is from restoreAcessToken :$userDetails");
final extractData =
json.decode(prefs.getString('userData')!) as Map<String, dynamic>;
print('restore access token: ${extractData['accessToken']}');
reset();
}
As a rule. You must NOT write using the same Stream/MultipartFile more than once. If you need to retry sending to the same destination, you have to use a new MultipartFile each time you retry.
This is a login, that catch user data and write in the other pages, like his name, etc
I set sharedPreference here:
Future<bool> login() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
SharedPreferences nome = await SharedPreferences.getInstance();
var email = _emailController.text;
var senha = _senhaController.text;
var auth = 'Basic ' + base64Encode(utf8.encode('$email:$senha'));
var url = Uri.parse("http://177.70.102.109:3005/autenticacao");
var resposta = await http.get(
url,
headers: (<String, String>{'authorization': auth}),
);
// List campos = [];
if (resposta.statusCode == 200) {
await sharedPreferences.setString(
'token', "Token ${jsonDecode(resposta.body)['token']}");
await nome.setString(
'nome', "${jsonDecode(resposta.body)['result'][0]['nome']}");
print(nome);
return true;
} else {
return false;
}
}
And i want to receive and pass the 'nome' to a TextWidget in another class.
In the other page you can write something like that:
class ExamplePage extends StatefulWidget {
const ExamplePage({Key? key}) : super(key: key);
#override
State<ExamplePage> createState() => _ExamplePageState();
}
class _ExamplePageState extends State<ExamplePage> {
final _controller = TextEditingController();
#override
void initState() {
initNome();
super.initState();
}
Future<void> initNome() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
String _nome = sharedPreferences.getString("nome", "");
_controller.text = _nome;
}
#override
Widget build(BuildContext context) {
return Text(_controller.text)
}
}
To read the value in some other widget you can use
getString https://pub.dev/documentation/shared_preferences/latest/shared_preferences/SharedPreferences/getString.html
Implementation would be similar to this:
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
Text(sharedPreferences.getString("nome");
See this post for example:
Flutter Shared Preference in Text Widget
I'm new to the GetX framework and am trying the call the controller class function from the view using the Elevated button but it causes an error.
The method 'getContacts' isn't defined for the type 'Type'.
this is my contoller class
class ContactController extends GetxController {
var id;
#override
void onInit() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
id = preferences.getString('id');
print(id);
}
getContact() async {
// SERVER API URL
Uri url = Uri.parse('http://localhost:8000/get-contacts');
// Store all data with Param Name.
var data = {'sender': id};
//json encode
String? body = json.encode(data);
// Starting Web API Call.
var response = await http.post(url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: body);
// Getting Server response into variable.
var contact = jsonDecode(response.body);
// If Web call Success than Hide the CircularProgressIndicator.
if (response.statusCode == 200) {
print(contact);
} else {
print("error");
}
}
}
this is my view class
class Contacts extends StatefulWidget {
const Contacts({Key? key}) : super(key: key);
#override
State<Contacts> createState() => _ContactsState();
}
class _ContactsState extends State<Contacts> {
final contactController = Get.put((ContactController));
#override
Widget build(BuildContext context) {
return Container(
child: ElevatedButton(
onPressed:()=>contactController.getContacts(),
child: Text("GET CONTACT")));
}
}
I can't able to call the function inside the elevated Button
onPressed:()=>contactController.getContacts(),
any one help me to solve this issue
Try changing it
final contactController = Get.put(ContactController());
I am trying to import an asynchronous function in Flutter to handle securely storing user data. The problem is I keep getting the following error:
packages/authentication_repository/lib/src/authentication_repository.dart:64:15:
Error: Method not found: 'set'. await SecureStorageService.set(
^^^
Here is my code:
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class SecureStorageService {
static SecureStorageService _intance;
FlutterSecureStorage flutterSecureStorage;
SecureStorageService._internal() {
this.flutterSecureStorage = new FlutterSecureStorage();
}
static Future<SecureStorageService> getInstance() async {
if (_intance == null) {
_intance = SecureStorageService._internal();
}
return _intance;
}
Future<void> set(String key, String value) async {
await this.flutterSecureStorage.write(key: key, value: value);
}
Future<String> get(String key) async {
return await this.flutterSecureStorage.read(key: key);
}
Future<void> clear() async {
await this.flutterSecureStorage.deleteAll();
}
}
And then I import the code like follows:
import 'package:crowdplan_flutter/storage_util.dart';
...
class AuthenticationRepository {
final _controller = StreamController<AuthenticationStatus>();
final secureStorage = SecureStorageService.getInstance();
...
try {
final response = await http.post(
url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'email': email,
'password': password,
'client_id': clientId,
}),
);
if (response.statusCode == 200) {
print(response.body);
print(json.decode(response.body)['access_token']);
print(json.decode(response.body)['refresh_token']);
await secureStorage.set(
key: 'access_token',
value: json.decode(response.body)['access_token']);
await secureStorage.set(
key: 'refresh_token',
value: json.decode(response.body)['refresh_token']);
await secureStorage.set(
key: 'user_id', value: json.decode(response.body)['user_id']);
_controller.add(AuthenticationStatus.authenticated);
}
} catch (error, stacktrace) {
print('Exception occurred: $error stackTrace: $stacktrace');
}
}
My Singleton is initiated in my main.dart file like so.
void main() async {
await SecureStorageService.getInstance();
runApp(App(
authenticationRepository: AuthenticationRepository(),
userRepository: UserRepository(),
));
}
I am new to Flutter so this might be a new noob error.
The set method isn't static and can't be accessed with SecureStorageService.set
Future<void> set(String key, String value) async {
await this.flutterSecureStorage.write(key: key, value: value);
}
I see in the 2nd code snippet that you've assigned the singleton to secureStorage.
Did you mean to access it with something like?:
secureStorage.set()
Part 2 - Code Example
Perhaps the async getInstance() in the singleton class is tripping you up. It doesn't need to be async (nor should it be). (In some cases you may want an async initializer instead of a constructor. See the bottom of the Example code here for a use-case.)
SecureStorageService (the singleton) gets instantiated in your main() method so inside AuthenticationRepository it'll use that same instance and be ready to use.
class AuthenticationRepository {
final secureStorage = SecureStorageService.getInstance;
// ↑ will get the same instance created in main()
The code sample in the question doesn't specify where/when the http.post method is being called, but I'm guessing it's an initialization / setup for AuthenticationRepository so I've mocked up an initStorage() method inside it.
This initStorage() call will use the SecureStorageService singleton, with a call to its secureStorage.set() method.
Hopefully this example helps you spot a difference between our code samples to figure out what's going wrong.
import 'package:flutter/material.dart';
/// Mocking FlutterSecureStorage
/// Note that the actual package FlutterSecureStorage does not have an async
/// constructor nor initializer
class FlutterSecureStorage {
Map<String,String> data = {};
Future<void> write({String key, String value}) async {
data[key] = value;
}
Future<String> read({String key}) async {
print('FSS read - returning value: ${data[key]}');
return data[key];
}
}
class SecureStorageService {
/// for singleton ↓ instance should be final and uses private constructor
static final SecureStorageService _instance = SecureStorageService._internal();
FlutterSecureStorage flutterSecureStorage;
/// Private constructor, not async
SecureStorageService._internal() {
flutterSecureStorage = FlutterSecureStorage();
}
/// This doesn't need to be async. FlutterSecureStorage (FSS) doesn't have an async initializer
/// and constructors are generally never async
/*static Future<SecureStorageService> getInstance() async {
if (_instance == null) {
_instance = SecureStorageService._internal();
}
return _instance;
}*/
/// static singleton instance getter, not async
static SecureStorageService get getInstance => _instance;
/// don't need "this" keyword & could use FSS methods directly, but leaving as is
Future<void> set({String key, String value}) async {
await flutterSecureStorage.write(key: key, value: value);
}
Future<String> get({String key}) async {
return flutterSecureStorage.read(key: key);
}
}
class Response {
int statusCode = 200;
Response() {
print('http post completed');
}
}
class AuthenticationRepository {
final secureStorage = SecureStorageService.getInstance;
String accessToken = '';
/// Encapsulates the slow init of a http.post call. When all ready, returns
/// the AuthenticationRepository in a usable state
Future<AuthenticationRepository> initStorage() async {
try {
// Mock http response
final response = await Future.delayed(Duration(seconds: 2), () => Response());
if (response.statusCode == 200) {
accessToken = 'access_token from http value';
await secureStorage.set(
key: 'access_token',
value: accessToken);
print('access token set');
// snipped other calls for brevity
}
} catch (error, stacktrace) {
print('Exception occurred: $error stackTrace: $stacktrace');
}
return this;
}
}
class SingleStoragePage extends StatefulWidget {
#override
_SingleStoragePageState createState() => _SingleStoragePageState();
}
class _SingleStoragePageState extends State<SingleStoragePage> {
Future<AuthenticationRepository> authRepo;
#override
void initState() {
super.initState();
authRepo = AuthenticationRepository().initStorage();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Singleton Storage'),
),
body: Center(
child: FutureBuilder<AuthenticationRepository>(
future: authRepo,
builder: (context, snapshot) {
print('FutureBuilder re/building');
if (snapshot.hasData) {
return Text('Access token: ${snapshot.data.accessToken}');
}
return Text('loading...');
},
),
),
);
}
}