feat: apply iOS-style theme and replace removed color constants
Switch AppColors to iOS system palette (007AFF blue, F2F2F7 grouped background, separator, label hierarchy) and rewrite AppTheme with iOS-inspired Material 3 tokens (no elevation, negative letter-spacing, 50px buttons, 12px radii). Replace removed primaryLight/accent references in recipe screens with primary.withValues(alpha:0.15) and primary. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,29 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class AppColors {
|
||||
// Primary
|
||||
static const primary = Color(0xFF4CAF50);
|
||||
static const primaryLight = Color(0xFF81C784);
|
||||
static const primaryDark = Color(0xFF388E3C);
|
||||
// iOS System Blue
|
||||
static const primary = Color(0xFF007AFF);
|
||||
|
||||
// Accent
|
||||
static const accent = Color(0xFFFF9800);
|
||||
// Backgrounds — iOS grouped layout
|
||||
static const background = Color(0xFFF2F2F7); // systemGroupedBackground
|
||||
static const surface = Color(0xFFFFFFFF); // secondarySystemGroupedBackground
|
||||
static const surfaceTertiary = Color(0xFFF2F2F7);
|
||||
|
||||
// Background
|
||||
static const background = Color(0xFFF5F5F5);
|
||||
static const surface = Color(0xFFFFFFFF);
|
||||
// Text — iOS label hierarchy
|
||||
static const textPrimary = Color(0xFF000000); // label
|
||||
static const textSecondary = Color(0xFF8E8E93); // secondaryLabel
|
||||
|
||||
// Text
|
||||
static const textPrimary = Color(0xFF212121);
|
||||
static const textSecondary = Color(0xFF757575);
|
||||
// Separator
|
||||
static const separator = Color(0xFFC6C6C8);
|
||||
|
||||
// Status
|
||||
static const error = Color(0xFFE53935);
|
||||
static const warning = Color(0xFFFFA726);
|
||||
static const success = Color(0xFF66BB6A);
|
||||
// iOS System colors
|
||||
static const error = Color(0xFFFF3B30); // systemRed
|
||||
static const warning = Color(0xFFFF9500); // systemOrange
|
||||
static const success = Color(0xFF34C759); // systemGreen
|
||||
|
||||
// Shelf life indicators
|
||||
static const freshGreen = Color(0xFF4CAF50);
|
||||
static const warningYellow = Color(0xFFFFC107);
|
||||
static const expiredRed = Color(0xFFE53935);
|
||||
// Shelf life indicators (same as system colors)
|
||||
static const freshGreen = Color(0xFF34C759);
|
||||
static const warningYellow = Color(0xFFFF9500);
|
||||
static const expiredRed = Color(0xFFFF3B30);
|
||||
}
|
||||
|
||||
@@ -1,30 +1,255 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'app_colors.dart';
|
||||
|
||||
ThemeData appTheme() {
|
||||
final base = ColorScheme.fromSeed(
|
||||
seedColor: AppColors.primary,
|
||||
brightness: Brightness.light,
|
||||
surface: AppColors.surface,
|
||||
onSurface: AppColors.textPrimary,
|
||||
).copyWith(
|
||||
primary: AppColors.primary,
|
||||
onPrimary: Colors.white,
|
||||
secondary: AppColors.primary,
|
||||
onSecondary: Colors.white,
|
||||
tertiary: AppColors.primary,
|
||||
error: AppColors.error,
|
||||
surfaceContainerHighest: AppColors.surfaceTertiary,
|
||||
onSurfaceVariant: AppColors.textSecondary,
|
||||
outline: AppColors.separator,
|
||||
);
|
||||
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
colorSchemeSeed: AppColors.primary,
|
||||
colorScheme: base,
|
||||
scaffoldBackgroundColor: AppColors.background,
|
||||
|
||||
// ── AppBar ────────────────────────────────────────
|
||||
appBarTheme: const AppBarTheme(
|
||||
centerTitle: true,
|
||||
elevation: 0,
|
||||
backgroundColor: AppColors.surface,
|
||||
scrolledUnderElevation: 0,
|
||||
backgroundColor: AppColors.background,
|
||||
foregroundColor: AppColors.textPrimary,
|
||||
systemOverlayStyle: SystemUiOverlayStyle(
|
||||
statusBarBrightness: Brightness.light,
|
||||
statusBarIconBrightness: Brightness.dark,
|
||||
),
|
||||
titleTextStyle: TextStyle(
|
||||
color: AppColors.textPrimary,
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.4,
|
||||
),
|
||||
),
|
||||
|
||||
// ── Card ──────────────────────────────────────────
|
||||
// iOS: white cards on grey background, no shadow
|
||||
cardTheme: CardThemeData(
|
||||
elevation: 0,
|
||||
color: AppColors.surface,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
|
||||
// ── Bottom navigation ─────────────────────────────
|
||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||
backgroundColor: AppColors.surface,
|
||||
selectedItemColor: AppColors.primary,
|
||||
unselectedItemColor: AppColors.textSecondary,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
elevation: 0,
|
||||
selectedLabelStyle: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w500,
|
||||
letterSpacing: -0.2,
|
||||
),
|
||||
unselectedLabelStyle: TextStyle(fontSize: 10),
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
|
||||
// ── Buttons ───────────────────────────────────────
|
||||
filledButtonTheme: FilledButtonThemeData(
|
||||
style: FilledButton.styleFrom(
|
||||
minimumSize: const Size(double.infinity, 50),
|
||||
backgroundColor: AppColors.primary,
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.3,
|
||||
),
|
||||
),
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
minimumSize: const Size(double.infinity, 50),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
minimumSize: const Size(double.infinity, 50),
|
||||
side: const BorderSide(color: AppColors.primary),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w400,
|
||||
letterSpacing: -0.3,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// ── Inputs ────────────────────────────────────────
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: AppColors.surface,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: AppColors.separator),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: AppColors.separator),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: AppColors.primary, width: 1.5),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: AppColors.error),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
hintStyle: const TextStyle(color: AppColors.textSecondary),
|
||||
),
|
||||
|
||||
// ── ListTile ──────────────────────────────────────
|
||||
listTileTheme: const ListTileThemeData(
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 16),
|
||||
minVerticalPadding: 12,
|
||||
),
|
||||
|
||||
// ── Divider ───────────────────────────────────────
|
||||
dividerTheme: const DividerThemeData(
|
||||
color: AppColors.separator,
|
||||
space: 1,
|
||||
thickness: 0.5,
|
||||
indent: 16,
|
||||
),
|
||||
|
||||
// ── Chip ──────────────────────────────────────────
|
||||
chipTheme: ChipThemeData(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
side: const BorderSide(color: AppColors.separator),
|
||||
),
|
||||
|
||||
// ── Dialog ───────────────────────────────────────
|
||||
dialogTheme: DialogThemeData(
|
||||
backgroundColor: AppColors.surface,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
),
|
||||
|
||||
// ── Bottom sheet ─────────────────────────────────
|
||||
bottomSheetTheme: const BottomSheetThemeData(
|
||||
backgroundColor: AppColors.surface,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
||||
),
|
||||
),
|
||||
|
||||
// ── Text ─────────────────────────────────────────
|
||||
textTheme: const TextTheme(
|
||||
headlineLarge: TextStyle(
|
||||
fontSize: 34,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.5,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
headlineMedium: TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.4,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
headlineSmall: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.3,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
titleLarge: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.3,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
titleMedium: TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.3,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
titleSmall: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.2,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
bodyLarge: TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w400,
|
||||
letterSpacing: -0.3,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
bodyMedium: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w400,
|
||||
letterSpacing: -0.2,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
bodySmall: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w400,
|
||||
letterSpacing: -0.1,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
labelLarge: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
letterSpacing: -0.2,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
labelMedium: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
letterSpacing: -0.1,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
labelSmall: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w400,
|
||||
letterSpacing: 0,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user