mirror of
https://github.com/block/buzz.git
synced 2026-11-12 04:12:21 +00:00
At large text sizes, organization sign-in prompts could clip their message and push recovery actions off-screen. Stack actions below the message above 1.3× text, use fixed card corners, and skip the Dynamic Island morph for that layout. Follow-up to #9050, addressing [Wes’s large-text blocker](https://github.com/block/buzz/pull/9050#discussion_r4469530660). Normal-size styling and recovery callbacks are unchanged. Validation: extended existing sign-in/cancel/error journeys at 1×, 2× and 3× using Inter on a 320×568 viewport; assert text lies inside the painted clip and the card stays on-screen. Removing the fix fails all six enlarged-text cases. Mobile checks and full tests run through the push hook. Human device testing of this follow-up is pending. Local Flutter previews at 3× text (320×568): | Before | After | | --- | --- | |  |  | Signed-off-by: kenny lopez <klopez4212@gmail.com> Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
120 lines
3.4 KiB
Dart
120 lines
3.4 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../theme/theme.dart';
|
|
|
|
/// Shared text and inset action pill for temporary and persistent feedback.
|
|
class ToastCapsuleContent extends StatelessWidget {
|
|
const ToastCapsuleContent({
|
|
super.key,
|
|
required this.message,
|
|
required this.foreground,
|
|
this.actionLabel = 'Retry',
|
|
this.destructiveAction = false,
|
|
this.secondaryAction,
|
|
this.onAction,
|
|
this.stackActions = false,
|
|
});
|
|
|
|
/// Places actions below the message when enlarged text needs more width.
|
|
final bool stackActions;
|
|
|
|
final String message;
|
|
final Color foreground;
|
|
final String actionLabel;
|
|
|
|
/// Uses the theme error colors for the primary action.
|
|
final bool destructiveAction;
|
|
|
|
/// Adds a second recovery action and stacks both buttons below the message.
|
|
final ({String label, VoidCallback onPressed})? secondaryAction;
|
|
final VoidCallback? onAction;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final text = Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: Grid.gutter,
|
|
vertical: Grid.twelve,
|
|
),
|
|
child: Text(
|
|
message,
|
|
textAlign: TextAlign.center,
|
|
style: context.textTheme.bodyMedium?.copyWith(
|
|
color: foreground,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
);
|
|
if (onAction == null) return text;
|
|
Widget action(
|
|
String label,
|
|
VoidCallback onPressed, {
|
|
bool destructive = false,
|
|
bool secondary = false,
|
|
}) => FilledButton(
|
|
onPressed: onPressed,
|
|
style: FilledButton.styleFrom(
|
|
backgroundColor: destructive
|
|
? lightColorScheme.error
|
|
: secondary
|
|
? darkColorScheme.secondaryContainer
|
|
: Colors.white,
|
|
foregroundColor: destructive
|
|
? lightColorScheme.onError
|
|
: secondary
|
|
? darkColorScheme.secondary
|
|
: Colors.black,
|
|
shape: const RoundedSuperellipseBorder(
|
|
borderRadius: BorderRadius.all(Radius.circular(999)),
|
|
),
|
|
minimumSize: const Size(48, 36),
|
|
padding: const EdgeInsets.symmetric(horizontal: Grid.gutter),
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
),
|
|
child: Text(label),
|
|
);
|
|
final secondary = secondaryAction;
|
|
if (secondary != null || stackActions) {
|
|
return Padding(
|
|
padding: const EdgeInsets.fromLTRB(
|
|
Grid.twelve,
|
|
6,
|
|
Grid.twelve,
|
|
Grid.twelve,
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
text,
|
|
action(actionLabel, onAction!, destructive: destructiveAction),
|
|
if (secondary != null) ...[
|
|
const SizedBox(height: 6),
|
|
action(secondary.label, secondary.onPressed, secondary: true),
|
|
],
|
|
],
|
|
),
|
|
);
|
|
}
|
|
return IntrinsicWidth(
|
|
child: IntrinsicHeight(
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Expanded(child: Center(widthFactor: 1, child: text)),
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 6, right: 6, bottom: 6),
|
|
child: action(
|
|
actionLabel,
|
|
onAction!,
|
|
destructive: destructiveAction,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|