How can I use async in a named constructor? [duplicate] - flutter

This question already has answers here:
Calling an async method from a constructor in Dart
(3 answers)
Closed 4 months ago.
I created a class, and I want to use async in a named constructor or a method in the class that is accessible outside the class. When making the named constructer return a Future type, I get an error saying: Constructors can't have a return type.
I then tried removing the Future type, and I still get an error saying The modifier 'async' can't be applied to the body of a constructor.
How can I use async in a named constructor?
class HttpService {
Future<void> HttpService.getAll() async {
final response = await http.get(
Uri.encodeFull('http://localhost:4000/'),
headers: {'headers': 'application/json'},
);
if (response.statusCode == 200) {}
}
}
I am new to oop, so I may be using it wrong? Any guidance is accepted.

Constructors can't be asynchronous. If you find yourself wanting an asynchronous constructor, you instead could make a static asynchronous method that acts like an asynchronous factory. From the perspective of the caller, there isn't much difference (and what differences there are mostly favor having a static method). You additionally could make all other constructors (including the default constructor) private to force callers to use the static method to get an instance of your class.
That said, in your case, you might not even need a class at all. Do you intend to have other methods on an HttpService? Is your HttpService maintaining any internal state? If not, then you would be better off with a freestanding function.

You can't use async in constructor. You should create a separate method for this.
Solution: 1 (Recommended)
class Foo {
static Future<void> fetch() async { // method
await future();
}
}
Solution: 2
class Foo {
Foo.named() { // named constructor
future().then((_) {
// future is completed do whatever you need
});
}
}

Related

Isolates in Flutter - Where can I place the isolates inside my Flutter code

I have some confusion regarding how Isolates can be used inside a Flutter application.
If we go through the documentation, It is said that functions that you pass inside the isolates should only be declared as top-level functions. Does that mean we cannot declare them inside a class ?
I created a class TestIsolate inside my lib/business_logic/bloc folder.
class TestIsolate {
Future<void> handle(int _m) async {
final response = ReceivePort();
await Isolate.spawn(_isolate, response.sendPort);
final sendPort = await response.first as SendPort;
final answer = ReceivePort();
sendPort.send([_m, answer.sendPort]);
await answer.first.then((p) {
log(p);
});
}
static void _isolate(SendPort _initialReplyTo) {
final port = ReceivePort();
_initialReplyTo.send(port.sendPort);
port.listen((message) {
final data = message[0] as int;
final send = message[1] as SendPort;
send.send(_syncHandle(data));
});
}
}
Future<String> _syncHandle(int data) async {
return 'done - $data';
}
I then called await TestIsolate.handle(15) upon an onTap event from my presentation layer which worked like a charm.
Am I doing this correctly ? If yes, can we call handle() placed inside TestIsolate class as a top-level function?
Any help would be really appreciated!
What you are doing is correct. The source you reference (which I will point out is an article, not documentation) says:
The function passed to the isolate spawn() must be a top-level function *(a function that is not within the boundary of a class) or a static method.
You are spawning an isolate with an entry point of _isolate(), which is a static method. So, according to your source, that is ok.
However, it may be the case that your source is outdated. According to the changelog for Dart 2.15:
Allow closures both in inter-isolate messages as well as as entrypoints in Isolate.spawn(<entrypoint>, ...) calls. Closures and their enclosing context may need to be copied in this process. The enclosing context is - as with normal messages - verified to only contain objects that are sendable.
Note of caution: The Dart VM's current representation of enclosing variables in closures can make closures hang on to more variables than strictly needed. Using such closures in inter-isolate communication can therefore lead to copying of larger transitive object graphs. If the extended transitive closure includes objects that are illegal to send, the sending will fail.
It would appear that closure (and non-static method) arguments to Isolate.spawn() were introduced after your source article was written.
As the changelog cautions though, you do want to be cognizant of the memory copying that will occur.
Further references:
Is DartDocs about the entry of Isolate.spawn wrong or something?

Dart: how do I initialize a class such that one of its properties is filled upon initialization via a function that returns a future?

I have a file called database.dart. In it, I have a string property called currentUsername. This is the currently logged in user's username. I want this class to call the getUsernameFS() function only once and then be able to reuse this string for the rest of the class's existence inside its other functions. How do I accomplish this?
The code below gives an error: Error: A value of type 'Future<String>' can't be assigned to a variable of type 'String'.
class Database {
late String currentUsername = getUsernameFS(); //ERROR IS HERE
Future<String> getUsernameFS() async {...}
String someFunction() {...//some function that uses currentUsername//...}
}
A direct (and perhaps naive) approach would be to add an asynchronous initialization step to initialize an instance of your class:
class Database {
late String currentUsername;
Future<void> initialize() async {
currentUsername = await getUsernameFS();
}
}
However, that's potentially error-prone since it creates more work for callers, and callers could accidentally neglect to call (or neglect to wait for) initialize, and there's no way to enforce that at compile-time.
Instead, I'd recommend a couple of other options:
Make your member variable a Future instead:
late Future<String> currentUsername = getUsernameFS();
This has the advantage of safely avoiding accidental errors from callers who neglect to explicitly call an asynchronous initialization method first. However, this has the disadvantage of forcing all callers to await the result, making them also asynchronous.
If possible, make your class constructor private and force callers to obtain instances with an asynchronous, factory-like static method:
class Database {
late String currentUsername;
Database._();
static Future<Database> create() async {
var db = Database._();
db.currentUsername = await db.getUsernameFS();
return db;
}
...
}
This also has the advantage of safely avoiding accidental errors, and it avoids forcing all consumers of currentUsername to be asynchronous. A disadvantage is that a private constructor would prevent your class from being extended.
If possible, I'd also make getUsernameFS a static method and pass the username to the private constructor. Then currentUsername wouldn't need to be late, and you would avoid any risk of accidentally using a late variable before it's initialized.

GetX doesn't accept method parameters either

I cannot use the ".obs" property variables that I have created as parameters in my api service methods. and it gives the error The prefix 'coinTwo' can't be used here because it is shadowed by a local declaration.
Try renaming either the prefix or the local declaration.
I wrote the problem in getx, but they did not understand the problem.
I would appreciate it if you could take a look at my issue on github.
İssue link: text
I would appreciate it if you could take a look at my issue on github.
İssue link: text
Your problem is in the declaration of getOrderBookData
Instead of
getOrderBookData(coinOne.value, coinTwo.value) async {
...
you should either have
getOrderBookData() async {
...
//Do stuff with coinOne.value, coinTwo.value
...
OR
getOrderBookData(String firstCoin, String secondCoin) async {
coinOne.value = firstCoin;
coinTwo.value = secondCoin;
...
Edit
Seeing your post on GetX's Github, it looks like you're missing some basic understanding on how functions and methods work.
The code you wrote is the equivalent of the following method signature :
getOrderBookData("aValue", "anotherValue") async {
and it doesn't makes any sense.
Your method's signature should only declare the parameters it's expecting and their type.
You can also define a default value for those parameters if needed.
getOrderBookData({String firstCoin = coinOne.value, String secondCoin = coinTwo.value}) async {
in function declarations you just tell what type it is, and not actual values
So either do
getOrderbookData(String one, String two) async {
var res = await api.fetchOrderBookData(one, two);
purchase.value = res.result!.buy!;
sales.value = res.result!.sell!;
}
or hardcode it to always use coinOne and coinTwo and give no parameters
getOrderbookData() async {
var res = await api.fetchOrderBookData(coinOne.value, coinTwo.value);
purchase.value = res.result!.buy!;
sales.value = res.result!.sell!;
}
Make these changes and the error will be gone.
First, make a minor change to your getOrderBookData method:
void getOrderBookData() async {...} // Don't pass any parameter here.
And then make a change to your onInit method too.
#override
void onInit() {
getOrderBookData();
super.onInit();
}

How can I inherit static methods in dart/flutter?

Is it possible in Dart/Flutter to inherit static methods or factories? Or do I need to workaround this by creating an instance to access that static method?
My case is that I want to serialize an object but need to access a general parse function for them.
abstract class Foo {
static Foo parse(); //Error, must have a body
Foo parse();//No error but need to call Foo().parse(); by creating an instance.
}
I want to create by using json so is bad practice and against performance to create a new instance to return another one?
class InheritedFoo {
final String string;
InheritedFoo(this.string);
#override
Foo parse() {
return InheritedFoo("some string");
}
}
Is it maybe possible to use a singleton to save performance (call InheritedFoo.inst.parse() )?
No you cannot do that. This excerpt is from the official Dart language specification:

How to specify generic type such that we can use some operation on the generic type in Flutter?

For example, I expect <T extends type> can works like this:
Class Parent {
String data;
Parent({ this.data });
}
Class Child extends Parent {
Child({ this.data }) : Parent(data: data);
void showData() { print(data); }
}
T wrapper<T extends Parent>(String value) {
var result = T(data: value);
return result;
}
void main() {
var trial = wrapper<Child>("Hello world");
trial.showMessage(); // print "Hello world"
}
But turns out it gives me error at var result = T(data: value);, saying that T is not a function. When I specified , I expect that T can be operated like Parent class, and if I supplied its descendant like Child, the operation done will be Child instead. But the constructor will work either way because T extends Parent. Is such thing possible?
Constructors are not inherited. You know that already, because you wrote one yourself in your child class that does nothing but call the base class with the same parameters.
One could as well write a different constructor. So "X extends Y" says a lot about X, but it does not say anything about how the constructor of X looks (or whether it even has a constructor accessible in that scope). So a constructor call is not in the properties available to you when you specify your generic to "extend Y", because Y can do exactly nothing to make sure all it's derivates follow a specific construction method.
Different languages deal with the problem of "but how do I construct a new instance of my generic type" in different ways, but the underlying concept is common to almost all concepts of generics where the generic code is compiled before knowing the specific types of all T's handled. A constructor is not inherited in most OOP languages, therefor it is not guaranteed to be there for any "X extends Y" even if Y has it.
It might be easy to overlook when you have all your code in one compilation unit. The compiler should be able to figure it out, right? But your code might not be in a single compilation unit:
Codebase one:
Class Parent {
String data;
Parent({ this.data });
}
T wrapper<T extends Parent>(String value) {
var result = T(data: value);
return result;
}
At this point, the compiler has no idea what "Child" might look like. It cannot possibly determine that the child class that will be used in the Future has a constructor like that.
Codebase 2:
Class Child extends Parent {
Child({ this.data }) : Parent(data: data);
void showData() { print(data); }
}
void main() {
var trial = wrapper<Child>("Hello world");
trial.showMessage(); // print "Hello world"
}
Now, at this point, a compiler could figure out that the program it's given would actually work. Some concepts of generics do that, where generics cannot be compiled into independent libraries, they always come as source code, because only the final compiler producing the executable can determine whether it would work with a specific class. Flutter does not do this. Flutter needs the generic itself be valid for the constraints given.
All newer language's versions of generics have followed the path of knowing the constraints beforehand and only allowing code operating inside those constraints. And I think it's good because while it has it's shortcomings, it leaves less room for errors or cryptic error messages.