Mitra §A: pre-home (S3a/S3b/AccountInactive) + design system + Bestie Home
- Port halo_tokens + halo_theme + HaloButton to mitra_app (rose palette, Bricolage display, Poppins body, JetBrainsMono). - Build S3a Input WhatsApp (figma-bestie BestieS3 first half) with +62 chip, leading-zero/62 normalization, allow '+' in input. - Build S3b OTP verification (6-digit, 60s resend timer, attempts hint, Focus(canRequestFocus:false) for maestro inputText compat) with full error branching (CODE_MISMATCH, OTP_EXPIRED, OTP_USED, ATTEMPTS_EXCEEDED, WRONG_FLOW, ACCOUNT_INACTIVE). - Add AccountInactive terminal screen for is_active=false mitras. - Typed MitraAuthError with Indonesian-first localized messages + retryAfterSeconds passthrough. - Rebuild home_screen.dart to match figma BestieHome (greeting + status card + Ganti Status CTA + Pengingat + 2-tile dark grid). - Backend: POST /internal/_test/seed-mitra (idempotent) and PATCH /internal/mitras/:id (display_name update). - Control center: inline Edit Nama on mitras row + expandable inline log table under clicked mitra (vs old below-table panel). - 5 maestro flows ts-mitra-A-01/03/04/05/06 covering invalid input, happy path, account inactive, phone-format normalization, and the back-to-S3a regression. All green. Plan + memory documented in: - requirement/phase4-mitra-prehome-plan.md - requirement/flow_mitra.md / flow_mitra.mermaid.md §A Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
316
mitra_app/lib/core/theme/halo_theme.dart
Normal file
316
mitra_app/lib/core/theme/halo_theme.dart
Normal file
@@ -0,0 +1,316 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'halo_tokens.dart';
|
||||
|
||||
ThemeData haloThemeData() {
|
||||
final base = ColorScheme.fromSeed(
|
||||
seedColor: HaloTokens.brand,
|
||||
brightness: Brightness.light,
|
||||
);
|
||||
|
||||
final colorScheme = base.copyWith(
|
||||
primary: HaloTokens.brand,
|
||||
onPrimary: Colors.white,
|
||||
primaryContainer: HaloTokens.brandSoft,
|
||||
onPrimaryContainer: HaloTokens.brandDark,
|
||||
secondary: HaloTokens.accent,
|
||||
onSecondary: HaloTokens.ink,
|
||||
secondaryContainer: HaloTokens.accentSoft,
|
||||
onSecondaryContainer: HaloTokens.brandDark,
|
||||
surface: HaloTokens.surface,
|
||||
onSurface: HaloTokens.ink,
|
||||
surfaceContainerHighest: HaloTokens.bg,
|
||||
error: HaloTokens.danger,
|
||||
onError: Colors.white,
|
||||
outline: HaloTokens.border,
|
||||
);
|
||||
|
||||
const textTheme = TextTheme(
|
||||
displayLarge: TextStyle(
|
||||
fontFamily: HaloTokens.fontDisplay,
|
||||
fontSize: 36,
|
||||
height: 40 / 36,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.5,
|
||||
color: HaloTokens.ink,
|
||||
),
|
||||
displayMedium: TextStyle(
|
||||
fontFamily: HaloTokens.fontDisplay,
|
||||
fontSize: 30,
|
||||
height: 34 / 30,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.4,
|
||||
color: HaloTokens.ink,
|
||||
),
|
||||
displaySmall: TextStyle(
|
||||
fontFamily: HaloTokens.fontDisplay,
|
||||
fontSize: 26,
|
||||
height: 30 / 26,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.3,
|
||||
color: HaloTokens.ink,
|
||||
),
|
||||
headlineMedium: TextStyle(
|
||||
fontFamily: HaloTokens.fontDisplay,
|
||||
fontSize: 22,
|
||||
height: 28 / 22,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: HaloTokens.ink,
|
||||
),
|
||||
titleLarge: TextStyle(
|
||||
fontFamily: HaloTokens.fontDisplay,
|
||||
fontSize: 22,
|
||||
height: 28 / 22,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: HaloTokens.ink,
|
||||
),
|
||||
titleMedium: TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 18,
|
||||
height: 24 / 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: HaloTokens.ink,
|
||||
),
|
||||
titleSmall: TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 15,
|
||||
height: 22 / 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: HaloTokens.ink,
|
||||
),
|
||||
bodyLarge: TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 16,
|
||||
height: 24 / 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: HaloTokens.ink,
|
||||
),
|
||||
bodyMedium: TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 15,
|
||||
height: 22 / 15,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: HaloTokens.ink,
|
||||
),
|
||||
bodySmall: TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 13,
|
||||
height: 18 / 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: HaloTokens.inkSoft,
|
||||
),
|
||||
labelLarge: TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 15,
|
||||
height: 22 / 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: HaloTokens.ink,
|
||||
),
|
||||
labelMedium: TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 12,
|
||||
height: 16 / 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: HaloTokens.inkSoft,
|
||||
),
|
||||
labelSmall: TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 10,
|
||||
height: 14 / 10,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: 0.4,
|
||||
color: HaloTokens.inkMuted,
|
||||
),
|
||||
);
|
||||
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: colorScheme,
|
||||
scaffoldBackgroundColor: HaloTokens.bg,
|
||||
textTheme: textTheme,
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: HaloTokens.bg,
|
||||
foregroundColor: HaloTokens.ink,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
centerTitle: false,
|
||||
titleTextStyle: TextStyle(
|
||||
fontFamily: HaloTokens.fontDisplay,
|
||||
fontSize: 22,
|
||||
height: 28 / 22,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: HaloTokens.ink,
|
||||
),
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: HaloTokens.brand,
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: HaloTokens.brandSoft,
|
||||
disabledForegroundColor: HaloTokens.inkMuted,
|
||||
elevation: 0,
|
||||
shadowColor: const Color(0x59E17A9D),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: HaloSpacing.s24,
|
||||
vertical: HaloSpacing.s16,
|
||||
),
|
||||
shape: const RoundedRectangleBorder(borderRadius: HaloRadius.pill),
|
||||
textStyle: const TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: HaloTokens.brandDark,
|
||||
side: const BorderSide(color: HaloTokens.border),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: HaloSpacing.s24,
|
||||
vertical: HaloSpacing.s16,
|
||||
),
|
||||
shape: const RoundedRectangleBorder(borderRadius: HaloRadius.pill),
|
||||
textStyle: const TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: HaloTokens.brandDark,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: HaloSpacing.s16,
|
||||
vertical: HaloSpacing.s12,
|
||||
),
|
||||
shape: const RoundedRectangleBorder(borderRadius: HaloRadius.pill),
|
||||
textStyle: const TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: const InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: HaloTokens.surface,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: HaloSpacing.s20,
|
||||
vertical: HaloSpacing.s20,
|
||||
),
|
||||
constraints: BoxConstraints(minHeight: 64),
|
||||
hintStyle: TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 15,
|
||||
color: HaloTokens.inkMuted,
|
||||
),
|
||||
labelStyle: TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 13,
|
||||
color: HaloTokens.inkSoft,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: HaloRadius.lg,
|
||||
borderSide: BorderSide(color: HaloTokens.border),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: HaloRadius.lg,
|
||||
borderSide: BorderSide(color: HaloTokens.border),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: HaloRadius.lg,
|
||||
borderSide: BorderSide(color: HaloTokens.brand, width: 2),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: HaloRadius.lg,
|
||||
borderSide: BorderSide(color: HaloTokens.danger),
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderRadius: HaloRadius.lg,
|
||||
borderSide: BorderSide(color: HaloTokens.danger, width: 2),
|
||||
),
|
||||
),
|
||||
bottomSheetTheme: const BottomSheetThemeData(
|
||||
backgroundColor: HaloTokens.surface,
|
||||
surfaceTintColor: HaloTokens.surface,
|
||||
modalBackgroundColor: HaloTokens.surface,
|
||||
modalBarrierColor: Color(0x66000000),
|
||||
elevation: 0,
|
||||
modalElevation: 0,
|
||||
showDragHandle: true,
|
||||
dragHandleColor: HaloTokens.brandSoft,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(24),
|
||||
topRight: Radius.circular(24),
|
||||
),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
),
|
||||
dialogTheme: const DialogThemeData(
|
||||
backgroundColor: HaloTokens.surface,
|
||||
surfaceTintColor: HaloTokens.surface,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(borderRadius: HaloRadius.xl),
|
||||
titleTextStyle: TextStyle(
|
||||
fontFamily: HaloTokens.fontDisplay,
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: HaloTokens.ink,
|
||||
),
|
||||
contentTextStyle: TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 15,
|
||||
height: 22 / 15,
|
||||
color: HaloTokens.inkSoft,
|
||||
),
|
||||
),
|
||||
snackBarTheme: const SnackBarThemeData(
|
||||
behavior: SnackBarBehavior.floating,
|
||||
backgroundColor: HaloTokens.ink,
|
||||
contentTextStyle: TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
shape: RoundedRectangleBorder(borderRadius: HaloRadius.pill),
|
||||
elevation: 4,
|
||||
insetPadding: EdgeInsets.symmetric(
|
||||
horizontal: HaloSpacing.s16,
|
||||
vertical: HaloSpacing.s12,
|
||||
),
|
||||
actionTextColor: HaloTokens.brandSoft,
|
||||
),
|
||||
chipTheme: ChipThemeData(
|
||||
backgroundColor: HaloTokens.surface,
|
||||
selectedColor: HaloTokens.brand,
|
||||
disabledColor: HaloTokens.brandSoft.withValues(alpha: 0.5),
|
||||
labelStyle: const TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: HaloTokens.ink,
|
||||
),
|
||||
secondaryLabelStyle: const TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: HaloSpacing.s16,
|
||||
vertical: HaloSpacing.s8,
|
||||
),
|
||||
side: const BorderSide(color: HaloTokens.border),
|
||||
shape: const RoundedRectangleBorder(borderRadius: HaloRadius.pill),
|
||||
),
|
||||
dividerTheme: const DividerThemeData(
|
||||
color: HaloTokens.border,
|
||||
thickness: 1,
|
||||
space: 1,
|
||||
),
|
||||
);
|
||||
}
|
||||
132
mitra_app/lib/core/theme/halo_tokens.dart
Normal file
132
mitra_app/lib/core/theme/halo_tokens.dart
Normal file
@@ -0,0 +1,132 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Design tokens for the HaloBestie warm palette.
|
||||
///
|
||||
/// Mirrors `requirement/Figma/handoff/tokens.json`. Three palettes
|
||||
/// (warm/calm/playful) exist in the source-of-truth JSON; only `warm`
|
||||
/// ships in code today — the others are stubbed for phase 5.
|
||||
///
|
||||
/// Naming convention: every token prefixed with `Halo*` and grouped into
|
||||
/// purpose classes (`HaloTokens` for colors, `HaloSpacing`, `HaloRadius`,
|
||||
/// `HaloMotion`, `HaloShadows`).
|
||||
class HaloTokens {
|
||||
const HaloTokens._();
|
||||
|
||||
// Warm palette — default.
|
||||
static const Color bg = Color(0xFFFDF7F4);
|
||||
static const Color surface = Color(0xFFFFFFFF);
|
||||
static const Color ink = Color(0xFF2A1820);
|
||||
static const Color inkSoft = Color(0xFF6B5560);
|
||||
static const Color inkMuted = Color(0xFF9C8590);
|
||||
static const Color brand = Color(0xFFE17A9D);
|
||||
static const Color brandDark = Color(0xFF8C3255);
|
||||
static const Color brandSoft = Color(0xFFF7E4E9);
|
||||
static const Color brandSofter = Color(0xFFFBEFF3);
|
||||
// Launcher-icon background. Use this pink behind monochrome/white logos.
|
||||
// For full-color logos, use `surface` (#FFFFFF) as the icon background.
|
||||
static const Color brandLogoBg = Color(0xFFFF699F);
|
||||
static const Color accent = Color(0xFFF7B26A);
|
||||
static const Color accentSoft = Color(0xFFFCEAD3);
|
||||
static const Color mint = Color(0xFFB8DBC8);
|
||||
static const Color lilac = Color(0xFFD4C5E8);
|
||||
static const Color success = Color(0xFF5BA67F);
|
||||
static const Color danger = Color(0xFFD86B6B);
|
||||
static const Color border = Color(0xFFF0E4E8);
|
||||
|
||||
// Font family names — must match the `family:` entries in pubspec.yaml.
|
||||
// Falls back to system fonts when the .ttf assets are not bundled.
|
||||
static const String fontDisplay = 'BricolageGrotesque';
|
||||
static const String fontBody = 'Poppins';
|
||||
static const String fontMono = 'JetBrainsMono';
|
||||
|
||||
// TODO: phase5 — calm palette
|
||||
// static const Color calmBg = Color(0xFFF6F4F8);
|
||||
// static const Color calmBrand = Color(0xFF9B8BC4);
|
||||
// ...
|
||||
|
||||
// TODO: phase5 — playful palette
|
||||
// static const Color playfulBg = Color(0xFFFFF5F8);
|
||||
// static const Color playfulBrand = Color(0xFFFF69A0);
|
||||
// ...
|
||||
}
|
||||
|
||||
class HaloSpacing {
|
||||
const HaloSpacing._();
|
||||
|
||||
static const double s0 = 0;
|
||||
static const double s4 = 4;
|
||||
static const double s8 = 8;
|
||||
static const double s12 = 12;
|
||||
static const double s16 = 16;
|
||||
static const double s20 = 20;
|
||||
static const double s24 = 24;
|
||||
static const double s32 = 32;
|
||||
static const double s40 = 40;
|
||||
static const double s48 = 48;
|
||||
static const double s64 = 64;
|
||||
static const double s80 = 80;
|
||||
}
|
||||
|
||||
class HaloRadius {
|
||||
const HaloRadius._();
|
||||
|
||||
static const Radius _sm = Radius.circular(8);
|
||||
static const Radius _md = Radius.circular(12);
|
||||
static const Radius _lg = Radius.circular(16);
|
||||
static const Radius _xl = Radius.circular(22);
|
||||
static const Radius _pill = Radius.circular(9999);
|
||||
|
||||
static const BorderRadius sm = BorderRadius.all(_sm);
|
||||
static const BorderRadius md = BorderRadius.all(_md);
|
||||
static const BorderRadius lg = BorderRadius.all(_lg);
|
||||
static const BorderRadius xl = BorderRadius.all(_xl);
|
||||
static const BorderRadius pill = BorderRadius.all(_pill);
|
||||
}
|
||||
|
||||
class HaloMotion {
|
||||
const HaloMotion._();
|
||||
|
||||
static const Duration fast = Duration(milliseconds: 180);
|
||||
static const Duration normal = Duration(milliseconds: 280);
|
||||
static const Duration slow = Duration(milliseconds: 420);
|
||||
|
||||
static const Cubic ease = Cubic(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
|
||||
class HaloShadows {
|
||||
const HaloShadows._();
|
||||
|
||||
static const List<BoxShadow> soft = [
|
||||
BoxShadow(
|
||||
color: Color(0x0A8C3255),
|
||||
offset: Offset(0, 1),
|
||||
blurRadius: 2,
|
||||
),
|
||||
BoxShadow(
|
||||
color: Color(0x0F8C3255),
|
||||
offset: Offset(0, 8),
|
||||
blurRadius: 24,
|
||||
),
|
||||
];
|
||||
|
||||
static const List<BoxShadow> card = [
|
||||
BoxShadow(
|
||||
color: Color(0x0D8C3255),
|
||||
offset: Offset(0, 2),
|
||||
blurRadius: 6,
|
||||
),
|
||||
BoxShadow(
|
||||
color: Color(0x1A8C3255),
|
||||
offset: Offset(0, 18),
|
||||
blurRadius: 40,
|
||||
),
|
||||
];
|
||||
|
||||
static const List<BoxShadow> button = [
|
||||
BoxShadow(
|
||||
color: Color(0x59E17A9D),
|
||||
offset: Offset(0, 4),
|
||||
blurRadius: 14,
|
||||
),
|
||||
];
|
||||
}
|
||||
152
mitra_app/lib/core/theme/widgets/halo_button.dart
Normal file
152
mitra_app/lib/core/theme/widgets/halo_button.dart
Normal file
@@ -0,0 +1,152 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../halo_tokens.dart';
|
||||
|
||||
enum HaloButtonVariant { primary, secondary, ghost }
|
||||
|
||||
enum HaloButtonSize { sm, md, lg }
|
||||
|
||||
class HaloButton extends StatelessWidget {
|
||||
const HaloButton({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.onPressed,
|
||||
this.variant = HaloButtonVariant.primary,
|
||||
this.size = HaloButtonSize.md,
|
||||
this.icon,
|
||||
this.fullWidth = false,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final VoidCallback? onPressed;
|
||||
final HaloButtonVariant variant;
|
||||
final HaloButtonSize size;
|
||||
final Widget? icon;
|
||||
final bool fullWidth;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final disabled = onPressed == null;
|
||||
final padding = _padding();
|
||||
final fontSize = _fontSize();
|
||||
const shape = RoundedRectangleBorder(borderRadius: HaloRadius.pill);
|
||||
final textStyle = TextStyle(
|
||||
fontFamily: HaloTokens.fontBody,
|
||||
fontSize: fontSize,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
|
||||
Widget child = _content(textStyle);
|
||||
|
||||
Widget button;
|
||||
switch (variant) {
|
||||
case HaloButtonVariant.primary:
|
||||
button = Container(
|
||||
decoration: disabled
|
||||
? null
|
||||
: const BoxDecoration(
|
||||
borderRadius: HaloRadius.pill,
|
||||
boxShadow: HaloShadows.button,
|
||||
),
|
||||
child: ElevatedButton(
|
||||
onPressed: onPressed,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: HaloTokens.brand,
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: HaloTokens.brandSoft,
|
||||
disabledForegroundColor: HaloTokens.inkMuted,
|
||||
elevation: 0,
|
||||
padding: padding,
|
||||
shape: shape,
|
||||
textStyle: textStyle,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
break;
|
||||
case HaloButtonVariant.secondary:
|
||||
button = OutlinedButton(
|
||||
onPressed: onPressed,
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: HaloTokens.brandDark,
|
||||
disabledForegroundColor: HaloTokens.inkMuted,
|
||||
backgroundColor: HaloTokens.surface,
|
||||
side: BorderSide(
|
||||
color: disabled ? HaloTokens.border : HaloTokens.brandSoft,
|
||||
),
|
||||
padding: padding,
|
||||
shape: shape,
|
||||
textStyle: textStyle,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
break;
|
||||
case HaloButtonVariant.ghost:
|
||||
button = TextButton(
|
||||
onPressed: onPressed,
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: HaloTokens.brandDark,
|
||||
disabledForegroundColor: HaloTokens.inkMuted,
|
||||
padding: padding,
|
||||
shape: shape,
|
||||
textStyle: textStyle,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if (fullWidth) {
|
||||
return SizedBox(width: double.infinity, child: button);
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
||||
Widget _content(TextStyle textStyle) {
|
||||
if (icon == null) {
|
||||
return Text(label, style: textStyle);
|
||||
}
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconTheme(
|
||||
data: IconThemeData(size: textStyle.fontSize! + 2),
|
||||
child: icon!,
|
||||
),
|
||||
const SizedBox(width: HaloSpacing.s8),
|
||||
Text(label, style: textStyle),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
EdgeInsets _padding() {
|
||||
switch (size) {
|
||||
case HaloButtonSize.sm:
|
||||
return const EdgeInsets.symmetric(
|
||||
horizontal: HaloSpacing.s16,
|
||||
vertical: HaloSpacing.s8,
|
||||
);
|
||||
case HaloButtonSize.md:
|
||||
return const EdgeInsets.symmetric(
|
||||
horizontal: HaloSpacing.s24,
|
||||
vertical: HaloSpacing.s12,
|
||||
);
|
||||
case HaloButtonSize.lg:
|
||||
return const EdgeInsets.symmetric(
|
||||
horizontal: HaloSpacing.s32,
|
||||
vertical: HaloSpacing.s16,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
double _fontSize() {
|
||||
switch (size) {
|
||||
case HaloButtonSize.sm:
|
||||
return 13;
|
||||
case HaloButtonSize.md:
|
||||
return 15;
|
||||
case HaloButtonSize.lg:
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
mitra_app/lib/core/theme/widgets/widgets.dart
Normal file
1
mitra_app/lib/core/theme/widgets/widgets.dart
Normal file
@@ -0,0 +1 @@
|
||||
export 'halo_button.dart';
|
||||
Reference in New Issue
Block a user