diff --git a/dogfooding/lib/app/app_content.dart b/dogfooding/lib/app/app_content.dart index a5bb675a8..c3384d1d5 100644 --- a/dogfooding/lib/app/app_content.dart +++ b/dogfooding/lib/app/app_content.dart @@ -208,7 +208,9 @@ class _StreamDogFoodingAppContentState builder: (context, child) { return StreamComponentFactory( builders: _componentBuilders, - child: child!, + // Above the router, so a snackbar shown from any route has a + // messenger to reach and outlives the screen that showed it. + child: StreamSnackbarScope(child: child!), ); }, ); diff --git a/dogfooding/lib/widgets/share_call_card.dart b/dogfooding/lib/widgets/share_call_card.dart index 7220288a2..bd00a4ef8 100644 --- a/dogfooding/lib/widgets/share_call_card.dart +++ b/dogfooding/lib/widgets/share_call_card.dart @@ -29,48 +29,84 @@ class _ShareCallWelcomeCardState extends State { @override Widget build(BuildContext context) { - final theme = StreamVideoTheme.of(context); - final colorScheme = StreamTheme.of(context).colorScheme; + final colorScheme = context.streamColorScheme; + final spacing = context.streamSpacing; + final borderRadius = BorderRadius.all(context.streamRadius.lg); + + // The design puts the card's edges on the participant label's. The label + // sits spacing.xs inside the tile, and the tile sits the grid's own + // padding inside the call, so the card clears both. + final gridPadding = + StreamCallParticipantsGridTheme.of(context).padding?.resolve( + Directionality.maybeOf(context), + ) ?? + EdgeInsets.all(spacing.xs); return Align( alignment: Alignment.bottomCenter, child: Padding( - padding: const EdgeInsets.all(24), - child: Container( - decoration: BoxDecoration( - color: colorScheme.backgroundElevation1, - borderRadius: BorderRadius.circular(16), + padding: gridPadding + EdgeInsets.all(spacing.xs), + child: ConstrainedBox( + constraints: BoxConstraints( + // A phone gives the card the full width between those insets; a + // wider window keeps it at the width the web design draws. + maxWidth: context.streamScreenSize.isSmall ? double.infinity : 360, ), - foregroundDecoration: BoxDecoration( - border: Border.all(color: colorScheme.borderDefault), - borderRadius: BorderRadius.circular(16), - ), - clipBehavior: .antiAlias, - constraints: const BoxConstraints(maxWidth: 600), - child: ExpansionTile( - title: Padding( - padding: const EdgeInsets.all(8), - child: Text( - 'Your meeting is live!', - style: theme.textTheme.title3, + child: Material( + // Elevation rather than a painted shadow, so the card lifts off the + // call the same way every other raised Stream surface does. + elevation: context.streamElevation.level3, + color: colorScheme.backgroundElevation1, + shape: RoundedRectangleBorder(borderRadius: borderRadius), + clipBehavior: Clip.antiAlias, + child: DecoratedBox( + // Drawn over the children, which the Material clips to its own + // shape — an outward-aligned border would be eaten otherwise. + position: DecorationPosition.foreground, + decoration: BoxDecoration( + borderRadius: borderRadius, + border: Border.all(color: colorScheme.borderDefault), ), - ), - shape: const Border( - top: BorderSide(color: Colors.transparent), - bottom: BorderSide(color: Colors.transparent), - ), - trailing: Icon( - _isExpanded ? Icons.expand_more : Icons.expand_less, - color: colorScheme.textPrimary, - ), - childrenPadding: const EdgeInsets.all(16), - onExpansionChanged: (value) => setState(() => _isExpanded = value), - children: [ - _ShareCardContent( - call: widget.call, - encryptionKey: widget.encryptionKey, + child: ExpansionTile( + title: Text( + 'Your Meeting is Live!', + style: context.streamTextTheme.headingSm.copyWith( + color: colorScheme.textPrimary, + ), + ), + shape: const Border( + top: BorderSide(color: Colors.transparent), + bottom: BorderSide(color: Colors.transparent), + ), + trailing: Icon( + _isExpanded + ? context.streamIcons.chevronUp + : context.streamIcons.chevronDown, + size: spacing.lg, + color: colorScheme.textPrimary, + ), + // minTileHeight sizes the row inside tilePadding, so the + // 16 above and below the title comes from centring it in 52. + tilePadding: EdgeInsets.symmetric(horizontal: spacing.md), + minTileHeight: 52, + childrenPadding: EdgeInsets.fromLTRB( + spacing.md, + 0, + spacing.md, + spacing.md, + ), + // The content sizes itself to the card, not to its widest row. + expandedCrossAxisAlignment: CrossAxisAlignment.stretch, + onExpansionChanged: (value) => + setState(() => _isExpanded = value), + children: [ + _ShareCardContent( + call: widget.call, + encryptionKey: widget.encryptionKey, + ), + ], ), - ], + ), ), ), ), @@ -96,7 +132,7 @@ class ShareCallParticipantsCard extends StatelessWidget { return Padding( padding: const EdgeInsets.all(24), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text('Share the link', style: theme.textTheme.title1), const SizedBox(height: 16), @@ -115,8 +151,8 @@ class _ShareCardContent extends StatelessWidget { @override Widget build(BuildContext context) { - final theme = StreamVideoTheme.of(context); - final colorScheme = StreamTheme.of(context).colorScheme; + final colorScheme = context.streamColorScheme; + final spacing = context.streamSpacing; final callId = call.id; // An encrypted call cannot be joined without the key, so an invite to one @@ -129,84 +165,76 @@ class _ShareCardContent extends StatelessWidget { ); return Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (callUrl != null) ...[ StreamButton( - iconLeft: const Icon(Icons.person_add_alt_1), + iconLeft: Icon(context.streamIcons.userAddFill), + // The default padded tap target grows each 40px pill to 48 and so + // doubles the gap the design puts between the two; a full-width + // button keeps a large tap area without it. + themeStyle: const StreamButtonThemeStyle( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), onPressed: () async { await SharePlus.instance.share( ShareParams(uri: Uri.parse(callUrl)), ); }, - child: const Text('Share link with others'), + child: const Text('Add Others'), ), - const SizedBox(height: 16), + SizedBox(height: spacing.xs), ], - Text( - 'Share this call ID with others you want in the meeting.', - style: theme.textTheme.body, - ), - const SizedBox(height: 8), StreamButton( style: StreamButtonStyle.secondary, type: StreamButtonType.outline, - iconLeft: const Icon(Icons.copy_all), + iconLeft: Icon(context.streamIcons.copyFill), + themeStyle: const StreamButtonThemeStyle( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), onPressed: () async { await Clipboard.setData(ClipboardData(text: callId)); if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Row( - children: [ - Icon(Icons.check, color: colorScheme.accentSuccess), - const SizedBox(width: 8), - Text( - 'Call ID copied to clipboard', - style: theme.textTheme.body.copyWith( - color: theme.colorTheme.textHighEmphasis, - ), - ), - ], - ), + StreamSnackbarMessenger.of(context).show( + StreamSnackbar( + message: const Text('Call ID copied to clipboard'), + variant: StreamSnackbarVariant.success, ), ); } }, - child: Text('Call id: $callId'), + child: const Text('Copy Call ID'), ), if (callUrl != null) ...[ - Padding( - padding: const EdgeInsets.only(top: 16, bottom: 8), - child: Text( - 'Or scan the QR code to join from another device', - style: theme.textTheme.body, - ), - ), + SizedBox(height: spacing.md), Container( - width: double.infinity, + height: 160, alignment: Alignment.center, decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - color: Theme.of(context).scaffoldBackgroundColor, + borderRadius: BorderRadius.all(context.streamRadius.lg), + color: colorScheme.backgroundSurface, ), - padding: const EdgeInsets.all(16), - child: Container( + padding: EdgeInsets.all(spacing.md), + child: DecoratedBox( decoration: BoxDecoration( color: Colors.white, - borderRadius: BorderRadius.circular(16), + borderRadius: BorderRadius.all(context.streamRadius.xl), ), - constraints: const BoxConstraints(maxHeight: 150), child: AspectRatio( aspectRatio: 1, - child: QrImageView( - data: callUrl, - size: 200, - ), + child: QrImageView(data: callUrl, size: 200), ), ), ), + SizedBox(height: spacing.sm), + Text( + 'Scan the QR code to join from another device.', + textAlign: TextAlign.center, + style: context.streamTextTheme.metadataDefault.copyWith( + color: colorScheme.textSecondary, + ), + ), ], ], );