fix/feat: фикс перехода к медиа, и пагинация
This commit is contained in:
@@ -63,6 +63,7 @@ class ChatInfoScreen extends StatefulWidget {
|
||||
|
||||
class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
final _tabScrollController = ScrollController();
|
||||
final _bodyScrollController = ScrollController();
|
||||
|
||||
int _myId = 0;
|
||||
bool _isLoading = true;
|
||||
@@ -93,6 +94,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
@override
|
||||
void dispose() {
|
||||
_tabScrollController.dispose();
|
||||
_bodyScrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -251,6 +253,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
|
||||
Widget _buildScrollBody(ColorScheme cs) {
|
||||
return CustomScrollView(
|
||||
controller: _bodyScrollController,
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
@@ -762,6 +765,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
emptyLabel: emptyLabel,
|
||||
emptyIcon: emptyIcon,
|
||||
onGoToMessage: _goToMessage,
|
||||
scrollController: _bodyScrollController,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -313,8 +313,7 @@ class _ChatScreenState extends State<ChatScreen>
|
||||
final total = counters.values.fold<int>(0, (a, b) => a + b);
|
||||
return {
|
||||
'counters': [
|
||||
for (final key in order)
|
||||
{'reaction': key, 'count': counters[key]},
|
||||
for (final key in order) {'reaction': key, 'count': counters[key]},
|
||||
],
|
||||
'yourReaction': ?your,
|
||||
'totalCount': total,
|
||||
@@ -2292,7 +2291,10 @@ class _ChatScreenState extends State<ChatScreen>
|
||||
navigator.popUntil(
|
||||
(r) => r == chatRoute,
|
||||
);
|
||||
_requestGoToMessage(messageId, time);
|
||||
_requestGoToMessage(
|
||||
messageId,
|
||||
time,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -3371,6 +3373,24 @@ class _ChatScreenState extends State<ChatScreen>
|
||||
_finishTargetNavigation();
|
||||
}
|
||||
|
||||
({int min, int max})? _laidOutMessageRange(List<Object> items) {
|
||||
int? lo;
|
||||
int? hi;
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
final it = items[i];
|
||||
if (it is! _MessageItem) continue;
|
||||
final ro = _keyForMessage(
|
||||
it.message.id,
|
||||
).currentContext?.findRenderObject();
|
||||
if (ro is RenderBox && ro.attached) {
|
||||
lo ??= i;
|
||||
hi = i;
|
||||
}
|
||||
}
|
||||
if (lo == null) return null;
|
||||
return (min: lo, max: hi!);
|
||||
}
|
||||
|
||||
Future<void> _scrollToMessagePrecise(
|
||||
String id, {
|
||||
double alignment = 0.32,
|
||||
@@ -3381,28 +3401,28 @@ class _ChatScreenState extends State<ChatScreen>
|
||||
_suppressHistoryAutoload = true;
|
||||
try {
|
||||
var stable = 0;
|
||||
for (var iter = 0; iter < 48; iter++) {
|
||||
for (var iter = 0; iter < 120; iter++) {
|
||||
if (!mounted || !_scrollController.hasClients) return;
|
||||
final listObj = _listKey.currentContext?.findRenderObject();
|
||||
final boxObj = _keyForMessage(id).currentContext?.findRenderObject();
|
||||
final p = _scrollController.position;
|
||||
|
||||
if (boxObj is RenderBox && boxObj.attached && listObj is RenderBox) {
|
||||
if (listObj is RenderBox && boxObj is RenderBox && boxObj.attached) {
|
||||
final viewportH = listObj.size.height;
|
||||
final actualTop = boxObj
|
||||
.localToGlobal(Offset.zero, ancestor: listObj)
|
||||
.dy;
|
||||
final desiredTop = alignment * viewportH;
|
||||
final delta = desiredTop - actualTop;
|
||||
final p = _scrollController.position;
|
||||
final target = (p.pixels + delta).clamp(
|
||||
p.minScrollExtent,
|
||||
p.maxScrollExtent,
|
||||
);
|
||||
|
||||
if (delta.abs() <= 4.0 || (target - p.pixels).abs() <= 1.0) {
|
||||
if (delta.abs() <= 2.0 || (target - p.pixels).abs() <= 1.0) {
|
||||
stable++;
|
||||
if (stable >= 3) return;
|
||||
await Future.delayed(const Duration(milliseconds: 130));
|
||||
if (stable >= 4) return;
|
||||
await Future.delayed(const Duration(milliseconds: 60));
|
||||
continue;
|
||||
}
|
||||
stable = 0;
|
||||
@@ -3417,19 +3437,20 @@ class _ChatScreenState extends State<ChatScreen>
|
||||
(it) => it is _MessageItem && it.message.id == id,
|
||||
);
|
||||
if (pos == -1) return;
|
||||
var below = 0.0;
|
||||
for (var i = pos + 1; i < items.length; i++) {
|
||||
below += _estimatedItemExtent(items[i]);
|
||||
}
|
||||
final p = _scrollController.position;
|
||||
final maxExtent = p.maxScrollExtent;
|
||||
final viewportH = listObj is RenderBox ? listObj.size.height : 500.0;
|
||||
var targetOffset = below.clamp(0.0, maxExtent).toDouble();
|
||||
if ((targetOffset - p.pixels).abs() < 4.0) {
|
||||
targetOffset = (p.pixels + viewportH * 0.8).clamp(0.0, maxExtent);
|
||||
if ((targetOffset - p.pixels).abs() < 4.0) return;
|
||||
}
|
||||
_scrollController.jumpTo(targetOffset);
|
||||
|
||||
final viewportH = listObj is RenderBox ? listObj.size.height : 600.0;
|
||||
var stepMag = viewportH * 0.8;
|
||||
if (stepMag > 700) stepMag = 700;
|
||||
|
||||
final range = _laidOutMessageRange(items);
|
||||
final step = (range != null && pos > range.max) ? -stepMag : stepMag;
|
||||
|
||||
final target = (p.pixels + step).clamp(
|
||||
p.minScrollExtent,
|
||||
p.maxScrollExtent,
|
||||
);
|
||||
if ((target - p.pixels).abs() < 1.0) return;
|
||||
_scrollController.jumpTo(target);
|
||||
await WidgetsBinding.instance.endOfFrame;
|
||||
}
|
||||
} finally {
|
||||
@@ -3437,48 +3458,6 @@ class _ChatScreenState extends State<ChatScreen>
|
||||
}
|
||||
}
|
||||
|
||||
double _estimatedItemExtent(Object item) {
|
||||
if (item is! _MessageItem) return 44.0;
|
||||
final msg = item.message;
|
||||
var base = 0.0;
|
||||
final atts = msg.attachments;
|
||||
if (atts != null && atts.isNotEmpty) {
|
||||
for (final a in atts) {
|
||||
switch (a.type) {
|
||||
case AttachmentType.photo:
|
||||
case AttachmentType.video:
|
||||
int? w;
|
||||
int? h;
|
||||
if (a is PhotoAttachment) {
|
||||
w = a.width;
|
||||
h = a.height;
|
||||
} else if (a is VideoAttachment) {
|
||||
w = a.width;
|
||||
h = a.height;
|
||||
}
|
||||
base += (w != null && h != null && w > 0)
|
||||
? (236.0 * h / w).clamp(120.0, 360.0)
|
||||
: 260.0;
|
||||
base += 12;
|
||||
case AttachmentType.sticker:
|
||||
base += 160;
|
||||
case AttachmentType.audio:
|
||||
base += 72;
|
||||
case AttachmentType.file:
|
||||
base += 80;
|
||||
case AttachmentType.share:
|
||||
base += 96;
|
||||
default:
|
||||
base += 60;
|
||||
}
|
||||
}
|
||||
}
|
||||
final textLen = msg.text?.length ?? 0;
|
||||
if (textLen > 0) base += 24.0 + (textLen ~/ 34) * 20.0;
|
||||
if (base <= 0) base = _avgMessageHeight;
|
||||
return base.clamp(44.0, 1200.0).toDouble();
|
||||
}
|
||||
|
||||
Future<void> _openSearchResult(MessageSearchResult result) async {
|
||||
_closeSearch();
|
||||
await WidgetsBinding.instance.endOfFrame;
|
||||
@@ -5597,9 +5576,16 @@ class _SelectableMessageRowState extends State<_SelectableMessageRow> {
|
||||
|
||||
final GlobalKey _boundaryKey = GlobalKey();
|
||||
Offset? _lastTapDown;
|
||||
Timer? _openTimer;
|
||||
|
||||
bool _isPinnedNow() => widget.isPinned();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_openTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _openMenu() {
|
||||
final ctx = _boundaryKey.currentContext;
|
||||
if (ctx == null) return;
|
||||
@@ -5684,7 +5670,10 @@ class _SelectableMessageRowState extends State<_SelectableMessageRow> {
|
||||
if (widget.isSelectionActive()) {
|
||||
widget.onToggleSelection();
|
||||
} else {
|
||||
_openMenu();
|
||||
_openTimer?.cancel();
|
||||
_openTimer = Timer(const Duration(milliseconds: 200), () {
|
||||
if (mounted && !widget.isSelectionActive()) _openMenu();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,11 @@ Widget _emptyState(ColorScheme cs, String label, IconData icon) {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, color: cs.onSurfaceVariant.withValues(alpha: 0.35), size: 48),
|
||||
Icon(
|
||||
icon,
|
||||
color: cs.onSurfaceVariant.withValues(alpha: 0.35),
|
||||
size: 48,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(label, style: TextStyle(color: cs.onSurfaceVariant, fontSize: 15)),
|
||||
],
|
||||
@@ -220,7 +224,10 @@ void _notifySave(BuildContext context, MediaSaveResult result) {
|
||||
result.toGallery ? 'Сохранено в галерею' : 'Файл сохранён',
|
||||
);
|
||||
} else {
|
||||
showCustomNotification(context, 'Не удалось сохранить: ${result.error ?? ''}');
|
||||
showCustomNotification(
|
||||
context,
|
||||
'Не удалось сохранить: ${result.error ?? ''}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,6 +459,7 @@ class SharedMediaTab extends StatefulWidget {
|
||||
final String emptyLabel;
|
||||
final IconData emptyIcon;
|
||||
final void Function(String messageId, int time) onGoToMessage;
|
||||
final ScrollController? scrollController;
|
||||
|
||||
const SharedMediaTab({
|
||||
super.key,
|
||||
@@ -462,6 +470,7 @@ class SharedMediaTab extends StatefulWidget {
|
||||
required this.emptyLabel,
|
||||
required this.emptyIcon,
|
||||
required this.onGoToMessage,
|
||||
this.scrollController,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -474,15 +483,33 @@ class _SharedMediaTabState extends State<SharedMediaTab> {
|
||||
bool _loading = true;
|
||||
bool _loadingMore = false;
|
||||
bool _canLoadMore = false;
|
||||
int _total = 0;
|
||||
final List<SharedMediaItem> _items = [];
|
||||
final Set<String> _seen = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
widget.scrollController?.addListener(_onScroll);
|
||||
_load(widget.anchorMessageId, initial: true);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.scrollController?.removeListener(_onScroll);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onScroll() {
|
||||
if (!_hasMore || _loadingMore || _loading) return;
|
||||
final controller = widget.scrollController;
|
||||
if (controller == null || !controller.hasClients) return;
|
||||
final position = controller.position;
|
||||
if (position.pixels >= position.maxScrollExtent - 800) {
|
||||
_loadMore();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _load(String anchor, {required bool initial}) async {
|
||||
final page = await sharedContentModule.fetchMedia(
|
||||
chatId: widget.chatId,
|
||||
@@ -493,7 +520,6 @@ class _SharedMediaTabState extends State<SharedMediaTab> {
|
||||
);
|
||||
if (!mounted) return;
|
||||
|
||||
final pageMessageIds = page.items.map((e) => e.messageId).toSet();
|
||||
var added = 0;
|
||||
for (final item in page.items) {
|
||||
if (_seen.add(item.dedupKey)) {
|
||||
@@ -502,16 +528,29 @@ class _SharedMediaTabState extends State<SharedMediaTab> {
|
||||
}
|
||||
}
|
||||
_items.sort((a, b) => b.time.compareTo(a.time));
|
||||
_total = page.total > _total ? page.total : _total;
|
||||
|
||||
setState(() {
|
||||
_canLoadMore = added > 0 && pageMessageIds.length >= _pageSize;
|
||||
_canLoadMore = added > 0 && _items.length < _total;
|
||||
_loading = false;
|
||||
_loadingMore = false;
|
||||
});
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _maybeAutoLoad());
|
||||
}
|
||||
|
||||
void _maybeAutoLoad() {
|
||||
if (!mounted || !_hasMore || _loadingMore) return;
|
||||
final controller = widget.scrollController;
|
||||
if (controller == null || !controller.hasClients) return;
|
||||
final position = controller.position;
|
||||
if (position.maxScrollExtent - position.pixels <= 800) {
|
||||
_loadMore();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadMore() async {
|
||||
if (_loadingMore || _items.isEmpty) return;
|
||||
if (_loadingMore || _loading || _items.isEmpty) return;
|
||||
setState(() => _loadingMore = true);
|
||||
await _load(_items.last.messageId, initial: false);
|
||||
}
|
||||
@@ -589,7 +628,10 @@ class _SharedMediaTabState extends State<SharedMediaTab> {
|
||||
);
|
||||
}
|
||||
|
||||
return Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: children);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _mediaGrid(ColorScheme cs, List<SharedMediaItem> items) {
|
||||
@@ -633,7 +675,9 @@ class _MediaTile extends StatelessWidget {
|
||||
final att = item.attachment;
|
||||
final video = att is VideoAttachment ? att : null;
|
||||
final duration = video?.duration ?? 0;
|
||||
final thumb = att.baseUrl?.isNotEmpty == true ? att.baseUrl : att.previewData;
|
||||
final thumb = att.baseUrl?.isNotEmpty == true
|
||||
? att.baseUrl
|
||||
: att.previewData;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => _open(context),
|
||||
@@ -771,7 +815,9 @@ class _FileRow extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
ext.isEmpty ? formatBytes(size) : '$ext • ${formatBytes(size)}',
|
||||
ext.isEmpty
|
||||
? formatBytes(size)
|
||||
: '$ext • ${formatBytes(size)}',
|
||||
style: TextStyle(color: cs.onSurfaceVariant, fontSize: 13),
|
||||
),
|
||||
],
|
||||
@@ -1094,7 +1140,10 @@ class _ProfileVoiceTileState extends State<_ProfileVoiceTile> {
|
||||
child: Container(
|
||||
width: 46,
|
||||
height: 46,
|
||||
decoration: BoxDecoration(color: cs.primary, shape: BoxShape.circle),
|
||||
decoration: BoxDecoration(
|
||||
color: cs.primary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: _loadingAudio
|
||||
? const Padding(
|
||||
padding: EdgeInsets.all(13),
|
||||
|
||||
@@ -728,8 +728,7 @@ class _MessageActionsLayerState extends State<_MessageActionsLayer>
|
||||
}
|
||||
|
||||
Rect _reactionAnchorRect() {
|
||||
if (_effectiveStyle == MessageActionsStyle.list &&
|
||||
_menuRect != Rect.zero) {
|
||||
if (_effectiveStyle == MessageActionsStyle.list && _menuRect != Rect.zero) {
|
||||
return _menuRect;
|
||||
}
|
||||
if (_buttonHitRects.isNotEmpty) {
|
||||
@@ -749,7 +748,8 @@ class _MessageActionsLayerState extends State<_MessageActionsLayer>
|
||||
final keyboardInset = MediaQuery.viewInsetsOf(context).bottom;
|
||||
|
||||
final safeTop = padding.top + 8;
|
||||
final safeBottom = size.height - math.max(padding.bottom, keyboardInset) - 8;
|
||||
final safeBottom =
|
||||
size.height - math.max(padding.bottom, keyboardInset) - 8;
|
||||
|
||||
final quick = widget.quickReactions;
|
||||
const chevronCell = 38.0;
|
||||
@@ -762,8 +762,10 @@ class _MessageActionsLayerState extends State<_MessageActionsLayer>
|
||||
final maxPillWidth = size.width - 16;
|
||||
double cell = maxCell;
|
||||
if (pillWidth > maxPillWidth) {
|
||||
cell = ((maxPillWidth - pillPad * 2 - chevronCell) / quick.length)
|
||||
.clamp(28.0, maxCell);
|
||||
cell = ((maxPillWidth - pillPad * 2 - chevronCell) / quick.length).clamp(
|
||||
28.0,
|
||||
maxCell,
|
||||
);
|
||||
pillWidth = pillPad * 2 + quick.length * cell + chevronCell;
|
||||
}
|
||||
|
||||
@@ -777,7 +779,10 @@ class _MessageActionsLayerState extends State<_MessageActionsLayer>
|
||||
double pillTop = pillAbove
|
||||
? anchor.top - gap - pillHeight
|
||||
: anchor.bottom + gap;
|
||||
pillTop = pillTop.clamp(safeTop, math.max(safeTop, safeBottom - pillHeight));
|
||||
pillTop = pillTop.clamp(
|
||||
safeTop,
|
||||
math.max(safeTop, safeBottom - pillHeight),
|
||||
);
|
||||
final collapsed = Rect.fromLTWH(pillLeft, pillTop, pillWidth, pillHeight);
|
||||
|
||||
final panelWidth = math.min(size.width - 24, 300.0);
|
||||
@@ -796,13 +801,16 @@ class _MessageActionsLayerState extends State<_MessageActionsLayer>
|
||||
panelTop = math.max(safeTop, panelBottom - desiredHeight);
|
||||
panelHeight = panelBottom - panelTop;
|
||||
}
|
||||
final expanded = Rect.fromLTWH(panelLeft, panelTop, panelWidth, panelHeight);
|
||||
final expanded = Rect.fromLTWH(
|
||||
panelLeft,
|
||||
panelTop,
|
||||
panelWidth,
|
||||
panelHeight,
|
||||
);
|
||||
|
||||
final morph = Rect.lerp(collapsed, expanded, e)!;
|
||||
final radius = ui.lerpDouble(pillHeight / 2, 20.0, e)!;
|
||||
final entryAlign = pillAbove
|
||||
? Alignment.bottomLeft
|
||||
: Alignment.topLeft;
|
||||
final entryAlign = pillAbove ? Alignment.bottomLeft : Alignment.topLeft;
|
||||
|
||||
return IgnorePointer(
|
||||
ignoring: t < 0.5,
|
||||
@@ -810,8 +818,7 @@ class _MessageActionsLayerState extends State<_MessageActionsLayer>
|
||||
opacity: t,
|
||||
child: Stack(
|
||||
children: [
|
||||
if (e < 0.999)
|
||||
_buildCloudTail(collapsed, pillAbove, cs, t, e),
|
||||
if (e < 0.999) _buildCloudTail(collapsed, pillAbove, cs, t, e),
|
||||
Positioned.fromRect(
|
||||
rect: morph,
|
||||
child: Transform.scale(
|
||||
@@ -979,10 +986,7 @@ class _MessageActionsLayerState extends State<_MessageActionsLayer>
|
||||
child: Opacity(
|
||||
opacity: fade,
|
||||
child: Stack(
|
||||
children: [
|
||||
_tailCircle(big, 8.0, cs),
|
||||
_tailCircle(small, 5.0, cs),
|
||||
],
|
||||
children: [_tailCircle(big, 8.0, cs), _tailCircle(small, 5.0, cs)],
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -1429,42 +1433,45 @@ class _ReactionEmojiPickerState extends State<_ReactionEmojiPicker> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cs = Theme.of(context).colorScheme;
|
||||
return Column(
|
||||
children: [
|
||||
_buildSearchField(cs),
|
||||
Expanded(
|
||||
child: !_loaded
|
||||
? const Center(
|
||||
child: SizedBox(
|
||||
width: 26,
|
||||
height: 26,
|
||||
child: CircularProgressIndicator(strokeWidth: 2.4),
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Column(
|
||||
children: [
|
||||
_buildSearchField(cs),
|
||||
Expanded(
|
||||
child: !_loaded
|
||||
? const Center(
|
||||
child: SizedBox(
|
||||
width: 26,
|
||||
height: 26,
|
||||
child: CircularProgressIndicator(strokeWidth: 2.4),
|
||||
),
|
||||
)
|
||||
: _results.isEmpty
|
||||
? const SizedBox.shrink()
|
||||
: GridView.builder(
|
||||
padding: const EdgeInsets.fromLTRB(8, 2, 8, 10),
|
||||
keyboardDismissBehavior:
|
||||
ScrollViewKeyboardDismissBehavior.onDrag,
|
||||
addAutomaticKeepAlives: false,
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 40,
|
||||
mainAxisSpacing: 2,
|
||||
crossAxisSpacing: 2,
|
||||
),
|
||||
itemCount: _results.length,
|
||||
itemBuilder: (context, i) {
|
||||
final emoji = _results[i];
|
||||
return _EmojiCell(
|
||||
emoji: emoji,
|
||||
onTap: () => widget.onPick(emoji),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
: _results.isEmpty
|
||||
? const SizedBox.shrink()
|
||||
: GridView.builder(
|
||||
padding: const EdgeInsets.fromLTRB(8, 2, 8, 10),
|
||||
keyboardDismissBehavior:
|
||||
ScrollViewKeyboardDismissBehavior.onDrag,
|
||||
addAutomaticKeepAlives: false,
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 40,
|
||||
mainAxisSpacing: 2,
|
||||
crossAxisSpacing: 2,
|
||||
),
|
||||
itemCount: _results.length,
|
||||
itemBuilder: (context, i) {
|
||||
final emoji = _results[i];
|
||||
return _EmojiCell(
|
||||
emoji: emoji,
|
||||
onTap: () => widget.onPick(emoji),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1533,9 +1540,7 @@ class _EmojiCell extends StatelessWidget {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: onTap,
|
||||
child: Center(
|
||||
child: Text(emoji, style: const TextStyle(fontSize: 22)),
|
||||
),
|
||||
child: Center(child: Text(emoji, style: const TextStyle(fontSize: 22))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user