What is the best way to make a network call? - flutter

I am fairly new to Flutter. I would like to know what is the best way in terms of coding best practices to do a network call.
I searched on internet (including Stackoverflow) on how to make the REST call (GET, POST) and found some code samples. For example, one of them is given below.
new RaisedButton(
onPressed: () async {
Post newPost = new Post(
userId: "123", id: 0, title: titleControler.text, body: bodyControler.text);
Post p = await createPost(CREATE_POST_URL,
body: newPost.toMap());
print(p.title);
},
)
Now, I don't think it's a good idea to club everything in onPressed(). I am especially interested to know how to fit a network call before a page load (or update after the data is fetched). I know it's done by setState(). But would like to know how the different pieces are put together to write a best code.
Any help will be highly appreciated.

The best way is to keep every piece short and specific to one task only. This is not specific to the Flutter (and applies to any coding in general). One type of doing it is as mentioned below :
Let's say you want to fetch a list of employees from the API.
Define Employee class, specify mandatory/non-mandatory attributes etc. And also write .fromJson() to instantiate.
Start with didChangeDependencies in your Stateful widget like below.
#override
void didChangeDependencies() {
super.didChangeDependencies();
_requestEmployeeListFromAPI();
}
The _requestDataFromAPI() function will just be a pointer for the network call as follows :
Future<void> _requestEmployeeListFromAPI() async {
try {
_myList = await network.getEmployeeList();
} catch (exception) {
print(exception);
// ...
} else {
// show exception
}
} finally {
if (mounted) {
setState(() => _isLoadingCompleted = true);
}
}
}
Handle your UI based on the value of _isLoadingCompleted
Now, your network file may consists all the network calls to be done in the project. Example below :
Future<List<Employee>> getEmployeeList() async {
try {
http.Response response = await http.post(
"$getEmployeeListUrl",
);
Map<String, dynamic> decodedBody = json.decode(response.body);
print(decodedBody);
List<Employee> employeeList = (decodedBody["EmployeeList"] as List)
.map((element) => Employee.fromJson(element))
.toList();
return employeeList;
} catch (e) {
print(e);
}
}
This way, every piece is written at a different position and is very easy to enhance / debug / find bug, if any.

Related

Unable to use a Future value - Flutter/Dart

I've fetched a json object and deserialized it and then returned it too.
I want to use this in another file.
I'm unable to assign the values that I'm getting in the first step.
Here are all the codes...
Service
Future getGeoPoints(String accessToken, String tripId) async {
String requestUrl;
var response = await get(
Uri.parse(requestUrl),
headers: {
'Authorization': "Bearer $accessToken",
},
);
if (response.statusCode == 200) {
Map<String, dynamic> responseBody = json.decode(response.body);
GetGeoPoints geoPoints = GetGeoPoints.fromJson(responseBody);
List listOfGeoPoints = [];
for (var geoPoint in geoPoints.geoPoints) {
listOfGeoPoints.add(
{
'latitude': geoPoint.latitude,
'longitude': geoPoint.longitude,
'timestamp': geoPoint.timeStamp,
},
);
}
// print('List of geo points: ' + '$listOfGeoPoints');
return listOfGeoPoints;
} else {
throw Exception('Failed to load data from server');
}
}
File where I need the above values
List routeCoordinates;
Future<void> getValues() async {
getGeoPoints(widget.accessToken, widget.tripId)
.then((value) => routeCoordinates = value);
}
When I run the app, routeCoordinates is null but when I hotreload, it contains the value.
I want to have the values as soon as the screen starts. What is the right way to assign the values here?
I've also tried this:
routeCoordinates = getGeoPoints...
It throws error..
Please help.. Thanks..
The function getGeoPoints() is an asynchronous one. But on the other file, you are not using the await keyword, instead you are using then(). So your code is not waiting for that function to return value.
Try using below code,
List routeCoordinates;
Future<void> getValues() async {
routeCoordinates = await getGeoPoints(widget.accessToken, widget.tripId);
}
Let us know how it went.
You need to use a FutureBuilder to define a behaviour depending on the state of the request. You'll be able to tell the widget what to return while your app is waiting for the response to your request. You can also return a specific widget if you get an error(if your user is offline, for example).
Edit: I've linked the official docs but give this article a read if it's not clear enough.

Best way to keep fetching new data from MySQL

I need some suggestions or help please. I am trying to do a small real time chat application, most tutorials that I have been looking basically use firebase, but I have been using MySQL already for other parts of my small projects, I am not sure if I understood Stream correctly, but as far as I know I think that the data does keep fetching, I tried it but it didn't update the new data, and besides StreamBuilder being a little hard to use, I went for the easiest method using a regular http and displaying it by using a function which will be run at the initiation of page like this:
void initState() {
super.initState();
_getChat();
}
_getChat() {
Pulling.getChatsContents().then((value) {
setState(() {
_chatContent = value;
_chatLength = "${value.length}";
});
});
}
Storing them here:
List<Chats> _chatContent;
String _chatLength = '0';
where the List<Chats> is the following:
class Chats {
final String senderid;
final String msgcontent;
Chats(
{this.senderid,
this.msgcontent});
factory Chats.fromJson(Map<String, dynamic> json) {
return Chats(
senderid: json['sender_id'] as String,
msgcontent: json['msg_content'] as String,
);
}
}
and the Pulling is:
class Pulling {
static const ROOT = 'https://example.com/chatsContent.php';
static StreamController streamChat = StreamController();
static Future<List<Chats>> getChatsContents() async {
try {
var myUser = '2';
var map = Map<String, dynamic>();
map['user'] = myUser;
final response = await http.post(ROOT, body: map);
if (200 == response.statusCode) {
final chatsParsed =
json.decode(response.body).cast<Map<String, dynamic>>();
List<Chats> listChat =
chatsParsed.map<Chats>((json) => Chats.fromJson(json)).toList();
return listChat;
} else {
return List<Chats>();
}
} catch (e) {
return List<Chats>();
}
}
}
So basically with all these I am getting the data and displaying, but when new data is added, I do not get the update, there may be a way to do it with Stream, but I am actually quite confused combining it with http, so I was thinking if it is possible and if it is efficient for the app if I added this to the void initState:
_timer = Timer.periodic(Duration(seconds: 1), (timer) => _getChat());
also calling _timer at the beginning:
Timer _timer;
Thank you guys for reading all this and helping me!
UPDATE
The way that I display the fetched data is as follows:
return Column(
children: [
for (var i = 0; i < int.parse(_chatLength); i++)
Container(
child: Text(_chatContent[i].msgcontent),
),
]);
Have you though about checking out about Redux ? It's a really good thing to use for realtime applications. It's quite long to understand, you can find really good tutorials online like this one which is a really good one.
Let me know if you need some help ;)
In my opinion you should use WebSocket instead of HTTP in order to fetch new data instantly.

How do I load a single entry in Sembast based on it's ID?

I look through a bunch of tutorials for Sembast. The tutorials give examples about how to get all the items of a certain class like Cake but not how to load a specific one based on it's ID:
#override
Future<List<Cake>> getAllCakes() async {
final snapshots = await _store.find(_database);
return snapshots
.map((snapshot) => Cake.fromMap(snapshot.key, snapshot.value))
.toList(growable: false);
}
How do I lot a single Cake for a given ID?
You can use store.record(key).get(db)
Assuming your key is an int, this could look like this:
Future<Cake> getCake(int key) async {
final snapshot = await _store.record(key).getSnapshot(_database);
return snapshot != null ? Cake.fromMap(key, snapshot.value) : null;
}
More information (yes documentation is not that great) here

How to refactor functional error handling in flutter with dartz

I have two methods
#override
Future<Option<StorageFailure>> init() async {
final root = await getRootDir();
return root.fold(
(failure) => some(failure),
(dir) {
final images = Directory("$dir/images");
final videos = Directory("$dir/videos");
images.create(); // more error handling here (try|either)
videos.create();
},
);
}
#override
Future<Either<StorageFailure, Directory>> getRootDir() async {
try {
final root = await getApplicationDocumentsDirectory();
return right(Directory("${root.path}/files"));
} catch (e) {
return left(StorageFailure(reason: e.toString()));
}
}
On the init method after folding i need to do more error handling but i don't like to nest too much my code. Also i don't know how to return a Failure from the right function.
What would be a better way to chain those values?
The cool thing about Either is, that you can chain actions and take care of error handling just once at the end. If the program fails to get the root directory, the part about creating the media subdirectories is never executed. So there is no need for nested error handling.
I am in the process of learning functional programming myself. So there might be an even better solution, but here is how I would do it:
// I intentionally added all type annotations for better understanding.
Future<Option<StorageFailure>> init() async {
final Either<StorageFailure, Directory> root = await getRootDir();
final Either<StorageFailure, Either<StorageFailure, Success>> result = await root.traverseFuture(createImagesAndVideosSubfolders);
final Either<StorageFailure, Success> flattenedResult = result.flatMap(id);
return flattenedResult.fold((failure) => some(failure), (success) => none());
}
Future<Either<StorageFailure, Success>> createImagesAndVideosSubfolders(Directory dir) async {
try {
await Directory('${dir.path}/images').create();
await Directory('${dir.path}/videos').create();
return right(Success('success'));
} catch (e) {
return left(StorageFailure(reason: e.toString()));
}
}
get the root directory
create the media directories (if getRootDir failed, the root.traverseFuture method just returns the existing StorageFailure)
Flatten the nested Eithers with flatMap and the id function. The traverseFuture method is the map version for futures. If there would be a flatMap equivalent for futures as well, the result would not be wrapped in an either and the last step to flatten the result would not be necessary.
You could simplify the init function even more by just returning Either instead of converting it to an Option.
Like so:
Future<Either<StorageFailure, Success>> init() async {
...
return result.flatMap(id);
}

Dart Flutter, help me understand futures

See this code:
class SomeClass{
String someVariable;
SomeClass();
Future<String> getData () async {
Response response = await get('http://somewebsite.com/api/content');
Map map = jsonDecode(response.body); // do not worry about statuscode, trying to keep it minimal
someVariable = map['firstName'];
return 'This is the first name : $someVariable';
}
}
Now look at main:
void main(){
String someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData().then((value) => value);
return firstNameDeclaration;
}
}
When working with Future, like in the case of firstNameDeclaration why do I have to use .then() method to access the string object, since I am waiting for the function to finish?
When searching on the web, some people use .then() others don't, I am confused.
Kindly help me have a clearer understanding of how Futures and async functions overall work.
Background
Asynchronous operations let your program complete work while waiting for another operation to finish. Here are some common asynchronous operations:
Fetching data over a network.
Writing to a database.
Reading data from a file.
To perform asynchronous operations in Dart, you can use the Future class and the async and await keywords.
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
Futures vs async-await
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
async-await is just a a declarative way to define asynchronous functions and use their results into Future and it provides syntactic sugar that help you write clean code involving Futures.
Consider this dart code snipped involving Futures -
Future<String> getData(int number) {
return Future.delayed(Duration(seconds: 1), () {
return 'this is a future string $number.';
});
}
main(){
getData(10).then((data) => {
print(data)
});
}
As you can see when you use Futures, you can use then callback when the function return a future value. This is easy to manage if there is single "then" callback but the situation escalates quickly as soon as there are many nested "then" callbacks for example -
Future<String> getProductCostForUser() {
return getUser().then((user) => {
var uid = user.id;
return getOrder(uid).then((order) => {
var pid = order.productId;
return getProduct(pid).then((product) => {
return product.totalCost;
});
});
});
}
main(){
getProductCostForUser().then((cost) => {
print(cost);
});
}
As you can when there multiple chained "then" callback the code become very hard to read and manage. This problem is solved by "async-await". Above chained "then" callbacks can be simplified by using "async-await" like so -
Future<String> getProductCostForUser() async {
var user = await getUser();
var order = await getOrder(user.uid);
var product = await getProduct(order.productId);
return product.totalCost;
}
main() async {
var cost = await getProductCostForUser();
print(cost);
}
As you can above code is much more readable and easy to understand when there are chained "then" callbacks.
I hope this explains some basic concepts and understanding regarding the "async-await" and Futures.
You can further read about topic and examples here
Basically, you should either use await OR then(). However, Dart guidelines advocates that you should prefer use await over then() :
This code :
Future<int> countActivePlayers(String teamName) {
return downloadTeam(teamName).then((team) {
if (team == null) return Future.value(0);
return team.roster.then((players) {
return players.where((player) => player.isActive).length;
});
}).catchError((e) {
log.error(e);
return 0;
});
}
should be replaced by :
Future<int> countActivePlayers(String teamName) async {
try {
var team = await downloadTeam(teamName);
if (team == null) return 0;
var players = await team.roster;
return players.where((player) => player.isActive).length;
} catch (e) {
log.error(e);
return 0;
}
}
In your case, you should write :
void main(){
Future<String> someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData();
return firstNameDeclaration;
// Or directly : return await instance.getData();
// Or : return instance.getData();
}
}