nanoid function

String nanoid({
  1. int length = 21,
  2. String? alphabet,
  3. Random? random,
})

This generator function create a tiny, secure, URL-friendly, unique string ID

The length of the nanoid is adjustable (default is 21 characters). The characters are taken from alphabet (Defaults to Alphabet.url [a-zA-Z0-9_-]) at random order.

Examples:

BGmaV2KoFx0Ar2yS6zMDc
TZGlaZw38c43IILQQ-Jxc
n_vyOfT-Q9hwWyYQZSYop
nMbnyHZiCV_NGgXNdANLX
0hpndh5fAcjBU3ZTABFdR

nonoid uses Random.secure by default. When platforms don't support it, it falls back to Random.

For predictable ID generation during tests, you can pass a seeded random number generator.

final random = Random(42);
for (int i = 0; i < 3; i++) {
  final id = nanoid(random: random);
  print(id);
}
// NI2rPCdXKSZyNNboBnBIy
// oduoyIeuj6itVcufRe11I
// 8rnfaUlBaqZ97p13lEhsT

Implementation

String nanoid({
  int length = 21,
  String? alphabet,
  Random? random,
}) {
  if (length < 2) {
    throw ArgumentError.value(
      length,
      "length",
      "Length must be greater than 2",
    );
  }
  if (length > 255) {
    throw ArgumentError.value(length, "length", "Length must be less than 255");
  }
  int i = length;
  if (alphabet != null && alphabet.isEmpty) {
    throw ArgumentError.value(
      alphabet,
      "alphabet",
      "Alphabet must not be empty",
    );
  }

  final String characters = alphabet ?? Alphabet.url;
  final int alphabetSize = characters.length;
  assert(alphabetSize > 0);

  final Random secureRandom = random ?? Random.secure();
  Random? insecureRandom;

  int nextRandomIndex() {
    try {
      return secureRandom.nextInt(alphabetSize);
      // ignore: avoid_catching_errors
    } on UnsupportedError {
      // Random.secure() is not supported on this platform, fallback to the insecure version
      insecureRandom ??= Random();
      return insecureRandom!.nextInt(alphabetSize);
    }
  }

  final sb = StringBuffer();
  while (0 < i--) {
    final randomIndex = nextRandomIndex();
    final character = characters[randomIndex];
    sb.write(character);
  }
  return sb.toString();
}