DioError Http status error [401] while displaying data - flutter

I'm trying to display data from the API to the screen, but I can't do it because of the 401 error, take a look at my code and tell me what exactly is wrong, I think that I wrote the API incorrectly. At the moment I am trying to find the information myself, but I think the problem is in the API, and if so, what exactly is the problem?
Code :
API:
class ApiService {
Dio dio = new Dio();
var token ="token";
var refresh_token ="token";
Future getUserCards() async {
try {
Response resp;
var get_cards = "https://example/api/cards";
resp = await dio.get(get_cards);
dio.options.headers["Authorization"] = "Bearer ${token}";
dio.options.headers['Content-Type'] = "application/json";
var json = (resp.data);
var value = json["id"]["row"]["seq_num"]["text"];
return value;
} catch (e) {
print(e);
}
Future loginUser(String username, String password) async {
var storage = new FlutterSecureStorage();
await storage.write(key: 'JWT', value: token);
var login = "https://example/users/login/";
final data = {"username": username, "password": password};
Response response;
response = await dio.post(login, data: data);
dio.options.headers["Authorization"] = "Bearer ${token}";
dio.options.headers['Content-Type'] = "application/json";
if (response.statusCode == 200) {
Get.to(CardScreen());
return response.data;
} else if (response.statusCode == 401) {
var refreshToken = await dio.post(
"https://example.api/cards/refresh/");
response = await dio.post(refresh_token, data: data);
dio.options.headers["Authorization"] =
"Bearer ${token},'Content-Type': 'application/json','refresh_token': '$refresh_token'";
storage = response.data["token"];
refresh_token = response.data["refresh_token"];
return loginUser("username", "password");
} else
return null;
}
}
UI :
children: [
Expanded(
child: FutureBuilder<dynamic>(
future: ApiService().getUserCards(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Card(
child: Text(snapshot.data[index]),
);
});
}
},
)

Related

Get token auth value to another dart using sharedprefence

how to retrieve token variable from sharedprefence in flutter?
i am very new to implement api for my flutter project because previously I was only told to work on the frontend, i have saved auth token in login and here is my code to store token in sharedprefence
signIn(String email, password) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
Map data = {
'email': email,
'password': password
};
var jsonResponse = null;
var response = await http.post(Uri.parse("/api/login"), body: data);
if(response.statusCode == 200) {
jsonResponse = json.decode(response.body);
if(jsonResponse != null) {
setState(() {
_isLoading = false;
});
sharedPreferences.setString("token", jsonResponse['data']['token']['original']['token']);
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (BuildContext context) => HomePage()), (Route<dynamic> route) => false);
}
}
else {
setState(() {
_isLoading = false;
});
scaffoldMessenger.showSnackBar(SnackBar(content:Text("Mohon cek Email dan Password kembali!", textAlign: TextAlign.center,), backgroundColor: Colors.red,));
}
}
and here is the darts place that I want to call the token for auth in the post method
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:kiriapp/models/angkot.dart';
class AngkotProvider with ChangeNotifier {
late AngkotModel _angkot;
AngkotModel get angkot => _angkot;
set angkot(AngkotModel newAngkot) {
_angkot = newAngkot;
notifyListeners();
}
static Future<AngkotModel?> tambah(
String user_id,
String route_id,
String plat_nomor,
String pajak_tahunan,
String pajak_stnk,
String kir_bulanan) async {
try {
var body = {
'user_id': user_id,
'route_id': route_id,
'plat_nomor': plat_nomor,
'pajak_tahunan': pajak_tahunan,
'pajak_stnk': pajak_stnk,
'kir_bulanan': kir_bulanan,
};
print(body);
var response = await http.post(
Uri.parse('api/create'),
headers: {
'Authorization': 'Bearer $howtocallthetoken?,
},
body: body,
);
print(response.statusCode);
print(response.body);
if (response.statusCode == 201) {
return AngkotModel.fromJson(jsonDecode(response.body));
} else if (response.statusCode == 400) {
return AngkotModel.fromJson(jsonDecode(response.body));
}{
return null;
}
} catch (e) {
print(e);
return null;
}
}
}
thanks
To store something in shared preference we use setString function, just like you did. Now to retrieve it, you should use getString and it will return the token you stored earlier.
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
String accessToken = sharedPreferences.getString("token");
var response = await http.post(
Uri.parse('api/create'),
headers: {
'Authorization': 'Bearer $accessToken',
},
body: body,
);
Don't forget to make the function async, and handle null values as the getString function might return token as null if not stored correctly.

Cannot Get StreamBuilder Data

I am trying to get the updated data from a stream but, even though I get data coming down in my future function, the snapshot.data give me this error:
type '_ControllerStream<dynamic>' is not a subtype of type 'Iterable<dynamic>'
Here is my function and stream:
Future getChat(orderId) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var _token = prefs.getString('token');
print('The Latest Order Token is $_token');
final Map<String, dynamic> body = {
"id": "$orderId",
};
final List _messageData = [];
var url = Uri.parse('$_server/api/driver/get/convo/?access_token=$_token');
await http.post(url, body: body, headers: {
"Content-Type": 'application/x-www-form-urlencoded'
}).then((http.Response response) {
print(response.body);
switch (response.statusCode) {
case 200:
final Map<String, dynamic> responseData = json.decode(response.body);
print("The ${response.body}");
var x = responseData['message_data'].split(",");
print(x);
for (int i = 0; i < x.length; i++) {
_messageData.add(x[i]);
print(x[i]);
}
print(x);
break;
default:
final Map<String, dynamic> responseData = json.decode(response.body);
print(responseData);
return _messageData;
}
return _messageData;
});
}
Stream getChatData(Duration refreshTime, id) async* {
while (true) {
await Future.delayed(refreshTime);
yield getChat(id).asStream();
}
}
I get this in the data response:
"message_data": ""11-12-21:09:01:14AM - Billy Fakename: fire
test,11-12-21:09:01:30AM - Test TEster: ewserasece,""
My stream builder is:
Stream _chatStream;
#override
void initState() {
_chatStream = getChatData(Duration(seconds: 3), orderid);
super.initState();
}
StreamBuilder(
stream: _chatStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
final messages = snapshot.data;
List<MessageBubble> messageWidgets = [];
for (var message in messages) {
final msgText = message;
final msgSender = message;
// final msgSenderEmail = message.data['senderemail'];
final currentUser = "loggedInUser.displayName";
// print('MSG'+msgSender + ' CURR'+currentUser);
final msgBubble = MessageBubble(
msgText: msgText,
msgSender: msgSender,
user: currentUser == msgSender);
messageWidgets.add(msgBubble);
}
return Expanded(
child: ListView(
reverse: true,
padding:
EdgeInsets.symmetric(vertical: 15, horizontal: 10),
children: messageWidgets,
),
);
} else {
return Center();
}
},
),
But, I get this error: type '_ControllerStream' is not a subtype of type 'Iterable' or the snapshot will be null.
How do I get the information that shows up in the future function, show up in the stream?
Could you show us where you define _chatStream ?
Your StreamBuilder uses _chatStream but you only showed us where you define the method
Future getChat(orderId)
and the method
Stream getChatData(Duration refreshTime, id)
where you create a stream that you do not use in the code you've shared.
Did you want to use getChatData in your StreamBuilder?
Did I miss something?

Get object data from future flutter

I have to check the email and password with the rest API that is going well. The problem is my future is returning a class object that has a token. I need that that for other screen and after login navigate to other screens.
Future<LoginResponse> createLoginState(String email, String password) async {
final http.Response response = await http.post(
'https://www.polestarkw.com/api/login',
headers: <String, String>{
'Accept': 'application/json',
//'content-type' : 'application/json'
},
body: {
"email":email ,
"password":password ,
});
if (response.statusCode == 200) {
// print(response.body);
LoginResponse loginResponse=LoginResponse.fromJson(json.decode(response.body)) ;
return loginResponse;
} else {
throw Exception('Failed to create album.');
}
}
class LoginResponse {
Object _data;
String token_type;
String expires_in;
String access_token;
String refresh_token;
LoginResponse(
{this.token_type, this.expires_in, this.access_token, this.refresh_token});
LoginResponse.fromJson(Map<String, dynamic> json) {
token_type = json['token_type'];
expires_in = json['expires_in'];
access_token = json['access_token'];
refresh_token = json['refresh_token'];
}
}
I need this loginResponse object on my other page. Here is using a future instance.
_futureJwt = createLoginState(emailController.text, pwdController.text);
how to get data from _futureJwt.
The code should go something like this
Future<LoginResponse> createLoginState(String email, String password) async {
final http.Response response = await http.post(
'https://www.polestarkw.com/api/login',
headers: <String, String>{
'Accept': 'application/json',
//'content-type' : 'application/json'
},
body: {
"email":email ,
"password":password ,
});
if (response.statusCode == 200) {
// print(response.body);
LoginResponse loginResponse=fromJson(json.decode(response.body)) ;
return loginResponse;
} else {
throw Exception('Failed to create album.');
}
}
LoginResponse fromJson(Map<String, dynamic> json) {
token_type = json['token_type'];
expires_in = json['expires_in'];
access_token = json['access_token'];
refresh_token = json['refresh_token'];
return LoginResponse(token_type,expires_in,access_token,refresh_token);
}
class LoginResponse {
Object _data;
String token_type;
String expires_in;
String access_token;
String refresh_token;
LoginResponse(
{this.token_type, this.expires_in, this.access_token, this.refresh_token});
}
The above code should work in the way u have written it too but I am not sure since I use this way
Then you can use this like
LoginResponse _futureJwt = await createLoginState(emailController.text, pwdController.text);
var token_type = _futureJwt.token_type;
var expires_in = _futureJwt.expires_in;
var access_token = _futureJwt.access_token;
var refresh_token = _futureJwt.refresh_token;
As simple as that. If you do not want to wait for the Future, you can use .then like this
createLoginState(emailController.text, pwdController.text).then((_futureJwt){
var token_type = _futureJwt.token_type;
var expires_in = _futureJwt.expires_in;
var access_token = _futureJwt.access_token;
var refresh_token = _futureJwt.refresh_token;
});
Use FutureBuilder.
Then you can use AsyncSnapshot to access hasData(), hasError() and get the data like so:
#override
Widget build(BuildContext context) {
Future<String> exampleFuture = Future.delayed(Duration(seconds: 2), "value")
return FutureBuilder(
future: exampleFuture,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasError) {
return Text("error");
} else if (!snapshot.hasData) {
return Text("loading");
} else {
return Text(snapshot.data);
}
});
}

How to show error if server is unreachable flutter

Am still pretty new to flutter. I have a network call to be executed. But before doing that I need to check whether the device have internet connectivity and that the server is api server is reachable. I have managed to check if the internet connectivity is available, but cant show an when server is not reachable
This is what i have done so far:
login(username, password) async {
final String url = "http://10.0.2.2:8080/api/auth/signin"; // iOS
var responseJson;
try {
final response= await http.post(
url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'username': username,
'password': password,
}),
);
responseJson = _response(response);
} on SocketException {
throw FetchDataException('No Internet connection');
}
print(responseJson);
SharedPreferences prefs = await SharedPreferences.getInstance();
var parse = jsonDecode(responseJson.body);
await prefs.setString('username', parse["username"]);
await prefs.setString('message', parse["message"]);
await prefs.setString('accessToken', parse["accessToken"]);
return responseJson;
}
dynamic _response(http.Response response) {
switch (response.statusCode) {
case 200:
var responseJson = json.decode(response.body.toString());
print(responseJson);
return responseJson;
case 400:
throw BadRequestException(response.body.toString());
case 401:
case 403:
throw UnauthorisedException(response.body.toString());
case 500:
throw FetchDataException(
'Error occured while Communication with Server with StatusCode : ${response
.statusCode}');
default:
throw FetchDataException(
'Error occured while Communication with Server with StatusCode : ${response
.statusCode}');
}
}
My login button function
RoundedButton(
text: "LOGIN",
press: () async {
if (_formKey.currentState.validate()) {
progressDialog.show();
await login(
username,
password,
);
SharedPreferences prefs =
await SharedPreferences.getInstance();
String token = prefs.getString("accessToken");
print(token);
if (token == null) {
progressDialog.hide();
showAlertsDialog(context);
} else {
showAlertzDialog(context);
}
}
},
)
Whenever I switch of the server and click on login, the app is stuck a progress bar showing signing in. How can I display an alert that there is no connection to the server?
This is how you can manage your API call.
Future<dynamic> requestGET({String url}) async {
try {
final response = await http.get(Uri.parse(url));
switch (response.statusCode) {
case 200:
case 201:
final result = jsonDecode(response.body);
final jsonResponse = {'success': true, 'response': result};
return jsonResponse;
case 400:
final result = jsonDecode(response.body);
final jsonResponse = {'success': false, 'response': result};
return jsonResponse;
case 401:
final jsonResponse = {
'success': false,
'response': ConstantUtil.UNAUTHORIZED
};
return jsonResponse;
case 500:
case 501:
case 502:
final jsonResponse = {
'success': false,
'response': ConstantUtil.SOMETHING_WRONG
};
return jsonResponse;
default:
final jsonResponse = {
'success': false,
'response': ConstantUtil.SOMETHING_WRONG
};
return jsonResponse;
}
} on SocketException {
final jsonResponse = {
'success': false,
'response': ConstantUtil.NO_INTERNET
};
return jsonResponse;
} on FormatException {
final jsonResponse = {
'success': false,
'response': ConstantUtil.BAD_RESPONSE
};
return jsonResponse;
} on HttpException {
final jsonResponse = {
'success': false,
'response': ConstantUtil.SOMETHING_WRONG //Server not responding
};
return jsonResponse;
}
}
Call this function and use response I'm calling it in init method of statefulWidget.
#override
void initState() {
// TODO: implement initState
super.initState();
final result = await requestGET('google.com');
if (result['success'] == false) {
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Error"),
content: Text(result['response']),
actions: [
FlatButton(
child: Text("OK"),
onPressed: () {
Navigator.pop(context);
},
),
],
);
;
},
);
}
}
I think you can check the response code from the api call using http code request from this link http status code
as you can check the response from json like this:
Future<String> checkServerResponse() await
{
http.Response response =
await http.get('server_link'):
print(response.statusCode);
}
now as you can see the response code of the server based on http status code.

How to do stream builder to get data from bloc in flutter

I am new in BLOC and I am trying to read respond from api.. but whenever I call stream builder... my widget always stops in wait... here is my code
here is api provider file
class Provider {
final _url = '...';
Future<List<LoginRespon>> login(a, b) async {
List<LoginRespon> datalogin = [];
try {
bool trustSelfSigned = true;
HttpClient httpClient = new HttpClient()
..badCertificateCallback =
((X509Certificate cert, String host, int port) =>
trustSelfSigned);
IOClient http = new IOClient(httpClient);
final response = await http.post(_url,
headers: {
HttpHeaders.contentTypeHeader: 'application/json',
},
body: json.encode({
"aa": a,
"bb": b,
}));
Map<String, dynamic> responseJson = json.decode(response.body);
if (responseJson["status"] == "200") {
datalogin.add(LoginRespon(
status: responseJson['status'],
data: Data(
name: responseJson['data']['name'],
position: responseJson['data']['pos'])));
return datalogin;
} else {
print("ppp");
}
} on Exception {
rethrow;
}
return datalogin;
}
}
and here is for stream builder
isClick
? StreamBuilder(
stream: bloc.login(),
builder: (context, snapshot) {
if (snapshot.hasData) {
print(snapshot.data);
return Text("success");
} else if (snapshot.hasError) {
return Text(
snapshot.error.toString());
}
return Text("wait..");
},
)
: FlatButton(
child: Text("Login"),
onPressed: () {
setState(() {
isClick = true;
});
},
),
is there a way so that I can call print(snapshot.data) inside if (snapshot.hasData)
You need to pass argument which required in method otherwise it will not successfully responce (200) and it will throw error.