Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion dogfooding/lib/app/app_content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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!),
);
},
);
Expand Down
190 changes: 109 additions & 81 deletions dogfooding/lib/widgets/share_call_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,48 +29,84 @@ class _ShareCallWelcomeCardState extends State<ShareCallWelcomeCard> {

@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,
),
],
),
],
),
),
),
),
Expand All @@ -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),
Expand All @@ -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
Expand All @@ -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,
),
),
],
],
);
Expand Down