Home > Enterprise >  Dart Class with final computed default value
Dart Class with final computed default value

Time:04-05

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'];
  }
  •  Tags:  
  • dart
  • Related