opt: оптимизация анимоджи при октрытии панели и заходе в чат

This commit is contained in:
Jganenokk
2026-07-12 13:21:40 +07:00
parent 3c6de2c496
commit 58dd027914
4 changed files with 57 additions and 8 deletions
@@ -14,6 +14,7 @@ class StickerPanelController {
duration: const Duration(milliseconds: 240),
reverseDuration: const Duration(milliseconds: 200),
);
anim.addStatusListener(_onAnimStatus);
showPanel.addListener(_onToggle);
}
@@ -21,11 +22,17 @@ class StickerPanelController {
late final AnimationController anim;
final ValueNotifier<bool> showPanel = ValueNotifier(false);
final ValueNotifier<bool> panelHold = ValueNotifier(true);
double panelHeight = 300;
Timer? _typingTimer;
void hide() => showPanel.value = false;
void _onAnimStatus(AnimationStatus status) {
final held = status != AnimationStatus.completed;
if (panelHold.value != held) panelHold.value = held;
}
void _onToggle() {
if (showPanel.value) {
anim.forward();
@@ -50,7 +57,9 @@ class StickerPanelController {
void dispose() {
_typingTimer?.cancel();
showPanel.removeListener(_onToggle);
anim.removeStatusListener(_onAnimStatus);
anim.dispose();
showPanel.dispose();
panelHold.dispose();
}
}
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:komet/frontend/screens/chats/chat/sticker_panel_controller.dart';
import 'package:komet/frontend/widgets/lottie_image.dart';
import 'package:komet/frontend/widgets/sticker_panel.dart';
import 'package:komet/models/animoji.dart';
import 'package:komet/models/sticker.dart';
@@ -21,10 +22,13 @@ class StickerPanelView extends StatelessWidget {
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: stickers.anim,
child: StickerPanel(
height: stickers.panelHeight,
onStickerTap: onStickerTap,
onEmojiTap: onEmojiTap,
child: LottieHoldScope(
isHeld: stickers.panelHold,
child: StickerPanel(
height: stickers.panelHeight,
onStickerTap: onStickerTap,
onEmojiTap: onEmojiTap,
),
),
builder: (context, child) {
final t = Curves.easeOutCubic.transform(
+8 -1
View File
@@ -79,6 +79,7 @@ import '../../widgets/confirm_dialog.dart';
import '../../widgets/connection_status.dart';
import '../../widgets/message_bubble.dart';
import '../../widgets/message_actions_overlay.dart';
import '../../widgets/lottie_image.dart';
import '../../widgets/attachment_panel.dart';
import '../../widgets/attachment/attachment_sheet.dart';
import '../../widgets/sticker_pack_sheet.dart';
@@ -465,6 +466,7 @@ class _ChatScreenState extends State<ChatScreen>
final Map<int, GlobalKey> _separatorKeys = {};
String? _lastSentId;
final ValueNotifier<int> _otherUnread = ValueNotifier(0);
final ValueNotifier<bool> _animojiHold = ValueNotifier(true);
final ValueNotifier<Set<String>> _selectedIds = ValueNotifier(const {});
late final AnimationController _selectionAnim;
@@ -740,6 +742,7 @@ class _ChatScreenState extends State<ChatScreen>
}
void _kickoffHistory() {
_animojiHold.value = false;
if (_historyKickedOff) return;
_historyKickedOff = true;
_shimmerStartTimer = Timer(const Duration(milliseconds: 150), () {
@@ -1312,6 +1315,7 @@ class _ChatScreenState extends State<ChatScreen>
WidgetsBinding.instance.removeObserver(this);
chats.chatsChanged.removeListener(_onChatsBump);
_otherUnread.dispose();
_animojiHold.dispose();
_saveDraft();
_messageController.removeListener(_onTextChanged);
_scrollController.removeListener(_onScrollForDate);
@@ -3990,7 +3994,10 @@ class _ChatScreenState extends State<ChatScreen>
),
child: AnimatedBuilder(
animation: _searchAnim,
child: underlap ? _buildUnderlapBody() : _buildColorBody(),
child: LottieHoldScope(
isHeld: _animojiHold,
child: underlap ? _buildUnderlapBody() : _buildColorBody(),
),
builder: (context, body) => Scaffold(
backgroundColor: cs.surface,
extendBodyBehindAppBar: underlap,
+32 -3
View File
@@ -64,6 +64,24 @@ class LottieScrollScope extends InheritedWidget {
!identical(oldWidget.isScrolling, isScrolling);
}
class LottieHoldScope extends InheritedWidget {
final ValueListenable<bool> isHeld;
const LottieHoldScope({
super.key,
required this.isHeld,
required super.child,
});
static ValueListenable<bool>? of(BuildContext context) => context
.dependOnInheritedWidgetOfExactType<LottieHoldScope>()
?.isHeld;
@override
bool updateShouldNotify(LottieHoldScope oldWidget) =>
!identical(oldWidget.isHeld, isHeld);
}
class LottiePlayer extends StatefulWidget {
final String lottieUrl;
final String? fallbackUrl;
@@ -97,6 +115,7 @@ class _LottiePlayerState extends State<LottiePlayer>
late final bool _native;
RlottieClip? _clip;
ValueListenable<bool>? _scrollState;
ValueListenable<bool>? _holdState;
int? _px;
bool _started = false;
bool _showedFrames = false;
@@ -110,8 +129,10 @@ class _LottiePlayerState extends State<LottiePlayer>
double? _lastElapsedMs;
bool get _isScrolling => _scrollState?.value ?? false;
bool get _isHeld => _holdState?.value ?? false;
bool get _canLoad =>
!_isScrolling &&
!_isHeld &&
(widget.eager || !LottieLoadGovernor.instance.throttled.value);
@override
@@ -131,6 +152,12 @@ class _LottiePlayerState extends State<LottiePlayer>
_scrollState = state;
_scrollState?.addListener(_onGateChanged);
}
final hold = LottieHoldScope.of(context);
if (!identical(hold, _holdState)) {
_holdState?.removeListener(_onGateChanged);
_holdState = hold;
_holdState?.addListener(_onGateChanged);
}
}
@override
@@ -155,6 +182,7 @@ class _LottiePlayerState extends State<LottiePlayer>
_deferTimer?.cancel();
LottieLoadGovernor.instance.throttled.removeListener(_onGateChanged);
_scrollState?.removeListener(_onGateChanged);
_holdState?.removeListener(_onGateChanged);
_ticker.dispose();
_releaseClip();
_frameIndex.dispose();
@@ -232,15 +260,16 @@ class _LottiePlayerState extends State<LottiePlayer>
if (_started) return;
if (_canLoad) {
_startLoad();
} else if (!_isScrolling) {
// Blocked only by the frame-time governor: defer, but never starve.
} else if (!_isScrolling && !_isHeld) {
_deferTimer ??= Timer(_maxLoadDefer, _forceDeferredLoad);
}
}
void _forceDeferredLoad() {
_deferTimer = null;
if (mounted && !_started && _clip == null && !_isScrolling) _startLoad();
if (mounted && !_started && _clip == null && !_isScrolling && !_isHeld) {
_startLoad();
}
}
void _startLoad() {