What's the better way to expose constant string value in a class that can be used in Switch case? [dart]

Viewed 308

So this is the code, which won't work due to "Case expressions must be constant"

void main() {
  final id = 'someIdA';

  switch (id) {
    case ConstantsClass.a.id:
      // do something
      break;
    case ConstantsClass.b.id:
      // do something
      break;
  }
}

// exposing all the constants but grouping the constants in class by class fashion
class ConstantsClass {
  static _A get a => _A();
  static _B get b => _B();
}

// wrapping all the constants related to A
class _A {
  final id = 'someIdA';
}

// wrapping all the constants related to B
class _B {
  final id = 'someIdB';
}

And I have read posts about this "Case expressions must be constant" issue: 1 2

But I don't seem to find an answer from those posts.

And I also try to find another way to do use a class that contains only constant fields: What's the best practice to keep all the constants in Flutter? [closed] which also gives me not much hope...

And I think there's no way for me to use class with constant fields like ConstantsClass in the code here, the best I can get is to put all the constant values in one class, which I think will be very messy...

so is there a better way?

edit1:

I have a lot of String literal values that I want to manage using class, like in the case that's discussed in Effective Dart

So I can't use enum, unless I need to convert every string literal to enum, which I'm not sure if it's possible...

Thanks!

2 Answers

First, classes _A and _B don't have const members, so fix that. const class members must be static.

class _A {
  static const id = 'someIdA';
}

class _B {
  static const id = 'someIdB';
}

But now they are static, so you won't be able to access them via the ConstantsClass.a and ConstantsClass.b getters. However, I argue that those getters are rather pointless. That is, if you rename your classes:

class ConstantsClassA {
  static const id = 'someIdA';
}

class ConstantsClassB {
  static const id = 'someIdB';
}

and get rid of the ConstantsClass entirely, it doesn't make any difference to callers whether they use ConstantsClass.a.id or ConstantsClassA.id.

If you really want your classes to be grouped under some common namespace, you instead could name those classes A and B, put them in a separate .dart file, and always expect consumers to import that file with a prefix. For example:

import 'my_constants.dart' as myConstants;

void main() {
  final id = 'someIdA';

  switch (id) {
    case myConstants.A.id:
      // do something
      break;
    case myConstants.B.id:
      // do something
      break;
  }
}

Ok, this is how I keep track of String literals, so that I don't have to worry about mis-spelling them, and it's also really quick and easy to access them without having to type the whole name:

//Keep these in a file called constants.dart
//and let all begin with a k:
const String kIdA = 'someIdA';
const String kIdB = 'someIdB';


import 'constants.dart';

void main() {
  final id = 'someIdA';

  switch (id) {
    case kIdA:  //Here, when you use your constants, you just type kia, 
    // and the IDE will fill in the constant name.
    
    // do something
      break;
    case kIdB:
    // do something
      break;
  }
}

If you still insist on a version using a class, I think this should work better:

void main() {
  final id = 'someIdA';

  switch (id) {
    case ConstantsClass.idA:
    // do something
      break;
    case ConstantsClass.idB:
    // do something
      break;
  }
}

class ConstantsClass {
  static const idA = 'someIdA';
  static const idB = 'someIdB';
  static const idC = 'someIdC';
  static const idD = 'someIdD';
}
Related