* Task/Ticket desktop dialogs — widened from 520 → 600dp, and tasks now uses SizedBox(width: 600) instead of ConstrainedBox(maxWidth:) which was the root cause of the "still small" issue (max-only never forced expansion)

* Pass Slip + Leave — both dialogs gained an isSheet flag; callers now branch on AppBreakpoints.tablet: bottom sheet on mobile, dialog on desktop
This commit is contained in:
2026-04-30 06:45:51 +08:00
parent 48cd3bcd60
commit 783c5eb0be
19 changed files with 1224 additions and 887 deletions
+14 -5
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import '../theme/m3_motion.dart';
import 'app_breakpoints.dart';
/// Standardized M3 Expressive page header with animated entrance.
///
@@ -18,7 +19,7 @@ class AppPageHeader extends StatelessWidget {
required this.title,
this.subtitle,
this.actions,
this.padding = const EdgeInsets.only(top: 20, bottom: 12),
this.padding,
});
final String title;
@@ -29,16 +30,18 @@ class AppPageHeader extends StatelessWidget {
/// Vertical padding around the header. Horizontal padding is handled by
/// the parent [ResponsiveBody] so only top/bottom should be set here.
final EdgeInsetsGeometry padding;
/// When null, defaults to responsive bottom padding (24dp desktop / 12dp mobile).
final EdgeInsetsGeometry? padding;
@override
Widget build(BuildContext context) {
final tt = Theme.of(context).textTheme;
final cs = Theme.of(context).colorScheme;
final isMobile = AppBreakpoints.isMobile(context);
final titleWidget = Text(
title,
style: tt.headlineSmall?.copyWith(
style: (isMobile ? tt.titleLarge : tt.headlineSmall)?.copyWith(
fontWeight: FontWeight.w700,
letterSpacing: -0.3,
color: cs.onSurface,
@@ -46,7 +49,7 @@ class AppPageHeader extends StatelessWidget {
textAlign: TextAlign.center,
);
final subtitleWidget = subtitle == null
final subtitleWidget = (subtitle == null || isMobile)
? null
: Text(
subtitle!,
@@ -93,9 +96,15 @@ class AppPageHeader extends StatelessWidget {
);
}
final effectivePadding = padding ??
EdgeInsets.only(
top: isMobile ? 12 : 20,
bottom: AppBreakpoints.isDesktop(context) ? 24 : 12,
);
return M3FadeSlideIn(
duration: M3Motion.standard,
child: Padding(padding: padding, child: content),
child: Padding(padding: effectivePadding, child: content),
);
}
}
+2 -1
View File
@@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -27,7 +28,7 @@ class OfflineBanner extends ConsumerWidget {
if (!isOnline) {
final label = pendingCount > 0
? 'Offline — $pendingCount item${pendingCount == 1 ? '' : 's'} queued'
: 'No internet — changes saved locally';
: kIsWeb ? 'No internet connection' : 'No internet — changes saved locally';
banner = _BannerStrip(
key: const ValueKey('offline'),
backgroundColor: scheme.errorContainer,