feat(ground-suite): add system glass themes

This commit is contained in:
Onur Özkan committed 2026-08-10 15:55:03 +03:00
1 parent 01fb9f7292
commit fd26a0bd14
9 files changed
+422 -136

No files matched your search

@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/observations/observed_event.dart';
import '../../../shared/widgets/severity_badge.dart';
import '../../../shared/widgets/glass_panel.dart';
import '../../observability/presentation/observability_controller.dart';
enum _DiagnosticFilter { all, logs, errors, shutdowns }
@@ -23,50 +24,74 @@ class _DiagnosticsPageState extends ConsumerState<DiagnosticsPage> {
final state = ref.watch(observabilityControllerProvider);
final events = state.events.where(_matches).toList(growable: false);
return ListView(
padding: const EdgeInsets.all(24),
children: [
Text('Diagnostics', style: Theme.of(context).textTheme.headlineMedium),
const SizedBox(height: 8),
const Text(
'Messages remain distinct by protocol class. A configuration result '
'will remain authoritative when configuration support is added.',
),
const SizedBox(height: 16),
Wrap(
spacing: 8,
runSpacing: 8,
children: _DiagnosticFilter.values
.map(
(filter) => ChoiceChip(
label: Text(_labelFor(filter)),
selected: _filter == filter,
onSelected: (_) => setState(() => _filter = filter),
return CustomScrollView(
slivers: [
SliverPadding(
padding: const EdgeInsets.fromLTRB(24, 24, 24, 16),
sliver: SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Diagnostics',
style: Theme.of(context).textTheme.headlineMedium,
),
)
.toList(growable: false),
),
const SizedBox(height: 12),
TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Filter diagnostics',
prefixIcon: Icon(Icons.search),
const SizedBox(height: 8),
const Text(
'Messages remain distinct by protocol class. A configuration '
'result will remain authoritative when configuration support '
'is added.',
),
const SizedBox(height: 16),
Wrap(
spacing: 8,
runSpacing: 8,
children: _DiagnosticFilter.values
.map(
(filter) => ChoiceChip(
label: Text(_labelFor(filter)),
selected: _filter == filter,
onSelected: (_) => setState(() => _filter = filter),
),
)
.toList(growable: false),
),
const SizedBox(height: 12),
TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Filter diagnostics',
prefixIcon: Icon(Icons.search),
),
onChanged: (value) {
setState(() => _query = value.trim().toLowerCase());
},
),
],
),
),
onChanged: (value) {
setState(() => _query = value.trim().toLowerCase());
},
),
const SizedBox(height: 16),
if (events.isEmpty)
const Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Text('No diagnostic observations match this filter.'),
const SliverPadding(
padding: EdgeInsets.fromLTRB(24, 0, 24, 24),
sliver: SliverToBoxAdapter(
child: GlassPanel(
child: Padding(
padding: EdgeInsets.all(20),
child: Text('No diagnostic observations match this filter.'),
),
),
),
)
else
...events.map((event) => _DiagnosticCard(event: event)),
SliverPadding(
padding: const EdgeInsets.fromLTRB(24, 0, 24, 24),
sliver: SliverList.builder(
itemCount: events.length,
itemBuilder: (context, index) =>
_DiagnosticCard(event: events[index]),
),
),
],
);
}
@@ -119,37 +144,39 @@ class _DiagnosticCard extends StatelessWidget {
return Semantics(
container: true,
label: content.semanticLabel,
child: Card(
margin: const EdgeInsets.only(bottom: 12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
content.leading,
const SizedBox(width: 12),
Expanded(
child: SelectionArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(content.title, style: theme.textTheme.titleMedium),
const SizedBox(height: 4),
Text(content.detail),
if (content.additionalDetail case final detail?) ...[
child: Padding(
padding: const EdgeInsets.only(bottom: 12),
child: GlassListItem(
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
content.leading,
const SizedBox(width: 12),
Expanded(
child: SelectionArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(content.title, style: theme.textTheme.titleMedium),
const SizedBox(height: 4),
Text(content.detail),
if (content.additionalDetail case final detail?) ...[
const SizedBox(height: 8),
Text(detail),
],
const SizedBox(height: 8),
Text(detail),
Text(
event.receivedAt.toIso8601String(),
style: theme.textTheme.bodySmall,
),
],
const SizedBox(height: 8),
Text(
event.receivedAt.toIso8601String(),
style: theme.textTheme.bodySmall,
),
],
),
),
),
),
],
],
),
),
),
),
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../observability/presentation/observability_controller.dart';
import '../../../shared/widgets/glass_panel.dart';
class OverviewPage extends ConsumerWidget {
const OverviewPage({super.key});
@@ -78,7 +79,7 @@ class OverviewPage extends ConsumerWidget {
],
),
const SizedBox(height: 24),
Card(
GlassPanel(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
@@ -125,7 +126,7 @@ class _SummaryCard extends StatelessWidget {
Widget build(BuildContext context) {
return SizedBox(
width: width,
child: Card(
child: GlassPanel(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(