refactor: update design system to Editorial Organicism theme with zero-radius components and new typography

This commit is contained in:
Suherdy Yacob 2026-04-03 16:54:05 +07:00
parent 8431a8dc13
commit b0b75de3b6
5 changed files with 108 additions and 112 deletions

View File

@ -83,9 +83,9 @@ class _BranchesScreenState extends State<BranchesScreen> {
return Container( return Container(
margin: const EdgeInsets.only(bottom: 16), margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration( decoration: const BoxDecoration(
color: AppTheme.surfaceContainerLow, color: AppTheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.zero,
// Spec rules: "Don't use standard drop shadows" // Spec rules: "Don't use standard drop shadows"
), ),
child: ListTile( child: ListTile(
@ -95,7 +95,7 @@ class _BranchesScreenState extends State<BranchesScreen> {
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppTheme.secondaryContainer.withOpacity(0.2), color: AppTheme.secondaryContainer.withOpacity(0.2),
shape: BoxShape.circle, shape: BoxShape.rectangle,
), ),
child: const Icon(Icons.storefront, color: AppTheme.secondary), child: const Icon(Icons.storefront, color: AppTheme.secondary),
), ),
@ -116,9 +116,9 @@ class _BranchesScreenState extends State<BranchesScreen> {
const SizedBox(height: 4), const SizedBox(height: 4),
Row( Row(
children: [ children: [
const Icon(Icons.phone, size: 14, color: AppTheme.primary), const Icon(Icons.phone, size: 14, color: AppTheme.onSurfaceVariant),
const SizedBox(width: 4), const SizedBox(width: 4),
Text(phone, style: Theme.of(context).textTheme.bodySmall?.copyWith(color: AppTheme.primary)), Text(phone, style: Theme.of(context).textTheme.bodySmall?.copyWith(color: AppTheme.onSurfaceVariant)),
], ],
), ),
] ]
@ -127,7 +127,7 @@ class _BranchesScreenState extends State<BranchesScreen> {
), ),
trailing: phone.isNotEmpty trailing: phone.isNotEmpty
? IconButton( ? IconButton(
icon: const Icon(Icons.chat_bubble_outline, color: Colors.green), icon: const Icon(Icons.chat_bubble, color: AppTheme.onSurface),
onPressed: () => _launchWhatsApp(phone), onPressed: () => _launchWhatsApp(phone),
tooltip: 'Chat on WhatsApp', tooltip: 'Chat on WhatsApp',
) )

View File

@ -85,17 +85,18 @@ class _LoginScreenState extends State<LoginScreen> {
children: [ children: [
const SizedBox(height: 64), const SizedBox(height: 64),
Text( Text(
'Mie Mapan\nMembership', // Editorial high-end entry 'Mie Mapan\nMembership.',
style: Theme.of(context).textTheme.displayLarge?.copyWith( style: Theme.of(context).textTheme.displayLarge?.copyWith(
color: AppTheme.primary, color: AppTheme.onSurface,
letterSpacing: -1.0, // High-fashion compact look
fontSize: 48, fontSize: 48,
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Text( Text(
'Sign in to access your culinary loyalty tier and discover exclusive offers.', 'Sign in to access your culinary loyalty tier and discover exclusive offers.',
style: Theme.of(context).textTheme.bodyLarge, style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: AppTheme.onSurfaceVariant,
),
), ),
const SizedBox(height: 80), const SizedBox(height: 80),
TextField( TextField(
@ -115,17 +116,8 @@ class _LoginScreenState extends State<LoginScreen> {
height: 56, height: 56,
child: _isLoading child: _isLoading
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: Container( : ElevatedButton(
decoration: BoxDecoration(
gradient: AppTheme.primaryGradient,
borderRadius: BorderRadius.circular(12),
),
child: ElevatedButton(
onPressed: _login, onPressed: _login,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
),
child: const Text( child: const Text(
'Access Membership', 'Access Membership',
style: TextStyle( style: TextStyle(
@ -135,18 +127,28 @@ class _LoginScreenState extends State<LoginScreen> {
), ),
), ),
), ),
), const SizedBox(height: 32),
const SizedBox(height: 16), Center(
TextButton( child: GestureDetector(
onPressed: () => Navigator.push( onTap: () => Navigator.push(
context, context,
MaterialPageRoute(builder: (_) => const BranchesScreen()), MaterialPageRoute(builder: (_) => const BranchesScreen()),
), ),
child: Text( child: Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(color: AppTheme.primary, width: 4),
),
),
padding: const EdgeInsets.only(bottom: 2),
child: const Text(
'Find Nearest Branch', 'Find Nearest Branch',
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary, fontSize: 14,
color: AppTheme.onSurface,
),
),
), ),
), ),
), ),

View File

@ -44,11 +44,11 @@ class _LoyaltyDashboardState extends State<LoyaltyDashboard> {
title: const Text('My Rewards'), title: const Text('My Rewards'),
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Icons.storefront_outlined), icon: const Icon(Icons.storefront),
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const BranchesScreen())), onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const BranchesScreen())),
), ),
IconButton( IconButton(
icon: const Icon(Icons.notifications_outlined), icon: const Icon(Icons.notifications),
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const NotificationsScreen())), onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const NotificationsScreen())),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
@ -73,9 +73,9 @@ class _LoyaltyDashboardState extends State<LoyaltyDashboard> {
return Container( return Container(
margin: const EdgeInsets.only(bottom: 40), margin: const EdgeInsets.only(bottom: 40),
padding: const EdgeInsets.all(32), padding: const EdgeInsets.all(32),
decoration: BoxDecoration( decoration: const BoxDecoration(
color: AppTheme.surfaceContainerHighest, // Soft Lift without shadow color: AppTheme.surfaceContainerHighest, // Soft Lift without shadow
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.zero,
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -93,9 +93,9 @@ class _LoyaltyDashboardState extends State<LoyaltyDashboard> {
const SizedBox(width: 16), const SizedBox(width: 16),
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration( decoration: const BoxDecoration(
color: AppTheme.secondaryContainer, color: AppTheme.secondaryContainer,
borderRadius: BorderRadius.circular(1000), // full explicit roundedness borderRadius: BorderRadius.zero, // Editorial block
), ),
child: Text( child: Text(
'Gold Member', 'Gold Member',

View File

@ -64,16 +64,9 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
final notif = _notifications[index]; final notif = _notifications[index];
return Container( return Container(
margin: const EdgeInsets.only(bottom: 16), margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration( decoration: const BoxDecoration(
color: AppTheme.surfaceContainerLow, color: AppTheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.zero,
boxShadow: [
BoxShadow(
color: AppTheme.onSurface.withOpacity(0.04),
blurRadius: 16,
offset: const Offset(0, 4),
)
]
), ),
child: ListTile( child: ListTile(
contentPadding: const EdgeInsets.all(16), contentPadding: const EdgeInsets.all(16),
@ -81,7 +74,7 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppTheme.primaryContainer.withOpacity(0.1), color: AppTheme.primaryContainer.withOpacity(0.1),
shape: BoxShape.circle, shape: BoxShape.rectangle,
), ),
child: const Icon(Icons.star, color: AppTheme.primary), child: const Icon(Icons.star, color: AppTheme.primary),
), ),

View File

@ -2,31 +2,26 @@ import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
class AppTheme { class AppTheme {
// Mapan Core Tokens (Heritage Gallery) // Editorial Organicism Tokens
static const Color primary = Color(0xFFE1251B); static const Color primary = Color(0xFFFFEF00);
static const Color primaryContainer = Color(0xFFBB0004); static const Color primaryContainer = Color(0xFFFFEF00);
static const Color secondary = Color(0xFFCA8342); static const Color secondary = Color(0xFF705900);
static const Color secondaryContainer = Color(0xFFFFBF3C); static const Color secondaryContainer = Color(0xFFFACD34);
static const Color onSecondaryContainer = Color(0xFFEADFD2); static const Color onPrimaryContainer = Color(0xFF5F5800);
static const Color onSecondaryContainer = Color(0xFF584500);
// Surface Hierarchy // Surface Hierarchy
static const Color surface = Color(0xFFFFF8F3); static const Color surface = Color(0xFFF7F7F4);
static const Color surfaceContainer = Color(0xFFF7ECDF); static const Color surfaceContainer = Color(0xFFE8E8E5);
static const Color surfaceContainerLow = Color(0xFFFDF2E5); static const Color surfaceContainerLow = Color(0xFFF0F1EE);
static const Color surfaceContainerLowest = Colors.white; static const Color surfaceContainerLowest = Colors.white;
static const Color surfaceContainerHighest = Color(0xFFECE1D4); static const Color surfaceContainerHighest = Color(0xFFDCDDDA);
// Text & On-Colors // Text & On-Colors
static const Color onSurface = Color(0xFF201B13); static const Color onSurface = Color(0xFF2D2F2D);
static const Color onSurfaceVariant = Color(0xFF5D3F3B); static const Color onSurfaceVariant = Color(0xFF5A5C5A);
static const Color onPrimary = Colors.white; static const Color onPrimary = Color(0xFFFFF59B);
static const Color outlineVariant = Color(0xFFACADAB);
/// The Signature 135-degree CTA Gradient for main buttons.
static const LinearGradient primaryGradient = LinearGradient(
colors: [primary, primaryContainer],
begin: Alignment.topLeft,
end: Alignment.bottomRight, // Approximation of 135 degrees
);
static ThemeData get lightTheme { static ThemeData get lightTheme {
final baseTheme = ThemeData.light(); final baseTheme = ThemeData.light();
@ -44,86 +39,92 @@ class AppTheme {
onSurface: onSurface, onSurface: onSurface,
onSurfaceVariant: onSurfaceVariant, onSurfaceVariant: onSurfaceVariant,
onPrimary: onPrimary, onPrimary: onPrimary,
error: Color(0xFFBA1A1A), error: Color(0xFFB02500),
), ),
textTheme: baseTheme.textTheme.copyWith( textTheme: baseTheme.textTheme.copyWith(
displayLarge: GoogleFonts.plusJakartaSans( displayLarge: GoogleFonts.epilogue(
color: onSurface, color: onSurface,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
letterSpacing: -0.5, letterSpacing: -0.02,
), ),
displayMedium: GoogleFonts.plusJakartaSans( displayMedium: GoogleFonts.epilogue(
color: onSurface,
fontWeight: FontWeight.bold,
letterSpacing: -0.02,
),
displaySmall: GoogleFonts.epilogue(
color: onSurface,
fontWeight: FontWeight.bold,
letterSpacing: -0.02,
),
headlineMedium: GoogleFonts.epilogue(
color: onSurface,
fontWeight: FontWeight.bold,
letterSpacing: -0.02,
),
titleLarge: GoogleFonts.epilogue(
color: onSurface, color: onSurface,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
displaySmall: GoogleFonts.plusJakartaSans( titleMedium: GoogleFonts.epilogue(
color: onSurface, color: onSurface,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
headlineMedium: GoogleFonts.plusJakartaSans( titleSmall: GoogleFonts.epilogue(
color: onSurface,
fontWeight: FontWeight.bold,
),
titleLarge: GoogleFonts.plusJakartaSans(
color: onSurface, color: onSurface,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
titleMedium: GoogleFonts.plusJakartaSans( bodyLarge: GoogleFonts.manrope(color: onSurface),
color: onSurface, bodyMedium: GoogleFonts.manrope(color: onSurfaceVariant),
fontWeight: FontWeight.w600, bodySmall: GoogleFonts.manrope(color: onSurfaceVariant),
), labelLarge: GoogleFonts.manrope(color: onSurfaceVariant),
titleSmall: GoogleFonts.plusJakartaSans(
color: onSurface,
fontWeight: FontWeight.w500,
),
bodyLarge: GoogleFonts.plusJakartaSans(color: onSurfaceVariant),
bodyMedium: GoogleFonts.plusJakartaSans(color: onSurfaceVariant),
bodySmall: GoogleFonts.plusJakartaSans(color: onSurfaceVariant),
labelLarge: GoogleFonts.plusJakartaSans(color: onSurfaceVariant),
), ),
elevatedButtonTheme: ElevatedButtonThemeData( elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.zero,
), ),
foregroundColor: onPrimary, foregroundColor: onPrimaryContainer,
backgroundColor: primaryContainer, // Fallback if no gradient is used backgroundColor: primaryContainer,
elevation: 0, elevation: 0,
side: const BorderSide(color: Colors.red, width: 2),
), ),
), ),
cardTheme: CardThemeData( cardTheme: const CardThemeData(
color: surfaceContainerLow, color: surfaceContainerLow,
elevation: 0, // Depth created via tonal shifts elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.zero),
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
), ),
inputDecorationTheme: InputDecorationTheme( inputDecorationTheme: InputDecorationTheme(
filled: true, filled: true,
fillColor: surfaceContainerLowest, // Spec: soft filled background fillColor: surfaceContainer, // Spec: surfaceContainer with 0px radius
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
border: const UnderlineInputBorder( border: const OutlineInputBorder(
borderSide: BorderSide.none, // Minimum/No outline by default
borderRadius: BorderRadius.only(topLeft: Radius.circular(8), topRight: Radius.circular(8)),
),
enabledBorder: const UnderlineInputBorder(
borderSide: BorderSide.none, borderSide: BorderSide.none,
borderRadius: BorderRadius.only(topLeft: Radius.circular(8), topRight: Radius.circular(8)), borderRadius: BorderRadius.zero,
), ),
focusedBorder: UnderlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.zero,
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: primary.withOpacity(0.5), width: 2), borderSide: BorderSide(color: primary.withOpacity(0.5), width: 2),
borderRadius: const BorderRadius.only(topLeft: Radius.circular(8), topRight: Radius.circular(8)), borderRadius: BorderRadius.zero,
), ),
labelStyle: const TextStyle(color: onSurfaceVariant), labelStyle: const TextStyle(color: onSurfaceVariant),
hintStyle: GoogleFonts.manrope(color: onSurfaceVariant),
), ),
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
backgroundColor: surface, backgroundColor: surface,
foregroundColor: onSurface, foregroundColor: onSurface,
elevation: 0, elevation: 0,
surfaceTintColor: Colors.transparent, surfaceTintColor: Colors.transparent,
titleTextStyle: GoogleFonts.plusJakartaSans( titleTextStyle: GoogleFonts.epilogue(
color: onSurface, color: onSurface,
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
letterSpacing: -0.02,
), ),
), ),
); );