merge: PR #1488 create task mobile UI

This commit is contained in:
Levi committed 2026-09-08 20:23:52 +08:00
commit 5728caf7db
31 files changed
+253 -93

No files matched your search

@@ -18,42 +18,50 @@ class AppComponentThemes extends StatelessWidget {
align: 1,
border: Border.all(color: theme.colorScheme.ring.withValues(alpha: 0.65), width: 1),
),
child: shad.ComponentTheme<shad.SecondaryButtonTheme>(
data: shad.SecondaryButtonTheme(
decoration: (context, states, value) {
final decoration = switch (value) {
final BoxDecoration decoration => decoration,
_ => BoxDecoration(borderRadius: BorderRadius.circular(theme.radiusMd)),
};
if (states.contains(WidgetState.disabled)) {
return decoration.copyWith(color: palette.surfaceSoft);
}
if (theme.brightness == Brightness.light) {
final background = Color.alphaBlend(palette.textPrimary.withValues(alpha: 0.08), palette.surfaceSoft);
final hoverBackground = Color.alphaBlend(
palette.textPrimary.withValues(alpha: 0.13),
palette.surfaceSoft,
);
return decoration.copyWith(color: states.contains(WidgetState.hovered) ? hoverBackground : background);
}
return value;
},
textStyle: (context, states, value) => states.contains(WidgetState.disabled)
? value.copyWith(color: palette.textMuted.withValues(alpha: 0.62))
: value,
iconTheme: (context, states, value) => states.contains(WidgetState.disabled)
? value.copyWith(color: palette.textMuted.withValues(alpha: 0.62))
: value,
child: shad.ComponentTheme<shad.TextFieldTheme>(
data: shad.TextFieldTheme(
filled: true,
border: Border.all(color: palette.border),
borderRadius: BorderRadius.circular(AppDesignTokens.controlRadius),
padding: AppDesignTokens.textFieldPadding,
),
child: shad.ComponentTheme<shad.CheckboxTheme>(
data: shad.CheckboxTheme(
size: AppDesignTokens.checkboxSize,
gap: AppDesignTokens.checkboxLabelGap,
borderColor: theme.brightness == Brightness.dark ? palette.textMuted : palette.border,
child: shad.ComponentTheme<shad.SecondaryButtonTheme>(
data: shad.SecondaryButtonTheme(
decoration: (context, states, value) {
final decoration = switch (value) {
final BoxDecoration decoration => decoration,
_ => BoxDecoration(borderRadius: BorderRadius.circular(theme.radiusMd)),
};
if (states.contains(WidgetState.disabled)) {
return decoration.copyWith(color: palette.surfaceSoft);
}
if (theme.brightness == Brightness.light) {
final background = Color.alphaBlend(palette.textPrimary.withValues(alpha: 0.08), palette.surfaceSoft);
final hoverBackground = Color.alphaBlend(
palette.textPrimary.withValues(alpha: 0.13),
palette.surfaceSoft,
);
return decoration.copyWith(color: states.contains(WidgetState.hovered) ? hoverBackground : background);
}
return value;
},
textStyle: (context, states, value) => states.contains(WidgetState.disabled)
? value.copyWith(color: palette.textMuted.withValues(alpha: 0.62))
: value,
iconTheme: (context, states, value) => states.contains(WidgetState.disabled)
? value.copyWith(color: palette.textMuted.withValues(alpha: 0.62))
: value,
),
child: shad.ComponentTheme<shad.ToastTheme>(
data: const shad.ToastTheme(toastConstraints: BoxConstraints(maxWidth: 420)),
child: child,
child: shad.ComponentTheme<shad.CheckboxTheme>(
data: shad.CheckboxTheme(
size: AppDesignTokens.checkboxSize,
gap: AppDesignTokens.checkboxLabelGap,
borderColor: theme.brightness == Brightness.dark ? palette.textMuted : palette.border,
),
child: shad.ComponentTheme<shad.ToastTheme>(
data: const shad.ToastTheme(toastConstraints: BoxConstraints(maxWidth: 420)),
child: child,
),
),
),
),
@@ -21,6 +21,8 @@ class AppDesignTokens {
static const double settingsFormControlWidth = 420;
static const double settingsNumberControlWidth = 168;
static const double settingsItemMinLabelWidth = 160;
static const EdgeInsets textFieldPadding = EdgeInsets.symmetric(horizontal: 12, vertical: 11);
static const EdgeInsets numberTextFieldPadding = EdgeInsets.symmetric(horizontal: 12, vertical: 10);
static const EdgeInsets pagePadding = EdgeInsets.symmetric(horizontal: 32);
static const EdgeInsets sidebarPadding = EdgeInsets.symmetric(horizontal: 16);
@@ -30,7 +30,15 @@ class AppLoadingButton extends StatelessWidget {
final palette = AppPalette.of(context);
final replaceChild = loading && icon == null;
final leading = loading && icon != null ? _LoadingIcon(variant: variant) : icon;
final effectiveChild = replaceChild ? _LoadingIcon(variant: variant) : child;
final effectiveChild = replaceChild
? Stack(
alignment: Alignment.center,
children: [
ExcludeSemantics(child: Opacity(opacity: 0, child: child)),
Positioned.fill(child: _LoadingIcon(variant: variant)),
],
)
: child;
final content = leading == null
? effectiveChild
: Row(
@@ -2,6 +2,7 @@ import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
import '../theme/app_design_tokens.dart';
import 'app_text_field.dart';
/// A bounded numeric text field with the app's standard spinner controls.
@@ -91,6 +92,7 @@ class _AppNumberInputState extends State<AppNumberInput> {
: TextInputType.number,
inputFormatters: formatters,
enabled: widget.enabled,
padding: AppDesignTokens.numberTextFieldPadding,
features: [
shad.InputFeature.spinner(
step: widget.step,