I have a device class in my application where one property needs to be computed and final plus some properties that can be set in the constructor or have default values. Here's the code:
class Device {
String idx;
String id;
String name;
String icon;
int order;
// have to create the idx before initializing the object because I can't
// figure out how to do that here.
Device(
{required this.idx,
required this.id,
required this.name,
this.icon = 'none',
this.order = -1});
// Creates a device from a JSON string
factory Device.fromJson(Map<String, dynamic> jsonData) {
return Device(
idx: jsonData['idx'],
id: jsonData['id'],
name: jsonData['name'],
icon: jsonData['icon'],
order: jsonData['order']);
}
// Returns the device as a String
static Map<String, dynamic> toMap(Device device) => {
'idx': device.idx,
'id': device.id,
'name': device.name,
'icon': device.icon,
'order': device.order
};
}
Basically I'm trying to set a unique index for the object so in my object list I can clearly identify a specific device. I'm using the Uuid package to generate a UUID for idx.
The only way I can make this work today is to create the idx in my other code that creates the object and pass it in. I read a lot of articles here that talk about different ways to solve this problem and I know I have to make the idx
value a constant but I can't figure out how to do that and call the Uuid library.
I know it would look something like this:
Device(
{this.idx = const <<some calculation/expression>>,
required this.id,
required this.name,
this.icon = 'none',
this.order = -1});
removing the required
modifier and putting a const
before the value assignment. Nothing I've tried lets me call the Uuid method. Can someone help me understand how to do this?
Updating the code based on the answer from @jamesdlin:
import 'package:uuid/uuid.dart';
const uuid = Uuid();
class Device {
String idx;
String id;
String name;
String icon;
int order;
Device(
{String? idx,
required this.id,
required this.name,
this.icon = 'none',
this.order = -1})
: idx = idx ?? uuid.v1();
// Creates a device object from a JSON string
factory Device.fromJson(Map<String, dynamic> jsonData) {
return Device(
idx: jsonData['idx'],
id: jsonData['id'],
name: jsonData['name'],
icon: jsonData['icon'],
order: jsonData['order']);
}
// Returns the device object as a String
static Map<String, dynamic> toMap(Device device) => {
'idx': device.idx,
'id': device.id,
'name': device.name,
'icon': device.icon,
'order': device.order
};
}
This works, but I don't ever have a use case where I want the idx
set manually, so how to I accomplish that? I could leave it like this, but I really want to better understand how to do exactly what I need.
CodePudding user response:
The only way I can make this work today is to create the idx in my other code that creates the object and pass it in.
If you want the object to be able to generate its own UUID, you just can do:
const uuid = Uuid();
class Device {
String idx = uuid.v1(); // Or whatever UUID version you want.
...
or you if you want the caller to have the option to pass in a UUID string, you can use the typical technique of using null
to achieve non-const
default function arguments:
const uuid = Uuid();
class Device
String idx;
Device({String? idx, ...})
: idx = idx ?? uuid.v1(),
...
Note that attempting to make a const
initializer for a UUID makes no sense. const
means that the object is a compile-time constant, and furthermore, const
objects are canonicalized, so a hypothetical const
expression that generated a UUID would end up producing the same String
for every Device
, which would be the opposite of unique.
Update for you updated question
This works, but I don't ever have a use case where I want the
idx
set manually, so how to I accomplish that? I could leave it like this, but I really want to better understand how to do exactly what I need.
I don't understand what you mean since you quite obviously do have a use case for setting idx
manually (your Device.fromJson
factory constructor). If you instead mean that you don't have a use case for code from outside the Device
class to manually set idx
, then you can add a private constructor with the idx
parameter and a public one without:
class Device {
String idx;
String id;
String name;
String icon;
int order;
Device({
required String id,
required String name,
String icon = 'none',
int order = -1,
}) : this._(
idx: uuid.v1(),
id: id,
name: name,
icon: icon,
order: order,
);
Device._({
required this.idx,
required this.id,
required this.name,
required this.icon,
required this.order,
});
// Creates a device object from a JSON string
factory Device.fromJson(Map<String, dynamic> jsonData) {
return Device._(
idx: jsonData['idx'],
id: jsonData['id'],
name: jsonData['name'],
icon: jsonData['icon'],
order: jsonData['order']);
}
}
or, since idx
isn't final
, .fromJson
could assign it a value:
factory Device.fromJson(Map<String, dynamic> jsonData) {
return Device(
id: jsonData['id'],
name: jsonData['name'],
icon: jsonData['icon'],
order: jsonData['order'])
..idx = jsonData['idx'];
}