Files
2026-08-11 00:11:23 +03:00

173 lines
5.3 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'colors.dart';
/// -----------------------------------------------------------------------------
/// Tripz Design Tokens
/// -----------------------------------------------------------------------------
/// A single source of truth for spacing, radii, shadows, durations and the
/// shared bottom-sheet surface used across the passenger map overlays.
///
/// Goal: kill the "magic numbers" scattered across every map widget
/// (radii of 15/24/25, ad-hoc shadows, handles that appear only sometimes)
/// so all sheets read as ONE consistent product — the way Uber / Careem / Bolt
/// feel like a single system rather than a pile of separate screens.
///
/// Everything here is additive. Existing widgets keep working untouched; new
/// and refactored widgets opt in.
/// 4pt spacing scale. Use these instead of raw `SizedBox(height: 10)`.
class AppSpacing {
AppSpacing._();
static const double xs = 4;
static const double sm = 8;
static const double md = 12;
static const double lg = 16;
static const double xl = 20;
static const double xxl = 24;
// Convenience gaps (const so they can live in const child lists).
static const Widget gapXs = SizedBox(height: xs, width: xs);
static const Widget gapSm = SizedBox(height: sm, width: sm);
static const Widget gapMd = SizedBox(height: md, width: md);
static const Widget gapLg = SizedBox(height: lg, width: lg);
static const Widget gapXl = SizedBox(height: xl, width: xl);
static const Widget vGapXs = SizedBox(height: xs);
static const Widget vGapSm = SizedBox(height: sm);
static const Widget vGapMd = SizedBox(height: md);
static const Widget vGapLg = SizedBox(height: lg);
static const Widget vGapXl = SizedBox(height: xl);
}
/// Corner radii. `sheet` is THE radius for every bottom sheet so they all match.
class AppRadii {
AppRadii._();
static const double sm = 8;
static const double md = 12;
static const double lg = 16;
static const double sheet = 24;
static const double pill = 999;
static const BorderRadius sheetTop = BorderRadius.only(
topLeft: Radius.circular(sheet),
topRight: Radius.circular(sheet),
);
static BorderRadius all(double r) => BorderRadius.circular(r);
}
/// Shared elevation. One shadow for sheets, one for cards — no more per-widget
/// hand-tuned `BoxShadow`s (and no neumorphic accent-color shadows).
class AppShadows {
AppShadows._();
static List<BoxShadow> get sheet => [
BoxShadow(
color: Colors.black.withValues(
alpha: Get.isDarkMode ? 0.45 : 0.12,
),
blurRadius: 24,
spreadRadius: 1,
offset: const Offset(0, -4),
),
];
static List<BoxShadow> get card => [
BoxShadow(
color: Colors.black.withValues(
alpha: Get.isDarkMode ? 0.30 : 0.06,
),
blurRadius: 12,
offset: const Offset(0, 4),
),
];
}
/// Motion durations. Keep sheet slide-in/out consistent everywhere.
class AppDurations {
AppDurations._();
static const Duration fast = Duration(milliseconds: 200);
static const Duration base = Duration(milliseconds: 300);
static const Duration slow = Duration(milliseconds: 450);
}
/// Minimum touch target (Material / iOS HIG guidance is 44–48px).
class AppSizes {
AppSizes._();
static const double minTouch = 48;
}
/// -----------------------------------------------------------------------------
/// Shared bottom-sheet surface
/// -----------------------------------------------------------------------------
/// The rounded-top card + shadow + optional drag handle that every map sheet
/// draws by hand today. Wrap your content in this and all sheets instantly
/// share the same radius, shadow, handle and horizontal rhythm.
///
/// It does NOT own show/hide animation — each caller keeps its own
/// AnimatedPositioned / transform logic so behaviour is unchanged.
class TripzSheetSurface extends StatelessWidget {
const TripzSheetSurface({
super.key,
required this.child,
this.padding =
const EdgeInsets.fromLTRB(AppSpacing.xl, AppSpacing.md, AppSpacing.xl, AppSpacing.lg),
this.showHandle = true,
this.color,
});
final Widget child;
final EdgeInsets padding;
final bool showHandle;
final Color? color;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: BoxDecoration(
color: color ?? AppColor.cardColor,
borderRadius: AppRadii.sheetTop,
boxShadow: AppShadows.sheet,
),
padding: padding,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (showHandle) ...[
const TripzSheetHandle(),
AppSpacing.vGapMd,
],
child,
],
),
);
}
}
/// The little grab handle at the top of a sheet. Consistent size everywhere.
class TripzSheetHandle extends StatelessWidget {
const TripzSheetHandle({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: AppColor.grayColor.withValues(alpha: 0.35),
borderRadius: BorderRadius.circular(AppRadii.pill),
),
),
);
}
}