refactor(apps): 主题系统迁移至 ColorScheme + 扩展架构并支持 Dark Mode

This commit is contained in:
qzl
2026-03-27 19:07:39 +08:00
parent ecc1ec6ce4
commit ae29a8209b
146 changed files with 4301 additions and 3200 deletions
@@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
enum CalendarViewType { day, month }
class CalendarState {
final CalendarViewType viewType;
final DateTime selectedDate;
CalendarState({required this.viewType, required this.selectedDate});
CalendarState copyWith({CalendarViewType? viewType, DateTime? selectedDate}) {
return CalendarState(
viewType: viewType ?? this.viewType,
selectedDate: selectedDate ?? this.selectedDate,
);
}
}
class CalendarStateManager extends ChangeNotifier {
CalendarState _state;
CalendarStateManager()
: _state = CalendarState(
viewType: CalendarViewType.month,
selectedDate: DateTime.now(),
);
CalendarState get state => _state;
CalendarViewType get viewType => _state.viewType;
DateTime get selectedDate => _state.selectedDate;
void setViewType(CalendarViewType type) {
_state = _state.copyWith(viewType: type);
notifyListeners();
}
void setSelectedDate(DateTime date) {
_state = _state.copyWith(selectedDate: date);
notifyListeners();
}
void resetToToday() {
final now = DateTime.now();
_state = CalendarState(
viewType: CalendarViewType.month,
selectedDate: DateTime(now.year, now.month, now.day),
);
notifyListeners();
}
void refresh() {
notifyListeners();
}
}
+21 -24
View File
@@ -21,6 +21,7 @@ class AppButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final isDisabled = onPressed == null || isLoading;
if (isOutlined) {
@@ -30,17 +31,15 @@ class AppButton extends StatelessWidget {
onPressed: isLoading ? null : onPressed,
style: OutlinedButton.styleFrom(
backgroundColor: isDisabled
? AppColors.authSecondaryButtonBackground.withValues(
alpha: 0.55,
)
: AppColors.authSecondaryButtonBackground,
? colorScheme.secondaryContainer.withValues(alpha: 0.55)
: colorScheme.secondaryContainer,
foregroundColor: isDisabled
? AppColors.authLinkMuted
: AppColors.authSecondaryButtonText,
? colorScheme.outline
: colorScheme.onSecondaryContainer,
side: BorderSide(
color: isDisabled
? AppColors.authSecondaryButtonBorder.withValues(alpha: 0.7)
: AppColors.authSecondaryButtonBorder,
? colorScheme.outlineVariant.withValues(alpha: 0.7)
: colorScheme.outlineVariant,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppRadius.full),
@@ -48,10 +47,10 @@ class AppButton extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xl),
),
child: isLoading
? const AppLoadingIndicator(
? AppLoadingIndicator(
variant: AppLoadingVariant.button,
color: AppColors.authSecondaryButtonText,
trackColor: AppColors.authSecondaryButtonBorder,
color: colorScheme.onSecondaryContainer,
trackColor: colorScheme.outlineVariant,
)
: Text(
text,
@@ -72,7 +71,7 @@ class AppButton extends StatelessWidget {
? const []
: [
BoxShadow(
color: AppColors.blue300.withValues(alpha: 0.24),
color: colorScheme.primary.withValues(alpha: 0.24),
blurRadius: 18,
offset: const Offset(0, 10),
),
@@ -87,19 +86,17 @@ class AppButton extends StatelessWidget {
elevation: const WidgetStatePropertyAll(0),
backgroundColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.disabled)) {
return AppColors.authPrimaryButtonDisabled;
return colorScheme.surfaceContainerHighest;
}
if (states.contains(WidgetState.pressed)) {
return AppColors.authPrimaryButtonPressed;
return colorScheme.primary.withValues(alpha: 0.85);
}
return AppColors.authPrimaryButton;
return colorScheme.primary;
}),
foregroundColor: const WidgetStatePropertyAll(
AppColors.authPrimaryButtonText,
),
foregroundColor: WidgetStatePropertyAll(colorScheme.onPrimary),
overlayColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.pressed)) {
return AppColors.white.withValues(alpha: 0.08);
return colorScheme.onPrimary.withValues(alpha: 0.08);
}
return null;
}),
@@ -113,10 +110,10 @@ class AppButton extends StatelessWidget {
),
),
child: isLoading
? const AppLoadingIndicator(
? AppLoadingIndicator(
variant: AppLoadingVariant.button,
color: AppColors.authPrimaryButtonText,
trackColor: AppColors.blue400,
color: colorScheme.onPrimary,
trackColor: colorScheme.primaryContainer,
)
: Text(
text,
@@ -125,8 +122,8 @@ class AppButton extends StatelessWidget {
fontWeight: FontWeight.w700,
letterSpacing: 0.2,
color: isDisabled
? AppColors.authLinkMuted
: AppColors.authPrimaryButtonText,
? colorScheme.outline
: colorScheme.onPrimary,
),
),
),
+3 -3
View File
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import '../../core/theme/design_tokens.dart';
class AppInput extends StatelessWidget {
final String label;
@@ -25,15 +24,16 @@ class AppInput extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: const TextStyle(
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColors.slate600,
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 6),
@@ -40,49 +40,46 @@ class AppLoadingIndicator extends StatelessWidget {
};
}
Color get _resolvedColor {
return color ??
switch (variant) {
AppLoadingVariant.surface => AppColors.blue500,
AppLoadingVariant.inline => AppColors.slate500,
AppLoadingVariant.button => AppColors.white,
};
}
Color get _resolvedTrackColor {
return trackColor ??
switch (variant) {
AppLoadingVariant.surface => AppColors.blue100,
AppLoadingVariant.inline => AppColors.slate200,
AppLoadingVariant.button => AppColors.blue300,
};
}
bool get _resolvedWithContainer {
return withContainer ??
switch (variant) {
AppLoadingVariant.surface => true,
AppLoadingVariant.inline => false,
AppLoadingVariant.button => false,
};
}
Widget _buildSpinner() {
Widget _buildSpinner(Color color, Color trackColor) {
return SizedBox(
width: _resolvedSize,
height: _resolvedSize,
child: CircularProgressIndicator(
strokeWidth: _resolvedStrokeWidth,
color: _resolvedColor,
backgroundColor: _resolvedTrackColor,
color: color,
backgroundColor: trackColor,
),
);
}
@override
Widget build(BuildContext context) {
if (!_resolvedWithContainer) {
return _buildSpinner();
final colorScheme = Theme.of(context).colorScheme;
final resolvedColor =
color ??
switch (variant) {
AppLoadingVariant.surface => colorScheme.primary,
AppLoadingVariant.inline => colorScheme.onSurfaceVariant,
AppLoadingVariant.button => colorScheme.onPrimary,
};
final resolvedTrackColor =
trackColor ??
switch (variant) {
AppLoadingVariant.surface => colorScheme.primaryContainer,
AppLoadingVariant.inline => colorScheme.outlineVariant,
AppLoadingVariant.button => colorScheme.secondary,
};
if (withContainer == false ||
(withContainer == null &&
switch (variant) {
AppLoadingVariant.surface => true,
AppLoadingVariant.inline => false,
AppLoadingVariant.button => false,
})) {
return _buildSpinner(resolvedColor, resolvedTrackColor);
}
return Container(
@@ -90,18 +87,18 @@ class AppLoadingIndicator extends StatelessWidget {
height: _resolvedSize + AppSpacing.md,
padding: const EdgeInsets.all(AppSpacing.xs),
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: colorScheme.outlineVariant),
boxShadow: [
BoxShadow(
color: AppColors.slate200.withValues(alpha: 0.55),
color: colorScheme.outlineVariant.withValues(alpha: 0.55),
blurRadius: AppRadius.md,
offset: const Offset(0, AppSpacing.xs),
),
],
),
child: _buildSpinner(),
child: _buildSpinner(resolvedColor, resolvedTrackColor),
);
}
}
+6 -11
View File
@@ -1,7 +1,5 @@
import 'package:flutter/material.dart';
import '../../core/theme/design_tokens.dart';
class AppPressable extends StatefulWidget {
const AppPressable({
super.key,
@@ -25,25 +23,22 @@ class _AppPressableState extends State<AppPressable> {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return AnimatedScale(
scale: _isPressed ? widget.pressedScale : 1,
duration: const Duration(milliseconds: 110),
curve: Curves.easeOut,
child: Material(
color: Colors.transparent,
color: colorScheme.surface.withValues(alpha: 0),
child: InkWell(
borderRadius: widget.borderRadius,
onTap: widget.onTap,
onHighlightChanged: (pressed) {
if (_isPressed == pressed) {
return;
}
setState(() {
_isPressed = pressed;
});
if (_isPressed == pressed) return;
setState(() => _isPressed = pressed);
},
splashColor: AppColors.blue100.withValues(alpha: 0.32),
highlightColor: AppColors.blue50.withValues(alpha: 0.28),
splashColor: colorScheme.primary.withValues(alpha: 0.12),
highlightColor: colorScheme.primary.withValues(alpha: 0.08),
child: widget.child,
),
),
@@ -18,6 +18,7 @@ class AppPullRefreshFeedback extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final resolvedLabel = label ?? context.l10n.commonRefreshing;
return IgnorePointer(
child: AnimatedOpacity(
@@ -31,24 +32,24 @@ class AppPullRefreshFeedback extends StatelessWidget {
vertical: AppSpacing.xs,
),
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const AppLoadingIndicator(
AppLoadingIndicator(
variant: AppLoadingVariant.inline,
color: AppColors.blue500,
trackColor: AppColors.blue100,
color: colorScheme.primary,
trackColor: colorScheme.primaryContainer,
),
const SizedBox(width: AppSpacing.sm),
Text(
resolvedLabel,
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: AppColors.slate600,
color: colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w500,
),
),
@@ -20,8 +20,9 @@ Future<T?> showAppSelectionSheet<T>(
final result = await showModalBottomSheet<T>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
backgroundColor: Theme.of(context).colorScheme.surface.withValues(alpha: 0),
builder: (sheetContext) {
final colorScheme = Theme.of(sheetContext).colorScheme;
return SafeArea(
top: false,
child: Container(
@@ -33,9 +34,9 @@ Future<T?> showAppSelectionSheet<T>(
),
padding: const EdgeInsets.symmetric(vertical: AppSpacing.lg),
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -46,10 +47,10 @@ Future<T?> showAppSelectionSheet<T>(
child: Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w700,
color: AppColors.slate900,
color: colorScheme.onSurface,
),
),
),
@@ -62,9 +63,9 @@ Future<T?> showAppSelectionSheet<T>(
isSelected: isSelected,
);
}),
const SizedBox(height: AppSpacing.sm),
const Divider(height: 1, color: AppColors.border),
const SizedBox(height: AppSpacing.sm),
SizedBox(height: AppSpacing.sm),
Divider(height: 1, color: colorScheme.outlineVariant),
SizedBox(height: AppSpacing.sm),
Padding(
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg),
child: SizedBox(
@@ -90,6 +91,7 @@ Widget _buildItem<T>(
required AppSelectionItem<T> item,
required bool isSelected,
}) {
final colorScheme = Theme.of(sheetContext).colorScheme;
return InkWell(
onTap: () => Navigator.of(sheetContext).pop(item.value),
child: Container(
@@ -105,12 +107,14 @@ Widget _buildItem<T>(
style: TextStyle(
fontSize: 15,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500,
color: isSelected ? AppColors.blue600 : AppColors.slate800,
color: isSelected
? colorScheme.primary
: colorScheme.onSurfaceVariant,
),
),
),
if (isSelected)
const Icon(Icons.check, size: 20, color: AppColors.blue600),
Icon(Icons.check, size: 20, color: colorScheme.primary),
],
),
),
@@ -50,16 +50,17 @@ class _AppSheetInputFieldState extends State<AppSheetInputField> {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.label,
style: const TextStyle(
style: TextStyle(
fontFamily: 'Inter',
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.slate700,
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: AppSpacing.sm),
@@ -67,17 +68,17 @@ class _AppSheetInputFieldState extends State<AppSheetInputField> {
duration: const Duration(milliseconds: 140),
curve: Curves.easeOut,
decoration: BoxDecoration(
color: AppColors.slate50,
color: colorScheme.surfaceContainerLowest,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(
color: _isFocused
? AppColors.borderQuaternary
: AppColors.borderSecondary,
? colorScheme.primary
: colorScheme.outlineVariant,
),
boxShadow: _isFocused
? [
BoxShadow(
color: AppColors.blue200.withValues(alpha: 0.35),
color: colorScheme.secondary.withValues(alpha: 0.35),
blurRadius: AppRadius.sm,
offset: const Offset(0, AppSpacing.xs / 2),
),
@@ -91,7 +92,9 @@ class _AppSheetInputFieldState extends State<AppSheetInputField> {
maxLines: widget.maxLines,
decoration: InputDecoration(
hintText: widget.hint,
hintStyle: const TextStyle(color: AppColors.slate400),
hintStyle: TextStyle(
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.6),
),
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
@@ -22,6 +22,7 @@ class AppToggleSwitch extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return GestureDetector(
onTap: onChanged == null ? null : () => onChanged!(!value),
child: Opacity(
@@ -32,13 +33,13 @@ class AppToggleSwitch extends StatelessWidget {
padding: const EdgeInsets.all(AppSpacing.xs / 2),
decoration: BoxDecoration(
color: value
? (activeBackgroundColor ?? AppColors.blue100)
: (inactiveBackgroundColor ?? AppColors.surfaceTertiary),
? (activeBackgroundColor ?? colorScheme.primaryContainer)
: (inactiveBackgroundColor ?? colorScheme.tertiaryContainer),
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(
color: value
? (activeBorderColor ?? AppColors.blue300)
: (inactiveBorderColor ?? AppColors.borderSecondary),
? (activeBorderColor ?? colorScheme.tertiary)
: (inactiveBorderColor ?? colorScheme.outlineVariant),
),
),
child: AnimatedAlign(
@@ -48,9 +49,9 @@ class AppToggleSwitch extends StatelessWidget {
width: AppSpacing.lg + AppSpacing.xs,
height: AppSpacing.lg + AppSpacing.xs,
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: colorScheme.outlineVariant),
),
),
),
@@ -21,6 +21,7 @@ class BackTitlePageHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return SizedBox(
height: height,
child: Stack(
@@ -50,10 +51,10 @@ class BackTitlePageHeader extends StatelessWidget {
title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: AppColors.slate900,
color: colorScheme.onSurface,
),
),
),
+151
View File
@@ -0,0 +1,151 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import '../../core/theme/design_tokens.dart';
enum DockTab { todo, calendar }
class BottomDock extends StatelessWidget {
final DockTab activeTab;
final VoidCallback? onTodoTap;
final VoidCallback? onCalendarTap;
final VoidCallback? onHomeTap;
const BottomDock({
super.key,
required this.activeTab,
this.onTodoTap,
this.onCalendarTap,
this.onHomeTap,
});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Container(
height: 72,
padding: const EdgeInsets.only(
left: AppSpacing.xl,
right: AppSpacing.xl,
top: AppSpacing.md,
bottom: AppSpacing.sm,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildToggle(context, colorScheme),
_buildHomeBtn(context, colorScheme),
],
),
);
}
Widget _buildToggle(BuildContext context, ColorScheme colorScheme) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 4),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.xxl),
border: Border.all(color: colorScheme.outlineVariant),
boxShadow: [
BoxShadow(
color: colorScheme.shadow.withValues(alpha: 0.12),
blurRadius: AppRadius.sm,
offset: const Offset(0, AppSpacing.xs / 2),
),
],
),
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildToggleItem(
icon: LucideIcons.listTodo,
isActive: activeTab == DockTab.todo,
onTap: onTodoTap,
colorScheme: colorScheme,
),
const SizedBox(width: 4),
_buildToggleItem(
icon: LucideIcons.calendar,
isActive: activeTab == DockTab.calendar,
onTap: onCalendarTap,
colorScheme: colorScheme,
),
],
),
);
}
Widget _buildToggleItem({
required IconData icon,
required bool isActive,
required ColorScheme colorScheme,
VoidCallback? onTap,
}) {
return Material(
color: colorScheme.surface.withValues(alpha: 0),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(AppRadius.xl),
child: AnimatedContainer(
duration: const Duration(milliseconds: 140),
curve: Curves.easeOut,
width: 44,
height: 44,
decoration: BoxDecoration(
color: isActive
? colorScheme.secondaryContainer
: colorScheme.surface.withValues(alpha: 0),
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(
color: isActive
? colorScheme.primary.withValues(alpha: 0.35)
: colorScheme.surface.withValues(alpha: 0),
),
),
child: Icon(
icon,
size: 20,
color: isActive
? colorScheme.primary
: colorScheme.onSurfaceVariant,
),
),
),
);
}
Widget _buildHomeBtn(BuildContext context, ColorScheme colorScheme) {
return Material(
color: colorScheme.surface.withValues(alpha: 0),
child: InkWell(
key: const ValueKey('bottom_dock_home_button'),
onTap: onHomeTap,
borderRadius: BorderRadius.circular(AppRadius.xl),
child: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: colorScheme.outlineVariant),
boxShadow: [
BoxShadow(
color: colorScheme.shadow.withValues(alpha: 0.12),
blurRadius: AppRadius.sm,
offset: const Offset(0, AppSpacing.xs / 2),
),
],
),
child: Icon(
LucideIcons.home,
size: 20,
color: colorScheme.onSurfaceVariant,
),
),
),
);
}
}
+10 -5
View File
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import '../../../core/l10n/l10n.dart';
import '../../../core/theme/design_tokens.dart';
enum MessageSender { user, ai }
@@ -23,6 +22,7 @@ class ChatBubble extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isUser = sender == MessageSender.user;
final colorScheme = Theme.of(context).colorScheme;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
@@ -36,7 +36,10 @@ class ChatBubble extends StatelessWidget {
padding: const EdgeInsets.only(bottom: 6),
child: Text(
_formatTimestamp(timestamp),
style: const TextStyle(fontSize: 11, color: AppColors.slate400),
style: TextStyle(
fontSize: 11,
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.7),
),
),
),
Container(
@@ -45,11 +48,11 @@ class ChatBubble extends StatelessWidget {
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: isUser ? AppColors.blue500 : AppColors.white,
color: isUser ? colorScheme.primary : colorScheme.surface,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.06),
color: colorScheme.shadow.withValues(alpha: 0.08),
blurRadius: 8,
offset: const Offset(0, 2),
),
@@ -60,7 +63,9 @@ class ChatBubble extends StatelessWidget {
content,
style: TextStyle(
fontSize: 15,
color: isUser ? AppColors.white : AppColors.slate700,
color: isUser
? colorScheme.onPrimary
: colorScheme.onSurface,
height: 1.45,
),
)
+17 -10
View File
@@ -18,8 +18,10 @@ Future<bool> showConfirmSheet(
final result = await showModalBottomSheet<bool>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
backgroundColor: Theme.of(context).colorScheme.surface.withValues(alpha: 0),
builder: (sheetContext) {
final colorScheme = Theme.of(sheetContext).colorScheme;
return SafeArea(
top: false,
child: Container(
@@ -31,9 +33,9 @@ Future<bool> showConfirmSheet(
),
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -42,17 +44,20 @@ Future<bool> showConfirmSheet(
Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: AppColors.slate900,
color: colorScheme.onSurface,
),
),
const SizedBox(height: AppSpacing.xs),
Text(
message,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 14, color: AppColors.slate500),
style: TextStyle(
fontSize: 14,
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: AppSpacing.lg),
SizedBox(
@@ -63,16 +68,18 @@ Future<bool> showConfirmSheet(
alignment: Alignment.center,
decoration: BoxDecoration(
color: isDestructive
? AppColors.feedbackErrorIcon
: AppColors.blue600,
? colorScheme.error
: colorScheme.primary,
borderRadius: BorderRadius.circular(AppRadius.full),
),
child: Text(
resolvedConfirmText,
style: const TextStyle(
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w700,
color: AppColors.white,
color: isDestructive
? colorScheme.onError
: colorScheme.onPrimary,
),
),
),
@@ -13,8 +13,10 @@ Future<bool> showDestructiveActionSheet(
final result = await showModalBottomSheet<bool>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
backgroundColor: Theme.of(context).colorScheme.surface.withValues(alpha: 0),
builder: (sheetContext) {
final colorScheme = Theme.of(sheetContext).colorScheme;
return SafeArea(
top: false,
child: Container(
@@ -26,9 +28,9 @@ Future<bool> showDestructiveActionSheet(
),
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -37,17 +39,20 @@ Future<bool> showDestructiveActionSheet(
Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: AppColors.slate900,
color: colorScheme.onSurface,
),
),
const SizedBox(height: AppSpacing.xs),
Text(
message,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 14, color: AppColors.slate500),
style: TextStyle(
fontSize: 14,
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: AppSpacing.lg),
SizedBox(
@@ -57,15 +62,15 @@ Future<bool> showDestructiveActionSheet(
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: AppColors.feedbackErrorIcon,
color: colorScheme.error,
borderRadius: BorderRadius.circular(AppRadius.full),
),
child: Text(
confirmText,
style: const TextStyle(
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w700,
color: AppColors.white,
color: colorScheme.onError,
),
),
),
@@ -72,7 +72,7 @@ class _DetailHeaderActionMenuState<T> extends State<DetailHeaderActionMenu<T>> {
CompositedTransformFollower(
link: _layerLink,
showWhenUnlinked: false,
offset: const Offset(
offset: Offset(
_buttonSize - _menuWidth,
_buttonSize + AppSpacing.sm,
),
@@ -101,18 +101,19 @@ class _DetailHeaderActionMenuState<T> extends State<DetailHeaderActionMenu<T>> {
}
Widget _buildMenuCard() {
final colorScheme = Theme.of(context).colorScheme;
return Material(
color: Colors.transparent,
color: colorScheme.surface.withValues(alpha: 0),
child: Container(
width: _menuWidth,
padding: const EdgeInsets.symmetric(vertical: AppSpacing.sm),
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: colorScheme.outlineVariant),
boxShadow: [
BoxShadow(
color: AppColors.slate300.withValues(alpha: 0.42),
color: colorScheme.shadow.withValues(alpha: 0.42),
blurRadius: AppSpacing.xl,
offset: const Offset(0, AppSpacing.sm),
),
@@ -125,12 +126,14 @@ class _DetailHeaderActionMenuState<T> extends State<DetailHeaderActionMenu<T>> {
for (int i = 0; i < widget.items.length; i++) ...[
_buildMenuItem(widget.items[i]),
if (i < widget.items.length - 1)
const Padding(
padding: EdgeInsets.symmetric(horizontal: AppSpacing.md),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
),
child: Divider(
height: 1,
thickness: 1,
color: AppColors.slate100,
color: colorScheme.outlineVariant,
),
),
],
@@ -141,21 +144,26 @@ class _DetailHeaderActionMenuState<T> extends State<DetailHeaderActionMenu<T>> {
}
Widget _buildMenuItem(DetailHeaderActionItem<T> item) {
final colorScheme = Theme.of(context).colorScheme;
final textColor = item.isDestructive
? AppColors.red500
: (item.enabled ? AppColors.slate700 : AppColors.slate400);
? colorScheme.error
: (item.enabled ? colorScheme.onSurface : colorScheme.onSurfaceVariant);
final pressedColor = item.isDestructive
? AppColors.feedbackErrorSurface
: AppColors.surfaceInfoLight;
? colorScheme.errorContainer
: colorScheme.primaryContainer;
return SizedBox(
height: AppSpacing.xxl * 2,
child: Material(
color: Colors.transparent,
color: colorScheme.surface.withValues(alpha: 0),
child: InkWell(
borderRadius: BorderRadius.circular(AppRadius.md),
splashColor: item.enabled ? pressedColor : Colors.transparent,
highlightColor: item.enabled ? pressedColor : Colors.transparent,
splashColor: item.enabled
? pressedColor
: colorScheme.surface.withValues(alpha: 0),
highlightColor: item.enabled
? pressedColor
: colorScheme.surface.withValues(alpha: 0),
onTap: item.enabled ? () => _handleSelect(item.value) : null,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
@@ -190,6 +198,7 @@ class _DetailHeaderActionMenuState<T> extends State<DetailHeaderActionMenu<T>> {
return const SizedBox.shrink();
}
final colorScheme = Theme.of(context).colorScheme;
return CompositedTransformTarget(
link: _layerLink,
child: GestureDetector(
@@ -200,19 +209,19 @@ class _DetailHeaderActionMenuState<T> extends State<DetailHeaderActionMenu<T>> {
height: _buttonSize,
decoration: BoxDecoration(
color: _isMenuOpen
? AppColors.surfaceInfo
: AppColors.surfaceTertiary,
? colorScheme.secondaryContainer
: colorScheme.tertiaryContainer,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(
color: _isMenuOpen
? AppColors.borderQuaternary
: AppColors.borderTertiary,
? colorScheme.outlineVariant
: colorScheme.outline,
),
),
child: const Icon(
child: Icon(
Icons.more_horiz,
size: AppSpacing.lg + AppSpacing.xs,
color: AppColors.slate600,
color: colorScheme.onSurfaceVariant,
),
),
),
@@ -18,6 +18,8 @@ class ErrorRetrySurface extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Center(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
@@ -28,7 +30,7 @@ class ErrorRetrySurface extends StatelessWidget {
Text(
message,
textAlign: TextAlign.center,
style: const TextStyle(color: AppColors.red500),
style: TextStyle(color: colorScheme.error),
),
const SizedBox(height: AppSpacing.md),
AppButton(text: context.l10n.commonRetry, onPressed: onRetry),
@@ -99,6 +99,7 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final chars = widget.controller.text.split('');
final slotHeight = AppSpacing.xl * 2 + AppSpacing.sm;
final slotSpacing = AppSpacing.sm;
@@ -113,17 +114,17 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
duration: const Duration(milliseconds: 180),
padding: const EdgeInsets.all(AppSpacing.sm),
decoration: BoxDecoration(
color: AppColors.authSectionBackground,
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(
color: _isFocused
? AppColors.authInputFocus
: AppColors.authSectionBorder,
? colorScheme.primary
: colorScheme.outlineVariant,
),
boxShadow: _isFocused
? [
BoxShadow(
color: AppColors.blue200.withValues(alpha: 0.28),
color: colorScheme.secondary.withValues(alpha: 0.28),
blurRadius: 18,
offset: const Offset(0, 8),
),
@@ -164,6 +165,7 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
chars: chars,
slotHeight: slotHeight,
isComplete: isComplete,
colorScheme: colorScheme,
),
),
if (index != widget.length - 1)
@@ -185,6 +187,7 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
required List<String> chars,
required double slotHeight,
required bool isComplete,
required ColorScheme colorScheme,
}) {
final hasChar = index < chars.length;
final isActive =
@@ -195,19 +198,19 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
height: slotHeight,
alignment: Alignment.center,
decoration: BoxDecoration(
color: hasChar ? AppColors.white : AppColors.authInputBackground,
color: hasChar ? colorScheme.surface : colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(
color: isActive
? AppColors.authPrimaryButton
? colorScheme.primary
: isComplete
? AppColors.authSecondaryButtonBorder
: AppColors.authInputBorder,
? colorScheme.outlineVariant
: colorScheme.outline,
),
boxShadow: isActive
? [
BoxShadow(
color: AppColors.blue200.withValues(alpha: 0.32),
color: colorScheme.secondary.withValues(alpha: 0.32),
blurRadius: 14,
offset: const Offset(0, 6),
),
@@ -219,7 +222,7 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
style: TextStyle(
fontSize: AppSpacing.xl,
fontWeight: FontWeight.w600,
color: hasChar ? AppColors.slate900 : AppColors.authLinkMuted,
color: hasChar ? colorScheme.onSurface : colorScheme.onSurfaceVariant,
),
),
);
+3 -2
View File
@@ -20,9 +20,10 @@ class LinkButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final color = enabled
? (foregroundColor ?? AppColors.authLinkText)
: AppColors.slate300;
? (foregroundColor ?? colorScheme.primary)
: colorScheme.outline;
return TextButton(
onPressed: enabled ? onTap : null,
+35 -28
View File
@@ -73,6 +73,7 @@ class MessageComposer extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return KeyedSubtree(
key: messageComposerContainerKey,
child: Container(
@@ -82,19 +83,19 @@ class MessageComposer extends StatelessWidget {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: AppColors.homeComposerShell,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.xxl),
border: Border.all(color: AppColors.homeComposerBorder),
boxShadow: const [
border: Border.all(color: colorScheme.outlineVariant),
boxShadow: [
BoxShadow(
color: AppColors.slate200,
color: colorScheme.surfaceContainerHighest.withValues(alpha: 1),
blurRadius: AppRadius.lg,
offset: Offset(AppSpacing.none, AppSpacing.sm),
offset: const Offset(AppSpacing.none, AppSpacing.sm),
),
BoxShadow(
color: AppColors.white,
color: colorScheme.surface,
blurRadius: AppRadius.md,
offset: Offset(AppSpacing.none, -AppSpacing.xs),
offset: const Offset(AppSpacing.none, -AppSpacing.xs),
),
],
),
@@ -114,30 +115,30 @@ class MessageComposer extends StatelessWidget {
icon: Icon(
LucideIcons.plus,
size: iconSize,
color: AppColors.slate500,
color: colorScheme.onSurfaceVariant,
),
),
),
),
const SizedBox(width: AppSpacing.sm),
Expanded(child: _buildCenterArea()),
Expanded(child: _buildCenterArea(colorScheme)),
const SizedBox(width: AppSpacing.sm),
IconButton(
key: messageComposerRightButtonKey,
visualDensity: VisualDensity.compact,
onPressed: onTapRightAction,
icon: _isTranscribing
? const AppLoadingIndicator(
? AppLoadingIndicator(
variant: AppLoadingVariant.inline,
size: AppSpacing.lg,
strokeWidth: AppSpacing.xs / 2,
color: AppColors.blue600,
trackColor: AppColors.blue100,
color: colorScheme.primary,
trackColor: colorScheme.primaryContainer,
)
: Icon(
_resolveRightIcon(),
size: iconSize,
color: _resolveRightIconColor(),
color: _resolveRightIconColor(colorScheme),
),
),
],
@@ -147,7 +148,7 @@ class MessageComposer extends StatelessWidget {
);
}
Widget _buildCenterArea() {
Widget _buildCenterArea(ColorScheme colorScheme) {
return SizedBox(
height: composerMinHeight,
child: AnimatedSwitcher(
@@ -155,7 +156,10 @@ class MessageComposer extends StatelessWidget {
switchInCurve: Curves.easeOut,
switchOutCurve: Curves.easeOut,
child: _isHoldMode
? _buildHoldToSpeakArea(key: const ValueKey('hold_mode'))
? _buildHoldToSpeakArea(
key: const ValueKey('hold_mode'),
colorScheme: colorScheme,
)
: _buildTextInputArea(key: const ValueKey('text_mode')),
),
);
@@ -165,7 +169,10 @@ class MessageComposer extends StatelessWidget {
return SizedBox(key: key, height: composerMinHeight, child: textInputChild);
}
Widget _buildHoldToSpeakArea({required Key key}) {
Widget _buildHoldToSpeakArea({
required Key key,
required ColorScheme colorScheme,
}) {
return RawGestureDetector(
key: messageComposerHoldAreaKey,
behavior: HitTestBehavior.opaque,
@@ -176,8 +183,8 @@ class MessageComposer extends StatelessWidget {
duration: const Duration(milliseconds: _holdActivateDurationMs),
),
(instance) {
instance.onLongPressStart = (_) => onHoldToSpeakStart();
instance.onLongPressEnd = (_) => onHoldToSpeakEnd();
instance.onLongPressStart = (details) => onHoldToSpeakStart();
instance.onLongPressEnd = (details) => onHoldToSpeakEnd();
instance.onLongPressMoveUpdate = onHoldToSpeakMoveUpdate;
instance.onLongPressCancel = onHoldToSpeakCancel;
},
@@ -188,12 +195,12 @@ class MessageComposer extends StatelessWidget {
width: double.infinity,
height: composerMinHeight,
alignment: Alignment.center,
child: _buildHoldToSpeakContent(),
child: _buildHoldToSpeakContent(colorScheme),
),
);
}
Widget _buildHoldToSpeakContent() {
Widget _buildHoldToSpeakContent(ColorScheme colorScheme) {
final l10n = L10n.current;
final resolvedRecordingText =
recordingText ?? l10n.homeRecordingReleaseSend;
@@ -208,7 +215,7 @@ class MessageComposer extends StatelessWidget {
alignment: Alignment.center,
child: Text(
resolvedRecordingText,
style: const TextStyle(color: AppColors.slate700),
style: TextStyle(color: colorScheme.onSurface),
),
);
}
@@ -220,13 +227,13 @@ class MessageComposer extends StatelessWidget {
const SizedBox(height: AppSpacing.xs),
Text(
resolvedRecordingText,
style: const TextStyle(color: AppColors.slate700),
style: TextStyle(color: colorScheme.onSurface),
),
const SizedBox(height: AppSpacing.xs),
Text(
resolvedRecordingHintText,
key: messageComposerRecordingHintKey,
style: const TextStyle(color: AppColors.slate500),
style: TextStyle(color: colorScheme.onSurfaceVariant),
),
],
);
@@ -237,7 +244,7 @@ class MessageComposer extends StatelessWidget {
alignment: Alignment.center,
child: Text(
resolvedTranscribingText,
style: const TextStyle(color: AppColors.slate500),
style: TextStyle(color: colorScheme.onSurfaceVariant),
),
);
}
@@ -246,7 +253,7 @@ class MessageComposer extends StatelessWidget {
alignment: Alignment.center,
child: Text(
resolvedHoldToSpeakText,
style: const TextStyle(color: AppColors.slate500),
style: TextStyle(color: colorScheme.onSurfaceVariant),
),
);
}
@@ -261,10 +268,10 @@ class MessageComposer extends StatelessWidget {
return _isHoldMode ? LucideIcons.keyboard : LucideIcons.mic;
}
Color _resolveRightIconColor() {
Color _resolveRightIconColor(ColorScheme colorScheme) {
if (isWaitingAgent || hasMessage) {
return AppColors.blue600;
return colorScheme.primary;
}
return AppColors.slate500;
return colorScheme.onSurfaceVariant;
}
}
+20 -27
View File
@@ -42,10 +42,13 @@ class _BackButtonState extends State<BackButton> {
@override
Widget build(BuildContext context) {
final background = _isPressed ? AppColors.surfaceInfo : AppColors.white;
final colorScheme = Theme.of(context).colorScheme;
final background = _isPressed
? colorScheme.secondaryContainer
: colorScheme.surface;
final borderColor = _isPressed
? AppColors.borderQuaternary
: AppColors.borderTertiary;
? colorScheme.outlineVariant
: colorScheme.outline;
return AnimatedScale(
scale: _isPressed ? 0.96 : 1,
@@ -60,13 +63,13 @@ class _BackButtonState extends State<BackButton> {
boxShadow: _isPressed
? const []
: [
const BoxShadow(
color: AppColors.white,
BoxShadow(
color: colorScheme.surface,
blurRadius: AppRadius.sm,
offset: Offset(0, -1),
offset: const Offset(0, -1),
),
BoxShadow(
color: AppColors.slate200.withValues(alpha: 0.42),
color: colorScheme.shadow.withValues(alpha: 0.42),
blurRadius: AppRadius.md,
offset: const Offset(0, AppSpacing.xs),
),
@@ -76,39 +79,29 @@ class _BackButtonState extends State<BackButton> {
width: AppSpacing.xl * 2,
height: AppSpacing.xl * 2,
child: Material(
color: Colors.transparent,
color: colorScheme.surface.withValues(alpha: 0),
child: InkWell(
borderRadius: BorderRadius.circular(AppRadius.full),
onTap: widget.onPressed ?? () => Navigator.of(context).pop(),
onTapDown: (_) {
if (_isPressed) {
return;
}
setState(() {
_isPressed = true;
});
if (_isPressed) return;
setState(() => _isPressed = true);
},
onTapCancel: () {
if (!_isPressed) {
return;
}
setState(() {
_isPressed = false;
});
if (!_isPressed) return;
setState(() => _isPressed = false);
},
onTapUp: (_) {
if (!_isPressed) {
return;
}
setState(() {
_isPressed = false;
});
if (!_isPressed) return;
setState(() => _isPressed = false);
},
child: Center(
child: Icon(
Icons.chevron_left,
size: AppSpacing.lg + AppSpacing.xs,
color: _isPressed ? AppColors.blue600 : AppColors.slate700,
color: _isPressed
? colorScheme.primary
: colorScheme.onSurfaceVariant,
),
),
),
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import '../../core/theme/design_tokens.dart';
class PhonePrefixSelector extends StatelessWidget {
const PhonePrefixSelector({
@@ -15,8 +14,9 @@ class PhonePrefixSelector extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Padding(
padding: const EdgeInsets.only(left: AppSpacing.md, right: AppSpacing.sm),
padding: const EdgeInsets.only(left: 12, right: 8),
child: PopupMenuButton<String>(
onSelected: onChanged,
itemBuilder: (context) => items
@@ -24,26 +24,24 @@ class PhonePrefixSelector extends StatelessWidget {
(item) => PopupMenuItem<String>(value: item, child: Text(item)),
)
.toList(growable: false),
color: AppColors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppRadius.md),
),
color: colorScheme.surface,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
value,
style: const TextStyle(
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: AppColors.slate700,
color: colorScheme.onSurface,
),
),
const SizedBox(width: AppSpacing.xs),
const Icon(
const SizedBox(width: 4),
Icon(
Icons.arrow_drop_down,
size: 18,
color: AppColors.slate500,
color: colorScheme.onSurfaceVariant,
),
],
),
+2 -1
View File
@@ -92,6 +92,7 @@ class _ToastWidgetState extends State<_ToastWidget>
@override
Widget build(BuildContext context) {
final config = ToastTypeConfig.fromType(context, widget.type);
final colorScheme = Theme.of(context).colorScheme;
return Positioned(
top: MediaQuery.of(context).padding.top + 12,
@@ -115,7 +116,7 @@ class _ToastWidgetState extends State<_ToastWidget>
border: Border.all(color: config.borderColor),
boxShadow: [
BoxShadow(
color: AppColors.slate900.withValues(alpha: 0.08),
color: colorScheme.shadow.withValues(alpha: 0.08),
blurRadius: 24,
offset: const Offset(0, 10),
),
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import '../../../core/l10n/l10n.dart';
import 'toast_type.dart';
import '../../../core/theme/design_tokens.dart';
class ToastTypeConfig {
final Color surfaceColor;
@@ -22,36 +21,38 @@ class ToastTypeConfig {
static ToastTypeConfig fromType(BuildContext context, ToastType type) {
final l10n = context.l10n;
final colorScheme = Theme.of(context).colorScheme;
return switch (type) {
ToastType.success => ToastTypeConfig(
surfaceColor: AppColors.feedbackSuccessSurface,
borderColor: AppColors.feedbackSuccessBorder,
iconColor: AppColors.feedbackSuccessIcon,
textColor: AppColors.feedbackSuccessText,
surfaceColor: colorScheme.tertiaryContainer,
borderColor: colorScheme.tertiary,
iconColor: colorScheme.tertiary,
textColor: colorScheme.onTertiaryContainer,
label: l10n.toastLabelSuccess,
icon: Icons.check_circle_outline,
),
ToastType.warning => ToastTypeConfig(
surfaceColor: AppColors.feedbackWarningSurface,
borderColor: AppColors.feedbackWarningBorder,
iconColor: AppColors.feedbackWarningIcon,
textColor: AppColors.feedbackWarningText,
surfaceColor: colorScheme.secondaryContainer,
borderColor: colorScheme.secondary,
iconColor: colorScheme.secondary,
textColor: colorScheme.onSecondaryContainer,
label: l10n.toastLabelWarning,
icon: Icons.warning_amber_rounded,
),
ToastType.error => ToastTypeConfig(
surfaceColor: AppColors.feedbackErrorSurface,
borderColor: AppColors.feedbackErrorBorder,
iconColor: AppColors.feedbackErrorIcon,
textColor: AppColors.feedbackErrorText,
surfaceColor: colorScheme.errorContainer,
borderColor: colorScheme.error,
iconColor: colorScheme.error,
textColor: colorScheme.onErrorContainer,
label: l10n.toastLabelError,
icon: Icons.error_outline,
),
ToastType.info => ToastTypeConfig(
surfaceColor: AppColors.feedbackInfoSurface,
borderColor: AppColors.feedbackInfoBorder,
iconColor: AppColors.feedbackInfoIcon,
textColor: AppColors.feedbackInfoText,
surfaceColor: colorScheme.primaryContainer,
borderColor: colorScheme.primary,
iconColor: colorScheme.primary,
textColor: colorScheme.onPrimaryContainer,
label: l10n.toastLabelInfo,
icon: Icons.info_outline,
),