feat(ground-suite): add system glass themes
This commit is contained in:
1 parent
01fb9f7292
commit
fd26a0bd14
9 files changed
+422
-136
No files matched your search
@@ -0,0 +1,121 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../app/theme.dart';
|
||||
|
||||
/// A bounded, translucent surface used for Ground Suite's data panels.
|
||||
///
|
||||
/// The blur is applied only inside the clipped panel, keeping the effect local
|
||||
/// to the UI rather than turning the whole desktop window into a filter.
|
||||
class GlassPanel extends StatelessWidget {
|
||||
const GlassPanel({
|
||||
required this.child,
|
||||
super.key,
|
||||
this.borderRadius = const BorderRadius.all(Radius.circular(16)),
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
final BorderRadius borderRadius;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ClipRRect(
|
||||
borderRadius: borderRadius,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 18, sigmaY: 18),
|
||||
child: _GlassSurface(borderRadius: borderRadius, child: child),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A scroll-friendly translucent panel without a backdrop filter.
|
||||
///
|
||||
/// Timeline rows use this surface so diagnostic volume does not turn into a
|
||||
/// growing set of expensive compositing layers.
|
||||
class GlassListItem extends StatelessWidget {
|
||||
const GlassListItem({
|
||||
required this.child,
|
||||
super.key,
|
||||
this.borderRadius = const BorderRadius.all(Radius.circular(16)),
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
final BorderRadius borderRadius;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) =>
|
||||
_GlassSurface(borderRadius: borderRadius, child: child);
|
||||
}
|
||||
|
||||
class _GlassSurface extends StatelessWidget {
|
||||
const _GlassSurface({required this.child, required this.borderRadius});
|
||||
|
||||
final Widget child;
|
||||
final BorderRadius borderRadius;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
final isDark = scheme.brightness == Brightness.dark;
|
||||
final fill = isDark ? const Color(0xb30a0a0a) : const Color(0xc9ffffff);
|
||||
final border = isDark
|
||||
? scheme.primary.withValues(alpha: 0.24)
|
||||
: scheme.outline.withValues(alpha: 0.7);
|
||||
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: fill,
|
||||
border: Border.all(color: border),
|
||||
borderRadius: borderRadius,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: isDark ? 0.24 : 0.08),
|
||||
blurRadius: 24,
|
||||
offset: const Offset(0, 10),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OrkavianBackdrop extends StatelessWidget {
|
||||
const OrkavianBackdrop({required this.child, super.key});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
final base = isDark ? const Color(0xff050505) : const Color(0xffeef3ee);
|
||||
final haze = isDark
|
||||
? orkavianGreen.withValues(alpha: 0.1)
|
||||
: const Color(0xff007a2e).withValues(alpha: 0.07);
|
||||
|
||||
return ColoredBox(
|
||||
color: base,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: -260,
|
||||
right: -220,
|
||||
child: IgnorePointer(
|
||||
child: Container(
|
||||
width: 620,
|
||||
height: 620,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: RadialGradient(colors: [haze, Colors.transparent]),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned.fill(child: child),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user