call parameter function to get data in flutter - flutter

I'm learning and trying to add parameters when calling parameters in functions when getting data from the API, but I'm a bit confused about how I call them in widgets.
static Future<Map<String, DataKuliahModel>> getDataKuliah(String smt) async {
String url = Constant.baseURL;
String token = await UtilSharedPreferences.getToken();
await Future.delayed(const Duration(milliseconds: 1000));
// String responseJson = await rootBundle.loadString('assets/1.json');
Map<String, DataKuliahModel> finalResult = {};
final response = await http.get(
Uri.parse(
'$url/auth/mhs_siakad/perwalian/get_paket',
),
headers: {
'Authorization': 'Bearer $token',
},
);
final result = jsonDecode(response.body)['data'] as Map<String, dynamic>;
result.forEach((key, value) {
DataKuliahModel dataKuliah = DataKuliahModel.fromMap(value);
finalResult.addAll({
key: dataKuliah,
});
});
return finalResult;
}
and I want to call him here

When you declare a function with positional parameters you need to provide those parameters when you call that function.
import 'package:flutter/material.dart';
class Services {
static Future<String> greeting(String name) async {
/// this function doesn't need to be Future
/// but when you call API to get some data it should be a Future
return 'Hello $name';
}
}
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
#override
Widget build(BuildContext context) {
return FutureBuilder(
/// pass positional parameter to [greeting] here
future: Services.greeting('Dash'),
builder: (context, AsyncSnapshot<String> snapshot) {
return Center(
child: Text(snapshot.data ?? 'default'),
);
},
);
}
}
Result: Hello Dash
In your case smt seems to be an int not a String
and you have to pass it as query parameter to http request as follows
static Future<Map<String, DataKuliahModel>> getDataKuliah(int smt) async {
String url = Constant.baseURL;
String token = await UtilSharedPreferences.getToken();
await Future.delayed(const Duration(milliseconds: 1000));
// String responseJson = await rootBundle.loadString('assets/1.json');
Map<String, DataKuliahModel> finalResult = {};
final response = await http.get(
// Uri.parse(
// '$url/auth/mhs_siakad/perwalian/get_paket',
// ),
Uri.http(url, '/auth/mhs_siakad/perwalian/get_paket',
{'smt':smt}),
headers: {
'Authorization': 'Bearer $token',
},
);
final result = jsonDecode(response.body)['data'] as Map<String, dynamic>;
result.forEach((key, value) {
DataKuliahModel dataKuliah = DataKuliahModel.fromMap(value);
finalResult.addAll({
key: dataKuliah,
});
});
return finalResult;
}

Have you looked at the Uri replace method?
You can do the following:
Uri.parse('$url/auth/mhs_siakad/perwalian/get_paket').replace(queryParameters:{ "smt":"$smt"});
Update on FutureBuilder:
// Put this outside your build function
Future<Map<String, DataKuliahModel>> DK ;
// Put this in your initState if you want the future to run on page load or use it for events like onTap
DK = Service.getDataKuliah(<PARAM>);
// This is in your build method
FutureBuilder(
future:DK,
builder: (context, snapshot) {
// add wigets to display results here
}
)

Related

How can I use Future<> riverpod example on Flutter?

#riverpod
Future<String> boredSuggestion(BoredSuggestionRef ref) async {
final response = await http.get(
Uri.https('https://www.boredapi.com/api/activity'),
);
final json = jsonDecode(response.body) as Map;
return json['activity']! as String;
}
class Home extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
final boredSuggestion = ref.watch(boredSuggestionProvider);
// Perform a switch-case on the result to handle loading/error states
return boredSuggestion.when(
loading: () => const Text('loading'),
error: (error, stackTrace) => Text('error: $error'),
data: (data) => Text(data),
);
}
}
I am trying to copy the simple example from Riverpod homepage. However, I get
Undefined class 'BoredSuggestionRef'. Try changing the name to the name of an existing class, or creating a class with the name 'BoredSuggestionRef'.
Error and I am trying to build it.
final testingP = StateProvider<Future<String>>((ref) async {
final response = await http.get(
Uri.https('https://www.boredapi.com/api/activity'),
);
final json = jsonDecode(response.body) as Map;
return json['activity']! as String;
});
#override
Widget build(BuildContext context, WidgetRef ref) {
final testing = ref.watch(testingP);
return testing.when(
loading: () => const Text('loading'),
error: (error, stackTrace) => Text('error: $error'),
data: (data) => Text(data),
);
}
And, in this example, I get The method 'when' isn't defined for the type 'Future'. Try correcting the name to the name of an existing method, or defining a method named 'when' error.
How can I use that example in this case?
Try the following code:
final testingP = FutureProvider.autoDispose<String>((ref) async {
final response = await http.get(
Uri.https('https://www.boredapi.com/api/activity'),
);
final json = jsonDecode(response.body) as Map;
return json['activity']! as String;
});
The method When is not defined for the type StateProvider, try using a Future provider since you're awaiting a future response.The code would look like:
final testingP = FutureProvider<String>((ref) async {
final response = await http.get(
Uri.https('https://www.boredapi.com/api/activity'),
);
final json = jsonDecode(response.body) as Map;
return json['activity']! as String;
});

How to properly make a api request in flutter?

Referring to this article
https://medium.com/solidmvp-africa/making-your-api-calls-in-flutter-the-right-way-f0a03e35b4b1
I was trying to call API from a flutter app. But to make it the right way, I was looking for a complete example and came here. My question is why do I need to create an ApiBaseHelper class then RepositoryClass then all other formalities to call an API. Why can't I use FutureBuilder and a simple async function associated with the API like this:
class Networking {
static const BASE_URL = 'https://example.com';
static Future<dynamic> getProductById({
required String? token,
required String? productId,
}) async {
final url = Uri.parse('$BASE_URL/products/$productId');
final accessToken = 'Bearer $token';
Map<String, String> requestHeaders = {
'Authorization': accessToken,
'Content-Type': 'application/json'
};
try {
final response = await http.get(
url,
headers: requestHeaders,
);
if (response.statusCode != 200) {
throw Exception('Error fetching data.');
}
final responseJSON = json.decode(response.body);
if (responseJSON['error'] != null) {
return throw Exception(responseJSON['error']);
}
final product = Product.fromJson(responseJSON);
return product;
} catch (e) {
throw Exception(e.toString());
}
}
}
And then calling it from a FutureBuilder like this:
FutureBuilder(
future: Networking.getProductById(token, id),
builder: (context, snapshot) {
// rest of the code
}
)
Can anyone tell me what is the most convenient and widely used way to call an API?

how to resend multipart request

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.

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?

Running Multiple Async Requests at Once in Dart. Proper way

I am trying to work with concurrency in Flutter, so I have three get-request from server and I need to get values from them at the same time. Each request was parsed into a model. Now I'm trying to figure out how I can combine all the models into one list with three get-request and run this the final list in ListView.builder. Also my task is quite hard for such noobie as I am, beacuse besides parsed concurrently all three lists I need to filter them out, because as said in API all requests are nested and depends on id. How can i resolve this?
This is my models:
ScheduleVariants{
final int mrId;
final int mvId;
ScheduleVariants({this.mrId, this.mvId});
}
FlightCard{
final int mvId;
final int stId;
FlightCard({this.mrId, this.stId});
}
Stop{
final int stId;
Stop({this.stId})
}
I need to get final values from Stop models. As you can see all models have nested stucture and I can't avoid this.
Now I am trying to make concurrent call like this:
class Dire extends StatefulWidget {
final int mrId;
final int mvId;
final int stId;
const Dire({Key key, this.mrId, this.mvId, this.stId}) : super(key: key);
#override
_DireState createState() => _DireState();
}
class _DireState extends State<Dire> {
#override
void initState() {
fetchData();
super.initState();
stops.where((element) => element.stId == widget.stId).toList();
card.where((element) => element.mvId == widget.mvId).toList();
sheduler.where((element) => element.mrId == widget.mrId).toList();
}
List<ScheduleVariants> sheduler;
List<FlightCard> card;
List<Stop> stops;
Future fetchData() async {
String username = '';
String password = '';
String basicAuth =
'Basic ' + base64Encode(utf8.encode('$username:$password'));
print(basicAuth);
final result = await Future.wait([
http.get(
Uri.parse(
"http://mysecurelink/getMarshVariants.php?fmt=json"),
headers: <String, String>{'authorization': basicAuth}),
http.get(
Uri.parse(
"http://mysecurelink/getFlightCard.php?fmt=json&mv_id"),
headers: <String, String>{'authorization': basicAuth}),
http.get(
Uri.parse(
"http://mysecurelink/getStops.php?fmt=json"),
headers: <String, String>{'authorization': basicAuth}),
]);
setState(() {
sheduler = json.decode(result[0].body) as List;
card = json.decode(result[1].body) as List;
stops = json.decode(result[2].body) as List;
});
}
#override
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: FutureBuilder(
future: fetchData(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: stops.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(stops[index].stTitle),
);
});
} else {
return CircularProgressIndicator();
}
}));
}
}
At the end of the main task is to run three parallel request filtered by ID and get data from the Stops model. How can you do it right?
I am noob and don't get how properly to do it and I will really glad if someone help me to figure this task out.
I am going to make some assumptions, because there is not enough information:
Dire is a combination of the three classes ScheduleVariants, FlightCard and Stop where ScheduleVariants.mvId == FlightCard.mvId and FlightCard.stId == Stop.stId.
All three APIs will return a list as their response.
All ScheduleVariants have unique mvId, all FlightCards have unique mvId and stId, and all Stops have unique stId.
There is nothing wrong with the way you execute multiple asynchronous requests. Future.wait in this case takes in a list of futures and returns a list of responses. The problem you are facing is just that you do not know how to merge the responses from the three API requests.
You seem to also be mixing up the use of state with the use of futures. At least in the code snippet you provided, it does not seem like you ever need to change the state after you initialize it, which means you do not need to use state at all.
Dire should just be a model class.
class Dire {
final ScheduleVariants scheduleVariant;
final FlightCard flightCard;
final Stop stop;
Dire(this.scheduleVariant, this.flightCard, this.stop);
}
In your widget where you want to get the Dires from the APIs, you can use this function in the FutureBuilder:
Future<List<Dire>> fetchData() async {
String username = '';
String password = '';
String basicAuth =
'Basic ' + base64Encode(utf8.encode('$username:$password'));
print(basicAuth);
final result = await Future.wait([
http.get(
Uri.parse(
"http://mysecurelink/getMarshVariants.php?fmt=json"),
headers: <String, String>{'authorization': basicAuth}),
http.get(
Uri.parse(
"http://mysecurelink/getFlightCard.php?fmt=json&mv_id"),
headers: <String, String>{'authorization': basicAuth}),
http.get(
Uri.parse(
"http://mysecurelink/getStops.php?fmt=json"),
headers: <String, String>{'authorization': basicAuth}),
]);
flightCardMap = HashMap.fromIterable(json.decode(result[1].body), (fc) => fc["mvId"], (fc) => FlightCard(fc));
stopMap = HashMap.fromIterable(json.decode(result[2].body), (s) => s["stId"], (s) => Stop(s));
return json.decode(result[0].body).map((sv) => {
flightCard = flightCardMap[sv["mvId"]];
return Dire(ScheduleVariants(sv), flightCard, stopMap[flightCard["stId"]]);
}).toList();
}
A disclaimer: I did not check this code snippet for syntax errors, so there might be some but the general idea is there.