opt: оптимизация анимоджи при октрытии панели и заходе в чат
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user