Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 27 additions & 10 deletions lib/core/i18n/app_strings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,24 @@ class AppStrings {
String get navPost => _zh ? '張貼' : 'Post';

String get listingsTitle => _zh ? '展場剩食媒合' : 'Exhibition Surplus Food';
String get listingsSubtitle =>
_zh ? '在公開展場快速媒合,減少浪費。' : 'Fast public-venue matching to reduce waste.';
String get refresh => _zh ? '重新整理' : 'Refresh';
String get privateDonor => _zh ? '匿名企業' : 'Private donor';
String get filterAllVenues => _zh ? '全部場館' : 'All venues';
String get filterFavoriteVenues => _zh ? '僅收藏場館' : 'Favorites only';
String get filterNearHubs => _zh ? '附近場館' : 'Near hubs';
String get filterAvailableNow => _zh ? '可立即取餐' : 'Available now';
String get clearFilter => _zh ? '清除篩選' : 'Clear filter';
String get reserveNow => _zh ? '立即預約' : 'Reserve';
String get openMap => _zh ? '地圖' : 'Map';
String get backToListings => _zh ? '回清單' : 'Back to listings';
String get pickupCountdownLabel => _zh ? '剩餘可領取時間' : 'Time left';
String pickupCountdownValue(int minutes) =>
_zh ? '$minutes 分鐘' : '$minutes min';
String get apiWarmupRetryHint => _zh
? '服務可能正在喚醒中,系統會自動重試一次。'
: 'Service may still be warming up. We automatically retry once.';
String get noActiveListings => _zh
? '目前沒有可領取項目。\n可切到地圖查看,或由企業先發佈。'
: 'No active listings right now.\nTry checking map view or post a new listing.';
Expand Down Expand Up @@ -71,11 +87,9 @@ class AppStrings {
String get offlineIdentityMode =>
_zh ? '使用離線身份模式' : 'Using offline identity mode';
String get reportSafetyConcern => _zh ? '回報風險事件' : 'Report safety concern';
String get reportRiskSelectReasonTitle =>
_zh ? '請選擇回報原因' : 'Select a reason';
String get riskReasonPrivateLocation => _zh
? '要求改到私下地點面交'
: 'Asked to move pickup to a private location';
String get reportRiskSelectReasonTitle => _zh ? '請選擇回報原因' : 'Select a reason';
String get riskReasonPrivateLocation =>
_zh ? '要求改到私下地點面交' : 'Asked to move pickup to a private location';
String get riskReasonSuspiciousBehavior =>
_zh ? '現場行為可疑 / 騷擾' : 'Suspicious behavior / harassment';
String get riskReasonNoShow =>
Expand All @@ -85,10 +99,13 @@ class AppStrings {
String get riskReasonOther => _zh ? '其他風險' : 'Other risk';
String get abuseReported => _zh ? '已送出風險回報。' : 'Safety report submitted.';
String get verifiedEnterprise => _zh ? '已驗證企業' : 'Verified enterprise';
String get trustedQualityEnterprise => _zh ? '交付品質穩定' : 'Trusted handoff quality';
String get trustedQualityEnterprise =>
_zh ? '交付品質穩定' : 'Trusted handoff quality';
String get highImpactEnterprise => _zh ? '高量捐贈企業' : 'High-impact donor';
String get flexiblePickupEnterprise => _zh ? '彈性取餐時段' : 'Flexible pickup window';
String get stableShelfLifeEnterprise => _zh ? '保存時效較穩定' : 'Stable shelf-life setup';
String get flexiblePickupEnterprise =>
_zh ? '彈性取餐時段' : 'Flexible pickup window';
String get stableShelfLifeEnterprise =>
_zh ? '保存時效較穩定' : 'Stable shelf-life setup';
String get pendingConfirm => _zh ? '待確認' : 'Pending';
String get confirmedFilter => _zh ? '已確認' : 'Confirmed';
String get showPickupCodeHelp => _zh
Expand All @@ -106,8 +123,8 @@ class AppStrings {
String get retry => _zh ? '重試' : 'Retry';
String get genericLoadErrorTitle => _zh ? '讀取失敗' : 'Unable to load';
String get genericLoadErrorBody => _zh
? '目前資料暫時無法載入,請稍後再試。'
: 'We cannot load data right now. Please try again.';
? '目前資料暫時無法載入,請稍後再試。若剛開啟服務,可能需要幾秒喚醒。'
: 'We cannot load data right now. If the service just woke up, retry in a few seconds.';

String statusLabel(AppStatusLabel status) {
switch (status) {
Expand Down
7 changes: 4 additions & 3 deletions lib/core/layout/app_shell.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ class AppShell extends StatelessWidget {
@override
Widget build(BuildContext context) {
final index = _currentIndex();
final isWide = MediaQuery.sizeOf(context).width >= 900;
final useRail = kIsWeb || isWide;
final width = MediaQuery.sizeOf(context).width;
final isWide = width >= 900;
final useRail = (kIsWeb || isWide) && width >= 760;
final s = AppStrings.of(context);

if (useRail) {
final isCompactRail = MediaQuery.sizeOf(context).width < 1200;
final isCompactRail = width < 1200;
return Scaffold(
body: Row(
children: [
Expand Down
55 changes: 53 additions & 2 deletions lib/core/theme/app_theme.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,71 @@
import 'package:flutter/material.dart';

class BoxmatchColors {
static const seed = Color(0xFF2D6A4F);
static const warmSurface = Color(0xFFF6FAF5);
static const warmSurfaceAlt = Color(0xFFEDF5E9);
static const warmAccent = Color(0xFFE9F6E6);
static const warmBorder = Color(0xFFBCD8BF);
static const warmWarningBg = Color(0xFFFFF4E0);
static const warmWarningText = Color(0xFF7A4A00);
static const warmDangerBg = Color(0xFFFFEBE9);
static const warmDangerText = Color(0xFF8F2D2D);
static const warmSuccessBg = Color(0xFFEAF8ED);
}

ThemeData buildAppTheme() {
final base = ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF0B6E4F)),
colorScheme: ColorScheme.fromSeed(
seedColor: BoxmatchColors.seed,
brightness: Brightness.light,
),
useMaterial3: true,
);

return base.copyWith(
appBarTheme: base.appBarTheme.copyWith(centerTitle: false),
scaffoldBackgroundColor: BoxmatchColors.warmSurface,
textTheme: base.textTheme.copyWith(
titleLarge: base.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w700,
letterSpacing: -0.2,
),
titleMedium: base.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
titleSmall: base.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w600,
),
bodyMedium: base.textTheme.bodyMedium?.copyWith(height: 1.35),
bodySmall: base.textTheme.bodySmall?.copyWith(height: 1.35),
),
appBarTheme: base.appBarTheme.copyWith(
centerTitle: false,
backgroundColor: BoxmatchColors.warmSurfaceAlt,
surfaceTintColor: Colors.transparent,
elevation: 0,
titleTextStyle: base.textTheme.titleLarge?.copyWith(
color: const Color(0xFF22352A),
fontWeight: FontWeight.w700,
letterSpacing: -0.2,
),
),
cardTheme: base.cardTheme.copyWith(
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
clipBehavior: Clip.antiAlias,
color: Colors.white,
surfaceTintColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: const BorderSide(color: BoxmatchColors.warmBorder),
),
),
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
isDense: true,
),
chipTheme: base.chipTheme.copyWith(
side: const BorderSide(color: BoxmatchColors.warmBorder),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(999)),
),
);
}
Loading
Loading