Dart class constructor initialise fields in constructor body - class

I am trying to initialise the field tasks in the below code but I am getting the error: Non-nullable instance field 'tasks' must be initialized.. I can successfully initialise fields using syntax like Example(this.tasks) {} or Example(String json) : this.tasks = [json] but I am unsure how to initialise a field when I need to use multiple lines to calculate the value like in the below code.
import 'dart:convert';
class Example {
List<String> tasks;
Example(String json) {
List<String> data = jsonDecode(json);
this.tasks = data;
}
}

In this example you don't need multiple lines to compute the value. You can just do:
Example(String json) : this.tasks = jsonDecode(json);
In the more general case where you do need multiple statements, if the field initialization values are unrelated, I'd use helper functions for each:
Example(String json) : this.tasks = _computeTasks(json);
static List<String> _computeTasks(String json) {
List<String> result;
// compute compute compute
return result;
}
If you have multiple fields that need to be initialized with values from the same computation, I'd first try to make it a factory constructor:
final Something somethingElse;
Example._(this.tasks, this.somethingElse);
factory Example(String json) {
List<String> tasks;
Something somethingElse;
// compute compute compute
return Example._(tasks, somethingElse);
}
If the constructor needs to be generative, and you need to compute multiple values in the same computation, and it's really important to not change this, then I'd probably make an intermediate object holding those values:
Example(String json) : this._(_computeValues(json));
Example._(_Intermediate values)
: tasks = values.tasks, somethingElse = values.somethingElse;
static _Intermediate _computeValues(String json) {
List<String> tasks;
Something somethingElse;
// compute compute compute
return _Intermediate(tasks, somethingElse);
}
...
}
// Helper class.
class _Intermediate {
final List<String> tasks;
final Something somethingElse;
_Intermediate(this.tasks, this.somethingElse);
}
If the types are the same, you can perhaps use a List instead of a helper class for the intermediate value. Or, you might be able to reuse a class like
class Pair<S, T> {
final S first;
final T second;
Pair(this.first, this.second);
}
It's not very important how you do it, the user will never see the intermediate value.

You are correct, non-nullable values must be initialized first upon object construction either as arguments, or in the initializer list.
You cannot however call methods on your object before initialization is complete. (And you probably shouldn't anyway as object construction should be kept as lightweight as possible)
If you have any processing that needs to be done before (or after) constructing an object, a factory constructor can be used. In fact it looks like you are trying to create an object from json which is just what is exemplified in the official docs.
To simplify that example i have linked to and remove anything about caching, it would look something like this:
class Logger {
String name;
bool mute = false;
factory Logger.fromJson(Map<String, Object> json) {
return Logger._internal(json['name'].toString());
}
Logger._internal(this.name);
}

Irn's solution is good. You could also make tasks nullable, if that's OK with you:
List<String>? tasks;

Related

Flutter dart replace replace Json object with variables

In this case I have class. Where I took a variable. Also I have a Json map. So I want to change Json map object replace with variables. Here is my code example....
So how can I achieve that
I want replace Json object with dart variable
class Data {
late String slug;
Map<String, String> singleProductVariable = {"slug": "$slug"};
}
Firstly, there is no JSON in your code sample.
I assume that you would like to set the value of the corresponding key in your Map when setting the variable.
If so, you might want to use a setter in a next way:
class Data {
String _slug;
late Map<String, String> v = {"slug": _slug};
Data(String slug) : _slug = slug;
set slug(String str) => v['slug'] = str;
}
void main() {
final d = Data("slug");
print(d.v);
d.slug = "newSlug";
print(d.v);
}
The output of the code above will be:
{slug: val}
{slug: newVal}

How to recreate singleton instance if different params are passed to the constructor in dart

I gathered the following understanding for creating a singleton in dart with params
class Foo extends ChangeNotifier {
late String channel;
void instanceMemberFunction () {
print('Foo created with channel $channel')
}
static final Foo _instance = Foo._internal();
Foo._internal() {
instanceMemberFunction();
}
factory Foo({
required String channel
}) {
_instance.channel = channel;
return _instance;
}
}
and I am calling the instance like so
Foo({channel: "bar"})
Now I want to have some working that if I use
Foo({channel: "baz"})
Then a new instance is created and it's okay in that case to destroy the old one. How can I achieve this in dart?
It seems like you've copied some existing example for creating a singleton without fully understanding what it's doing and why. The core parts are:
The single instance is stored in a global or static variable.
The class has one or more public factory constructors that returns that global/static variable, initializing it if necessary.
All other constructors for the class are private to force consumers to go through the factory constructors.
Therefore, if you want your factory constructor to replace its singleton based on its argument, you need to:
Make your factory constructor check if the argument is appropriate for the existing instance. If it is, return the existing instance. If not (or if there is no existing instance), create and return a new instance.
Since you need to check if the existing instance is initialized, make it nullable. (You alternatively could initialize it to a non-null sentinel value, e.g. Foo._internal(channel: '').
Pass the argument along to the private constructor.
class Foo extends ChangeNotifier {
final String channel;
void instanceMemberFunction () {
print('Foo created with channel $channel');
}
static Foo? _instance;
Foo._internal({required this.channel}) {
instanceMemberFunction();
}
factory Foo({required String channel}) {
if (channel != _instance?.channel) {
_instance = Foo._internal(channel: channel);
}
return _instance!;
}
}
Note that this implementation will create a new object if the constructor argument changes, which isn't very singleton-like. Depending on what you want to do, you could:
Return a new object (which could allow multiple simultaneous instances).
Return the existing object.
Return the existing object, but mutate it with the constructor argument.

why should i make the returned instance of factory a static field?

Creating a function that returns an instance of the class is right, but creating a factory that returns an instance of the class should be a static field. why should i make the returned instance of factory a static field?
the code is:
class DBHelper{
DBHelper._();
factory DBHelper()=>instance; // ->> cursor error
// static
// final
DBHelper instance=DBHelper._();
int number=2;
int fun()=>number;
}
I think you didn't quite get the purpose of factory methods. The example you've posted is the way of using singleton pattern in dart/flutter. It's, in my opinion the cleanest way of creating a singleton class.
Now the factory method can be used to to simplify the construction of objects and they don't have to return static fields at all. Here are a few examples:
class OneObject {
final int one;
final String two;
final double three;
OneObject(this.one, this.two, this.three);
factory OneObject.withoudTwo(int one, double three) {
return OneObject(one, "2", three);
}
factory OneObject.fromJson(Map<String, dynamic> aMap) {
return OneObject(aMap["one"], aMap["two"], aMap["three"]); // this is bad use of json parsing
}
}
In your case the DBHelper._(); is the actual constructor of the class and the _ makes it private and this allows you to create a factory method called DBHelper.();
To elaborate the answer a bit more, factory methods are similar to static methods and static methods can't access instance members of the class that they are declared on, they can access only static members. In your example, the instance property (without static) will be created only when a new instance of the DbHelper is created and because of that, returning it from a factory method is impossible because it's not available yet.

Dart Named constructor vs Static method what to prefer?

So after dart made new keyword optional,
we can initialize an object with exact same syntax but different internal implementation.
class Color {
int r = 0, g = 0, b = 0;
Color({this.r, this.b, this.g});
//Named constructors
Color.red() //Implementation
Color.cyan() //Implementation
// Static Initializers
static Color red() => //Initialze with parameter
static Color cyan() => //Initialze with parameter
}
We can use them like this regardless of being it a named constructor or static method:
Color red = Color.red();
Color cyan = Color.cyan();
What is the place to use each of them?
In practice there is little difference between a factory constructor and a static method.
For a generic class, it changes where you can (and must) write a type parameter:
class Box<T> {
T value;
Box._(this.value);
factory Box.withValue(this.value) => Box<T>._(value);
static Box<T> fromValue<T>(T value) => Box<T>._(value);
}
...
var box1 = Box<int>.withValue(1);
var box2 = Box.fromValue<int>(2);
So, for generic classes, factory constructors are often what you want. They have the most pleasant syntax.
For non-generic classes, there is very little difference, so it's mainly about signaling intent. And deciding which category the name goes into in the DartDoc.
If the main objective of the function is to create a new object, make it a constructor.
If the main objective is to do some computation and eventually return an object (even if it's a new object), make it a static function.
That's why parse methods are generally static functions.
In short, do what feels right for your API.
Constructors and static functions are different. You usually create a named constructor that returns an instance of an object with some predefined values. For example, you have a class called Person which stores Name and Job. You can create this named constructor Person.doctor(name) which you will return a Person object with Job = 'doctor'
class Person{
final name;
final job;
Person(this.name, this.job);
Person.doctor(this.name, {this.job = "doctor"});
}
Static functions or variable persists on all the instance of a class. Let us say, Person has a static variable called count. You increment the count variable whenever an instance of Person is created. You can call Person.count anywhere later in your code to get the value of count (Number of instances of Person)
class Person{
final name;
final job;
static int count;
Person(this.name, this.job){
count++;
}
Person.doctor(this.name, {this.job = "doctor"});
}
Another very useful feature of static class methods is that you can make them asynchronous, i.e. wait for full initialisation in case this depends on some asynchronous operation:
Future<double> getCurrentPrice(String ticker) async {
double price;
// for example, fetch current price from API
price = 582.18;
return price;
}
class Stock {
String ticker;
double currentPrice=0.0;
Stock._(this.ticker);
static Future<Stock> stockWithCurrentPrice(String ticker) async {
Stock stock = Stock._(ticker);
stock.currentPrice = await getCurrentPrice (ticker);
return stock;
}
}
void main() async {
Stock stock = await Stock.stockWithCurrentPrice('AAPL');
print ('${stock.ticker}: ${stock.currentPrice}');
}
Another benefit of the distinction between named constructor and static function is that in the documentation generated the function will be either filed in the construction section or the methods section, which further makes it's intentions clearer to the reader.
A person looking for a constructor in the constructor section of the documentation will easily discover the named constructors as opposed to having to also dig through the static functions section too.

Passing constructor as argument in Flutter

I have API communication service in my Flutter app with 10+ different services, and 100+ API calls that heed to parse data. In order to reuse code I've decided to create some common parsing code that is going to parse data from API:
ApiResponse handleObjectResponse({
#required http.Response serverResponse,
#required Function objectConstructor,
}) {
if (serverResponse.statusCode == 200) {
dynamic responseObject = objectConstructor(json.decode(serverResponse.body));
return ApiResponse(responseObject: responseObject);
} else {
ApiError error = responseHasError(serverResponse.body);
return ApiResponse(error: error);
}
}
This way I am able to parse JSON object from API in a reusable way no matter what the Object class is, just by passing constructor function to this method.
When I call this method in any of the Services I've created for fetching data like this:
handleObjectResponse(serverResponse: response, objectConstructor: ChartData.fromJson);
I get error: The getter 'fromJson' isn't defined for the class 'ChartData'.
Try importing the library that defines 'fromJson', correcting the name to the name of an existing getter, or defining a getter or field named 'fromJson'.
Where I think the problem is is in this model class and factory statement, but I don't know how to fix it:
class ChartData {
List<ChartDataPoint> points;
ChartData({
this.points,
});
factory ChartData.fromJson(Map<String, dynamic> json) {
List jsonPoints = json["data"];
return ChartData(
points: List.generate(jsonPoints.length,
(i) => ChartDataPoint.fromJsonArray(jsonPoints[i])));
}
}
You cannot pass constructors as functions. You need to create a function what will call the constructor instead:
(int a) => Foo(a);
Just a 2022 update: since 2.15 it's possible by Class.new, see the complete issue: https://github.com/dart-lang/language/issues/216.
class A {
final String a;
const A(this.a);
#override
String toString() => 'A($a)';
}
class B {
final String b;
const B(this.b);
#override
String toString() => 'B($b)';
}
void main() {
final List<Object Function(String)> constructors = [A.new, B.new];
for (final Object Function(String) constructor in constructors) {
final Object instance = constructor('My Constructor Parameter');
if (instance is A) {
print(instance.toString());
}
}
}
Note that if you're using named params, both class constructors must have the same param name, otherwise the constructor signatures won't match and then it will generate this static error:
The element type X can't be assigned to the list type Y.