generate method

ThemeSpec generate(
  1. int seed,
  2. ThemeFamily family,
  3. int engineVersion, {
  4. bool accessible = false,
  5. int? seedHueTenths,
  6. ThemeIntent? intent,
  7. bool intentCentred = false,
})

The theme for (seed, family, engineVersion) — and the Accessible lock when accessible (labels and display text ≥ 7:1).

Throws UnsupportedEngineVersion for versions this build cannot reproduce, and ArgumentError for ThemeFamily.plain and for families engineVersion has not shipped (their placeholder definitions are not frozen, so their output would not be a pure function of the version).

seedHueTenths (0–3599, M3 Expressive only) is the Android "Match my wallpaper" variant (spec §5.3 #1): it replaces the palette stream's base-hue draw (every other draw is unchanged), the result is marked ThemeVariant.wallpaper, and every guardrail runs exactly as for a generated theme.

intent (engine v4+) steers the theme: steerable families narrow their palette draws to it, every family takes its name, and the result is marked ThemeVariant.steered. intentCentred puts each narrowed choice at the middle of its range (the first theme of a steered feed). An empty intent is no intent. When every attempt fails, the fallback is the hero closest to the intent.

Implementation

ThemeSpec generate(
  int seed,
  ThemeFamily family,
  int engineVersion, {
  bool accessible = false,
  int? seedHueTenths,
  ThemeIntent? intent,
  bool intentCentred = false,
}) {
  if (!supportedEngineVersions.contains(engineVersion)) {
    throw UnsupportedEngineVersion(engineVersion);
  }
  if (family == ThemeFamily.plain) {
    throw ArgumentError.value(family, 'family', 'plain is never generated');
  }
  if (!familyShippedAt(family, engineVersion)) {
    throw ArgumentError.value(
      family,
      'family',
      'not shipped at engine version $engineVersion',
    );
  }
  if (seedHueTenths != null && family != ThemeFamily.m3Expressive) {
    throw ArgumentError.value(
      family,
      'family',
      'only Material 3 Expressive has a wallpaper variant',
    );
  }
  final steering = _steering(
    intent,
    intentCentred,
    engineVersion,
    seedHueTenths,
  );
  final s = seed & 0xFFFFFFFF;
  for (var attempt = 0; attempt < maxAttempts; attempt++) {
    final candidate = buildCandidate(
      s,
      family,
      engineVersion,
      attempt: attempt,
      accessible: accessible,
      seedHueTenths: seedHueTenths,
      intent: steering?.intent,
      intentCentred: intentCentred,
    );
    final fixed = repairTheme(candidate);
    if (fixed != null) return fixed;
  }
  if (steering != null && engineVersion >= 5) {
    // Before a hero: the family's own theme with only its accents
    // turned to the intent (the family's colour rules then hold).
    for (var attempt = 0; attempt < maxAttempts; attempt++) {
      final candidate = _candidate(
        s,
        family,
        engineVersion,
        attempt: attempt,
        accessible: accessible,
        seedHueTenths: seedHueTenths,
        intent: steering.intent,
        intentCentred: intentCentred,
        accentsOnly: true,
      );
      final fixed = repairTheme(candidate);
      if (fixed != null) return fixed;
    }
  }
  if (steering != null) {
    return heroForAccessible(
      steering.closestHero(s, family),
      accessible: accessible,
    );
  }
  return heroFallback(s, family, accessible: accessible);
}