From e18d422d7260d3faffb211c870510a5d6d51a7fd Mon Sep 17 00:00:00 2001 From: klopez4212 Date: Sun, 1 Nov 2026 17:53:41 +0000 Subject: [PATCH] fix(mobile): fill uploaded avatars on the Agents page (#8882) Uploaded images on the mobile Agents page now crop to fill the avatar squircle, matching desktop. Animated avatars and bundled character posters retain their existing sizing. Validation: `just mobile-check mobile-test` passed (4,050 Flutter tests; 34 skipped), plus before/after rendering of the production card. Regression coverage checks uploaded, preset and animated images. Snapshots use a sample rectangular PNG in a local Flutter widget preview, not an installed build. | Before | After | | --- | --- | | ![Uploaded PNG leaves gaps in the avatar frame](https://github.com/user-attachments/assets/8f8591a1-48fd-4496-99e6-fd1c978c79bc) | ![Uploaded PNG fills the avatar squircle](https://github.com/user-attachments/assets/c7a3191f-2cd1-43ec-876a-baeb7896bb45) | Signed-off-by: kenny lopez Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz> --- mobile/lib/features/agents/agent_card.dart | 7 ++- .../features/agents/agent_card_grid_test.dart | 53 +++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/mobile/lib/features/agents/agent_card.dart b/mobile/lib/features/agents/agent_card.dart index d627668a52..24cb4debc5 100644 --- a/mobile/lib/features/agents/agent_card.dart +++ b/mobile/lib/features/agents/agent_card.dart @@ -45,6 +45,9 @@ class AgentCard extends StatelessWidget { style: context.textTheme.headlineMedium, ), ); + final portraitFit = usesInsetAgentArtwork(imageUrl) + ? BoxFit.contain + : BoxFit.cover; final portrait = animation != null && !MediaQuery.disableAnimationsOf(context) && @@ -52,12 +55,12 @@ class AgentCard extends StatelessWidget { ? ProgressiveAnimatedAvatar( descriptor: animation, fallback: fallback, - fit: BoxFit.contain, + fit: portraitFit, ) : AvatarImageContent( imageUrl: poster, fallback: fallback, - fit: BoxFit.contain, + fit: portraitFit, ); return ClipRRect( borderRadius: BorderRadius.circular(Radii.card), diff --git a/mobile/test/features/agents/agent_card_grid_test.dart b/mobile/test/features/agents/agent_card_grid_test.dart index 597b5c0478..cedee0229a 100644 --- a/mobile/test/features/agents/agent_card_grid_test.dart +++ b/mobile/test/features/agents/agent_card_grid_test.dart @@ -3,11 +3,64 @@ import 'dart:ui' show SemanticsAction; import 'package:buzz/features/agents/agent_card.dart'; import 'package:buzz/features/agents/agent_card_grid.dart'; import 'package:buzz/shared/theme/theme.dart'; +import 'package:buzz/shared/widgets/avatar_image.dart'; +import 'package:buzz/shared/widgets/avatar_shape.dart'; +import 'package:buzz/shared/widgets/progressive_animated_avatar.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; void main() { + testWidgets( + 'uploaded portraits cover the frame while character artwork stays contained', + (tester) async { + for (final (url, expectedFit) in [ + ('https://example.com/portrait.png', BoxFit.cover), + ( + 'https://dwwgwmfqqjotj.cloudfront.net/avatars/20260925T154625166Z/poster/pollies/pollies-10.png', + BoxFit.contain, + ), + ( + 'https://example.com/portrait.png#buzz-anim=https%3A%2F%2Fexample.com%2Fanimated.png', + BoxFit.contain, + ), + ]) { + await tester.pumpWidget( + ProviderScope( + child: MaterialApp( + theme: AppTheme.light(), + home: Center( + child: SizedBox( + width: 180, + height: 240, + child: AgentCard( + name: 'Portrait', + imageUrl: url, + onOpen: () {}, + ), + ), + ), + ), + ), + ); + final portrait = find.descendant( + of: find.byType(AvatarClip), + matching: find.byType(AvatarImageContent), + ); + expect(portrait, findsOneWidget); + expect(tester.widget(portrait).fit, expectedFit); + final animation = find.byType(ProgressiveAnimatedAvatar); + if (animation.evaluate().isNotEmpty) { + expect( + tester.widget(animation).fit, + expectedFit, + ); + } + expect(tester.takeException(), isNull); + } + }, + ); + testWidgets('whole card opens profile without stealing footer taps', ( tester, ) async {