Files
buzz/mobile/lib/features/channels/message_gallery_frame.dart
klopez4212 46e60206a4 fix(mobile): fill and stabilize content-aware loading placeholders (#8083)
Fill mobile loading views across the screen and use known image, video,
voice-note, and file shapes in message placeholders. Freeze each loading
cycle’s layout so arriving metadata cannot reshuffle the shimmer just
before content appears; preserve cached Home and forum content during
reconnects.

Validation: mobile analysis and full test suite passed (2,752 passed, 4
skipped), plus 3 unconfigured-push tests and file-size checks. Installed
and launched the loading changes on Pixel 10 before the latest main
merge. Full `just ci` was stopped during desktop checks due to low disk
space.

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
2026-10-05 11:26:48 -04:00

61 lines
1.6 KiB
Dart

import 'package:flutter/material.dart';
import '../../shared/theme/theme.dart';
import '../../shared/widgets/skeleton.dart';
/// Shared carousel viewport height for real media and loading placeholders.
const messageMediaCarouselHeight = 220.0;
/// Shared label, spacing, and viewport layout for an image gallery.
class MessageGalleryFrame extends StatelessWidget {
final int count;
final Widget child;
final bool loading;
const MessageGalleryFrame({
required this.count,
required this.child,
this.loading = false,
super.key,
});
@override
Widget build(BuildContext context) {
final label = Text(
'$count images',
key: ValueKey(
loading
? 'message-skeleton-carousel-count'
: 'message-media-carousel-count',
),
style: context.textTheme.labelMedium?.copyWith(
color: context.colors.onSurfaceVariant,
fontWeight: FontWeight.w400,
),
);
return Padding(
padding: const EdgeInsets.only(top: Grid.half),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (loading)
ExcludeSemantics(
child: Stack(
alignment: Alignment.centerLeft,
children: [
Opacity(opacity: 0, child: label),
const SkeletonBar(width: 48, height: 10),
],
),
)
else
label,
const SizedBox(height: Grid.half + Grid.quarter),
child,
],
),
);
}
}