chore(studio): RadioGroup migration + second integration test

Two clean-ups:

- `fai_system_ai_editor.dart` uses the new `RadioGroup<T>`
  ancestor pattern Material 3 introduced after Flutter 3.32.
  Same UX (three radios for off/redacted/full), no
  deprecation warnings, `flutter analyze` is now zero-issue
  for the whole project.
- New `install_install_planned_test.dart` asserts that
  installing a `planned` seed entry surfaces a clear,
  human-readable error class (no panic, no silent success).
  Locks in the failed-precondition path Studio's
  friendly-error mapper depends on.

Integration test suite is now 3 scenarios:
  - capabilities_test.dart: system.approval+kind tag
  - install_install_planned_test.dart: planned-install error

All run against a fresh `fai serve` subprocess via
HubFixture, ~2 s total.

Signed-off-by: flemming-it <sf@flemming.it>
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-05-25 14:05:43 +02:00
parent 69b54629d3
commit 21c2a0f411
2 changed files with 94 additions and 45 deletions

View file

@ -553,53 +553,60 @@ class _PrivacyModeChips extends StatelessWidget {
l.systemAiPrivacyFullDesc,
),
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
l.systemAiPrivacyHeader,
style: theme.textTheme.labelSmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
letterSpacing: 0.6,
fontSize: 10,
),
),
const SizedBox(height: 4),
for (final (wire, label, desc) in modes)
InkWell(
onTap: () => onChanged(wire),
borderRadius: BorderRadius.circular(FaiRadius.sm),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: FaiSpace.sm,
horizontal: FaiSpace.sm,
),
child: Row(
children: [
Radio<String>(
value: wire,
groupValue: value,
onChanged: (v) => onChanged(v ?? wire),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: theme.textTheme.bodyMedium),
Text(
desc,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
),
),
],
),
// Material 3 deprecated per-Radio `groupValue` / `onChanged`
// after Flutter 3.32. The modern pattern wraps the radios in
// a RadioGroup<T> ancestor that owns the selection + change
// callback; the individual Radio widgets only declare their
// value. Same UX, no deprecation warnings.
return RadioGroup<String>(
groupValue: value,
onChanged: (v) {
if (v != null) onChanged(v);
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
l.systemAiPrivacyHeader,
style: theme.textTheme.labelSmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
letterSpacing: 0.6,
fontSize: 10,
),
),
],
const SizedBox(height: 4),
for (final (wire, label, desc) in modes)
InkWell(
onTap: () => onChanged(wire),
borderRadius: BorderRadius.circular(FaiRadius.sm),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: FaiSpace.sm,
horizontal: FaiSpace.sm,
),
child: Row(
children: [
Radio<String>(value: wire),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: theme.textTheme.bodyMedium),
Text(
desc,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
),
),
],
),
),
),
],
),
);
}
}