Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Native crypto / native-crypto (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s
103 lines
2.6 KiB
Dart
103 lines
2.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../../core/config/app_shape.dart';
|
|
|
|
/// Standard rounded top shape for modal bottom sheets.
|
|
const RoundedRectangleBorder kSheetShape = AppShape.sheetBorder;
|
|
|
|
/// Pill-shaped action button for the bottom row of a modal sheet.
|
|
class SheetButton extends StatelessWidget {
|
|
final String label;
|
|
final bool filled;
|
|
final VoidCallback? onTap;
|
|
final Color? color;
|
|
|
|
const SheetButton({
|
|
super.key,
|
|
required this.label,
|
|
required this.filled,
|
|
required this.onTap,
|
|
this.color,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final cs = Theme.of(context).colorScheme;
|
|
final disabled = onTap == null;
|
|
final fill = color ?? cs.primary;
|
|
final labelColor = filled ? cs.onPrimary : (color ?? cs.onSurface);
|
|
return GestureDetector(
|
|
onTap: onTap,
|
|
child: Container(
|
|
height: 44,
|
|
alignment: Alignment.center,
|
|
decoration: BoxDecoration(
|
|
color: filled
|
|
? (disabled ? fill.withValues(alpha: 0.4) : fill)
|
|
: cs.surfaceContainerHighest,
|
|
borderRadius: BorderRadius.circular(22),
|
|
),
|
|
child: Text(
|
|
label,
|
|
style: TextStyle(
|
|
color: disabled
|
|
? labelColor.withValues(alpha: filled ? 0.85 : 0.4)
|
|
: labelColor,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Grabber row that keeps the pill centred while an action sits at the edge.
|
|
class SheetGrabberBar extends StatelessWidget {
|
|
static const double height = 34;
|
|
static const double actionInset = 8;
|
|
|
|
final Widget action;
|
|
|
|
const SheetGrabberBar({super.key, required this.action});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SizedBox(
|
|
width: double.infinity,
|
|
height: height,
|
|
child: Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
const SheetGrabber(margin: EdgeInsets.zero),
|
|
Positioned(right: actionInset, child: action),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// The little drag "grabber" pill shown at the top of a bottom sheet.
|
|
class SheetGrabber extends StatelessWidget {
|
|
final EdgeInsetsGeometry margin;
|
|
|
|
const SheetGrabber({
|
|
super.key,
|
|
this.margin = const EdgeInsets.symmetric(vertical: 10),
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final cs = Theme.of(context).colorScheme;
|
|
return Container(
|
|
width: 40,
|
|
height: 4,
|
|
margin: margin,
|
|
decoration: BoxDecoration(
|
|
color: cs.onSurfaceVariant.withValues(alpha: 0.35),
|
|
borderRadius: BorderRadius.circular(2),
|
|
),
|
|
);
|
|
}
|
|
}
|