[READ ONLY MIRROR] Open Source TikTok alternative built on AT Protocol github.com/sprksocial/client
flutter atproto video dart
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

style(notifs): no more than 3 profiles shown

+40 -42
+40 -42
lib/src/features/notifications/ui/widgets/notification_item.dart
··· 436 436 } 437 437 438 438 Widget _buildAvatarsSection() { 439 - final authors = widget.groupedNotification.getUniqueAuthors(); 440 439 final totalCount = widget.groupedNotification.actorCount; 441 - final extraCount = totalCount - authors.length; 440 + final hasOverflow = totalCount > 3; 441 + final avatarCountToShow = hasOverflow 442 + ? 2 443 + : (totalCount >= 3 ? 3 : totalCount); 444 + final authors = widget.groupedNotification.getUniqueAuthors( 445 + limit: avatarCountToShow, 446 + ); 447 + final extraCount = hasOverflow ? totalCount - avatarCountToShow : 0; 442 448 443 449 if (authors.length == 1) { 444 450 // Single avatar ··· 459 465 } 460 466 461 467 // Multiple avatars in a row 468 + const avatarSize = 28.0; 469 + const overlapStep = 20.0; 470 + final visibleItemCount = authors.length + (extraCount > 0 ? 1 : 0); 471 + final stackWidth = avatarSize + ((visibleItemCount - 1) * overlapStep); 472 + 462 473 return SizedBox( 474 + width: stackWidth, 463 475 height: 32, 464 - child: Row( 465 - mainAxisSize: MainAxisSize.min, 476 + child: Stack( 477 + clipBehavior: Clip.none, 466 478 children: [ 467 - // Show avatars with overlap 468 479 ...authors.asMap().entries.map((entry) { 469 480 final index = entry.key; 470 481 final author = entry.value.author; ··· 472 483 final username = author.displayName ?? author.handle; 473 484 final handleHash = author.handle.hashCode; 474 485 475 - return Transform.translate( 476 - offset: Offset(-index * 8.0, 0), 486 + return Positioned( 487 + left: index * overlapStep, 477 488 child: UserAvatar( 478 489 imageUrl: avatarUrl, 479 490 username: username, 480 - size: 28, 491 + size: avatarSize, 481 492 backgroundColor: getAvatarColor(handleHash), 482 493 ), 483 494 ); 484 495 }), 485 - // Show +N count if there are more 486 496 if (extraCount > 0) 487 - Builder( 488 - builder: (context) { 489 - final theme = Theme.of(context); 490 - final colorScheme = theme.colorScheme; 491 - return Transform.translate( 492 - offset: Offset(-authors.length * 8.0, 0), 493 - child: Container( 494 - padding: const EdgeInsets.symmetric( 495 - horizontal: 6, 496 - vertical: 2, 497 - ), 497 + Positioned( 498 + left: authors.length * overlapStep, 499 + child: Builder( 500 + builder: (context) { 501 + final theme = Theme.of(context); 502 + final colorScheme = theme.colorScheme; 503 + return Container( 504 + width: avatarSize, 505 + height: avatarSize, 506 + alignment: Alignment.center, 498 507 decoration: BoxDecoration( 499 508 color: colorScheme.surfaceContainerHighest, 500 - borderRadius: BorderRadius.circular(12), 509 + shape: BoxShape.circle, 501 510 ), 502 - child: Row( 503 - mainAxisSize: MainAxisSize.min, 504 - children: [ 505 - Text( 506 - '+$extraCount', 507 - style: TextStyle( 508 - color: colorScheme.onSurfaceVariant.withAlpha(179), 509 - fontSize: 12, 510 - fontWeight: FontWeight.w500, 511 - ), 512 - ), 513 - const SizedBox(width: 2), 514 - Icon( 515 - Icons.keyboard_arrow_down, 516 - size: 14, 517 - color: colorScheme.onSurfaceVariant.withAlpha(138), 518 - ), 519 - ], 511 + child: Text( 512 + '+$extraCount', 513 + style: TextStyle( 514 + color: colorScheme.onSurfaceVariant.withAlpha(179), 515 + fontSize: 11, 516 + fontWeight: FontWeight.w600, 517 + ), 520 518 ), 521 - ), 522 - ); 523 - }, 519 + ); 520 + }, 521 + ), 524 522 ), 525 523 ], 526 524 ),