Flutter - Call API Rest - flutter

I have 3 .dart files: The Model, the API Call and the view. View call to the service to retrieve data, like this.
Model
class MapData {
String calle;
String pais;
String poblacion;
String postal;
String provincia;
MapData({
this.calle,
this.pais,
this.poblacion,
this.postal,
this.provincia,
});
factory MapData.fromJson(Map<String, dynamic> json) => new MapData(
calle: json["results"][0]['address_components'][1]["long_name"],
pais: json["results"][0]['address_components'][5]["long_name"],
poblacion: json["results"][0]['address_components'][2]["long_name"],
postal: json["results"][0]['address_components'][6]["long_name"],
provincia: json["results"][0]['address_components'][3]["long_name"],
);
}
Service
import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:datameter/models/mapData_model.dart';
class DatameterService {
static Future<MapData> getMapData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final response = await http
.get(
'https://maps.google.com/maps/api/geocode/json?latlng=' +
"43.321146" +
',' +
"-3.008724" +
'&key=KEY",
)
.catchError((error) {
print(error.toString());
});
var responseJson = json.decode(response.body);
if (responseJson != null) {
final responseJson = json.decode(response.body);
return MapData.fromJson(responseJson);
}
return null;
}
}
Main view
void initState() {
print(DatameterService.getMapData()); //This doesn´t work
super.initState();
}
The problem: I don´t know how can i get the data.
So: What is the rigth way to get api call data?

You are trying to print a future!
Maybe try this:
void initState() {
DatameterService.getMapData().then((map){
print(map);
});
super.initState();
}
In the initstate you cannot await for future to complete but you can do something when it completes with the keyword "then".

Related

type 'Null' is not a subtype of type 'List<Carousel>' in flutter

I encountered a warning while working with the Carousel Slider library I checked but could not find the cause Thank you for helping me find my problem
I put the classes that could have caused this warning here and below
Thank you all
import 'package:http/http.dart' as http;
import 'package:multi_shop/model/carousel.dart';
class RemoteService {
static var client = http.Client();
static Future<dynamic> fetchCarouselData() async {
try{
final response = await client.get(
Uri.parse('https://my-shop.com/home-carousels'),
);
if(response.statusCode == 200){
return carouselFromJson(response.body);
}
return null;
} catch(e){
return null;
}
}
}
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:multi_shop/http/remote_services.dart';
import 'package:multi_shop/model/carousel.dart';
class HomeController extends GetxController{
var box = GetStorage();
var isLoading = false;
List<Carousel> carouselData = [];
#override
void onInit() {
fetchCarousel();
if(box.read('carouselData') != null) {
carouselData.assignAll(box.read('carouselData'));
}
super.onInit();
}
void fetchCarousel() async {
try{
isLoading = true;
update();
List<Carousel> data = await RemoteService.fetchCarouselData();
carouselData.assignAll(data);
box.write('carouselData', data);
}finally{
isLoading = false;
update();
print('data fetch done');
}
}
}
Your code is not type safety,
do not use dynamic if you already have Carousel type;
return empty array instead of null;
I would refactor your code as
static Future<List<Carousel>> fetchCarouselData() async {
try{
final response = await client.get(
Uri.parse('https://my-shop.com/home-carousels'),
);
if(response.statusCode == 200){
return carouselFromJson(response.body);
}
return [];
} catch(e){
return [];
}
}
And please, do not call async code inside onInit(), use onReady() for async stuff, read comments of these methods for more.
For more enhancement I would recommend use fluent_result to deal better with exceptions and errors in the code.

Flutter: response variable blank in http request

I am trying to create a profile page for my flutter app
Here is a snippet
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class MyProfile extends StatefulWidget {
#override
_MyProfileState createState() => _MyProfileState();
}
class _MyProfileState extends State<MyProfile> {
bool visible=false;
var postData=List();
var jsonData;
Future accountInfo()async{
var url ='http://192.168.0.107/get_account.php';
var response= await http.get(url);
var jsonData= json.decode(response.body);
setState(() {
postData=jsonData;
});
debugPrint(jsonData);
return jsonData;
}
#override
void initState(){
super.initState();
accountInfo();
}
However, the variables postData and jsonData are returned as null.
Value of postData=[]
The API is working perfectly, tried it with postman and also tried intercepting with a proxy tool.
I am getting a valid json response body. But the values are not passed onto jsonData or postData.
When I use postData inside a text widget i get this error:-
RangeError(index): Index out of Range: no indices are valid: 0
You defined two jsonData. That may be the reason. Also, set jsonData inside setState(), too. Try this:
var jsonData;
Future accountInfo()async{
setState(() {
visible=true;
});
var url ='http://192.168.0.107/get_account.php';
var response = await http.get(url);
setState(() {
jsonData = json.decode(response.body); // Here we changed!
postData = jsonData;
});
The solution is to define jsonData as
Map<String, dynamic> jsonData= jsonDecode(response.body);

How to get CONTEXT for the provider to work? Flutter

In the Future fetchStudentInfo() function, i would like to use the userId from my Auth class to do filtering. The userId is embedded in the URL and it will retrieve data from database. But, the issue is that the context is lacking in the function itself. However, I couldn't figure out a way to pass in the context. It would be great if any legend could help me. The solution which retrieve data from internet is found on the flutter documentation. And i wouldn't like to hard code the userId.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';
import '../model/student.dart';
import '../provider/auth.dart';
Future<Student> fetchStudentInfo() async {
final auth = Provider.of<Auth>(context);
final response = await http.post(
'https://intermediary-sharpe.000webhostapp.com/Student/read_one.php?userId=$auth.userId');
if (response.statusCode == 200) {
return Student.fromJson(json.decode(response.body));
} else {
throw Exception('Failed');
}
}
class ProfileScreen extends StatefulWidget {
#override
_ProfileScreenState createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
Future<Student> student;
#override
void initState() {
// TODO: implement initState
super.initState();
student = fetchStudentInfo();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<Student>(
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.studentId);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return CircularProgressIndicator();
},
future: student,
),
);
}
}
import 'package:flutter/cupertino.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
import '../model/http_exception.dart';
class Auth with ChangeNotifier {
String _token;
DateTime _expiryDate;
String userId;
Timer _authTimer;
bool get isAuthenticated {
return token != null;
}
String get token {
if (_expiryDate != null &&
_expiryDate.isAfter(DateTime.now()) &&
_token != null) {
return _token;
}
return null;
}
Future<void> _authenticate(
String email, String password, String urlSegment) async {
final url =
'https://identitytoolkit.googleapis.com/v1/accounts:$urlSegment?key=AIzaSyCkNZysDY4PGpScw2jUlBpd0mvpGjgSEag';
try {
final response = await http.post(
url,
body: json.encode(
{
'email': email,
'password': password,
'returnSecureToken': true,
},
),
);
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['idToken'];
userId = responseData['localId'];
_expiryDate = DateTime.now().add(
Duration(
seconds: int.parse(
responseData['expiresIn'],
),
),
);
_autoLogout();
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'token': _token,
'userId': userId,
'expiryDate': _expiryDate.toIso8601String(),
});
prefs.setString('userData', userData);
} catch (error) {
throw error;
}
}
//Auto Login Function
Future<bool> tryAutoLogin() async {
final prefs = await SharedPreferences.getInstance();
if (!prefs.containsKey('userData')) {
return false;
}
final extractedUserData =
json.decode(prefs.getString('userData')) as Map<String, Object>;
final expiryDate = DateTime.parse(extractedUserData['expiryDate']);
if (expiryDate.isBefore(DateTime.now())) {
return false;
}
_token = extractedUserData['token'];
userId = extractedUserData['userId'];
_expiryDate = expiryDate;
notifyListeners();
_autoLogout();
return true;
}
//SignUp function
Future<void> signUp(String email, String password) async {
return _authenticate(email, password, 'signUp');
}
//Login Function
Future<void> login(String email, String password) async {
return _authenticate(email, password, 'signInWithPassword');
}
//Logout Function
Future<void> logout() async {
_token = null;
userId = null;
_expiryDate = null;
if (_authTimer != null) {
_authTimer.cancel();
_authTimer = null;
}
notifyListeners();
final prefs = await SharedPreferences.getInstance();
prefs.clear();
}
//Auto Logout function
void _autoLogout() {
if (_authTimer != null) {
_authTimer.cancel();
}
final timeToExpiry = _expiryDate.difference(DateTime.now()).inSeconds;
_authTimer = Timer(Duration(seconds: timeToExpiry), logout);
}
//PHP related functions
}
Thank you in advance.
I agree with #lyio, you need to modify the function to pass the context, however after passing context, you cannot call it from initState as stated in documentation of initState
BuildContext.dependOnInheritedWidgetOfExactType from this method. However, didChangeDependencies will be called immediately following this method, and BuildContext.dependOnInheritedWidgetOfExactType can be used there.
Getting provider with Provider.of(context) under the hood is using the inherited widget, so cannot be called using context from initState
So implement instead of initState use didChangeDependencies to call your fetchStudentsInfo(context) method
Wouldn't the easiest solution be to pass the context into fetchStudentInfo?
You would change fetchStudentInfo() to fetchStudentInfo(BuildContext context). And then, when you call the method you pass in the required context. That way, you have the appropriate context available.
If you are not using the `fetchStudentInfo()` outside of the state class, then just move that method into the state class and the issue will be resolved.
Since Any state class has a context getter defined by default./
I just realized how improper this answer was.
Update:
According to the answer by #dlohani, didChangeDependencies should be used in stead of initState.
So what you can do is following:
Pass BuildContext as parameter in the fetchStudentInfo method
Override didChangeDependencies in state class & call fetchStudentInfo from here instead of initState

Rx Flutter Request List From JsonPalceHolder

I try to get List from jsonPlaceHolder using flutter rxdart stream and try to apply bloc pattern on it.
this class that response for get post response from api
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/post_item.dart';
class ItemApi {
Future<List<JsonItem>> getPost() async {
String _url = 'https://jsonplaceholder.typicode.com/posts';
final _response = await http.get(_url);
if (_response.statusCode == 200) {
return (json.decode(_response.body) as List)
.map((jsonItem) => JsonItem.fromJson(jsonItem))
.toList();
}
}
}
I using repository class to wrap using ItemApi class
import 'json_item_request.dart';
import '../models/post_item.dart';
class Repository{
final jsonItemResponse = ItemApi();
Future<List<JsonItem>> getItem() => jsonItemResponse.getPost();
}
at the last i using bloc class that response for get data and set it inside PublishSubject
import '../models/post_item.dart';
import '../resouces/repository.dart';
import 'package:rxdart/rxdart.dart';
class JsonBloc {
final _repository = Repository();
final _streamOfJsonList = PublishSubject<List<JsonItem>>();
Observable<List<JsonItem>> get jsonList=> _streamOfJsonList.stream;
fetchAllPost() async{
Future<List<JsonItem>> list = _repository.getItem();
}
dispose(){
_streamOfJsonList.close();
}
}
My question is how i can set response inside _streamOfJsonList variable to using it when list changed.
Sounds like you already have all the moving parts connected? If so you just need to add the item list to the PublishSubject:
void fetchAllPost() async {
List<JsonItem> list = await _repository.getItem();
_streamOfJsonList.add(list);
}
This will trigger the onListen callback with the new list on anything that is listening to the stream.
You can add error and data to ReplaySubject like below :
void fetchAllPost() async {
List<JsonItem> list = await _repository.getItem();
if (list != null) {
_streamOfJsonList.sink.add(list);
} else {
_streamOfJsonList.addError("ERROR");
}
}

Access other Class method in Flutter/dart

I was working on login with preference. Everything is working fine when I wrote all code in main.dart.
Problem:
When I create separate class on MySharePref then I am getting some error.
MySharePref.dart
import 'package:first_app/UserModel.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SharePrefClass {
void _saveData(UserModel model) async{
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString("Username",model.userName);
await prefs.setString("Password", model.password);
}
Future<UserModel> _getData() async{
SharedPreferences preferences = await SharedPreferences.getInstance();
String username = preferences.getString("Username");
String password = preferences.getString("Password");
UserModel model = UserModel(username,password);
return model;
}
}
I want to access these both functions in main.dart:
_checkLogin() async {
UserModel userModel = new UserModel(
userNameEditText.text , passwordEditText.text);
SharePrefClass mySharedPref = new SharePrefClass();
final UserModel returnModel = mySharedPref._getData() ;
if(returnModel.userName == ""){
print("No data");
}else{
print("else executed");
}
}
I am getting error:
The prefix "_" means private field in dart.
Change the method name _getData() to getData() will let you can access this method in main.dart