diff --git a/lib/emoji_picker_flutter.dart b/lib/emoji_picker_flutter.dart index 7524c921..b065ee68 100644 --- a/lib/emoji_picker_flutter.dart +++ b/lib/emoji_picker_flutter.dart @@ -12,6 +12,7 @@ export 'package:emoji_picker_flutter/src/category_view/category_view.dart'; export 'package:emoji_picker_flutter/src/category_view/category_view_config.dart'; export 'package:emoji_picker_flutter/src/category_view/default_category_tab_bar.dart'; export 'package:emoji_picker_flutter/src/category_view/default_category_view.dart'; +export 'package:emoji_picker_flutter/src/category_view/recent_emojis_state_update_policy.dart'; export 'package:emoji_picker_flutter/src/category_view/recent_tab_behavior.dart'; export 'package:emoji_picker_flutter/src/config.dart'; export 'package:emoji_picker_flutter/src/default_emoji_set.dart'; diff --git a/lib/src/category_view/category_view_config.dart b/lib/src/category_view/category_view_config.dart index f128f366..6d7bf9d5 100644 --- a/lib/src/category_view/category_view_config.dart +++ b/lib/src/category_view/category_view_config.dart @@ -26,6 +26,7 @@ class CategoryViewConfig { this.dividerColor, this.categoryIcons = const CategoryIcons(), this.customCategoryView, + this.recentEmojisUpdatePolicy = RecentEmojisStateUpdatePolicy.notFromRecent, }); /// Tab bar height @@ -69,6 +70,14 @@ class CategoryViewConfig { /// Hot reload is not supported final CategoryViewBuilder? customCategoryView; + /// Determines the behavior of recent emojis list update in the state. + /// It can be configured to prevent recent emojis list from jumping when + /// user selects and emoji. + /// Defaults [RecentEmojisStateUpdatePolicy.notFromRecent]. + /// NOTE: This affects only the widget state changes. Locally stored recent + /// emojis list is getting updated regardless of this setting. + final RecentEmojisStateUpdatePolicy recentEmojisUpdatePolicy; + @override bool operator ==(other) { return (other is CategoryViewConfig) && @@ -83,7 +92,8 @@ class CategoryViewConfig { other.iconColorSelected == iconColorSelected && other.backspaceColor == backspaceColor && other.dividerColor == dividerColor && - other.categoryIcons == categoryIcons; + other.categoryIcons == categoryIcons && + other.recentEmojisUpdatePolicy == recentEmojisUpdatePolicy; } @override @@ -99,5 +109,6 @@ class CategoryViewConfig { iconColorSelected.hashCode ^ backspaceColor.hashCode ^ dividerColor.hashCode ^ - categoryIcons.hashCode; + categoryIcons.hashCode ^ + recentEmojisUpdatePolicy.hashCode; } diff --git a/lib/src/category_view/recent_emojis_state_update_policy.dart b/lib/src/category_view/recent_emojis_state_update_policy.dart new file mode 100644 index 00000000..e55e8136 --- /dev/null +++ b/lib/src/category_view/recent_emojis_state_update_policy.dart @@ -0,0 +1,20 @@ +/// Enum for defining the state update policy for recent emojis. +enum RecentEmojisStateUpdatePolicy { + /// No state updates. + never, + + /// State updates only when selected emoji is not from the recent list. + notFromRecent, + + /// State updates on every emoji selection. + always; + + /// Returns true if the policy is [never]. + bool get isNever => this == never; + + /// Returns true if the policy is [notFromRecent]. + bool get isNotFromRecent => this == notFromRecent; + + /// Returns true if the policy is [always]. + bool get isAlways => this == always; +} diff --git a/lib/src/emoji_picker.dart b/lib/src/emoji_picker.dart index 16dee245..f4f6375b 100644 --- a/lib/src/emoji_picker.dart +++ b/lib/src/emoji_picker.dart @@ -154,17 +154,14 @@ class EmojiPickerState extends State { final _emojiPickerInternalUtils = EmojiPickerInternalUtils(); /// Update recentEmoji list from outside using EmojiPickerUtils - void updateRecentEmoji(List recentEmoji, - {bool refresh = false}) { + void updateRecentEmoji(List recentEmoji) { _recentEmoji = recentEmoji; final recentTabIndex = _categoryEmoji .indexWhere((element) => element.category == Category.RECENT); if (recentTabIndex != -1) { _categoryEmoji[recentTabIndex] = _categoryEmoji[recentTabIndex] .copyWith(emoji: _recentEmoji.map((e) => e.emoji).toList()); - if (mounted && refresh) { - setState(() {}); - } + setState(() {}); } } @@ -300,24 +297,20 @@ class EmojiPickerState extends State { RecentTabBehavior.POPULAR) { _emojiPickerInternalUtils .addEmojiToPopularUsed(emoji: emoji, config: widget.config) - .then((newRecentEmoji) => { - // we don't want to rebuild the widget if user is currently on - // the RECENT tab, it will make emojis jump since sorting - // is based on the use frequency - updateRecentEmoji(newRecentEmoji, - refresh: category != Category.RECENT), - }); + .then((newRecentEmoji) { + if (_considerRecentEmojisStateUpdate(category)) { + updateRecentEmoji(newRecentEmoji); + } + }); } else if (widget.config.categoryViewConfig.recentTabBehavior == RecentTabBehavior.RECENT) { _emojiPickerInternalUtils .addEmojiToRecentlyUsed(emoji: emoji, config: widget.config) - .then((newRecentEmoji) => { - // we don't want to rebuild the widget if user is currently on - // the RECENT tab, it will make emojis jump since sorting - // is based on the use frequency - updateRecentEmoji(newRecentEmoji, - refresh: category != Category.RECENT), - }); + .then((newRecentEmoji) { + if (_considerRecentEmojisStateUpdate(category)) { + updateRecentEmoji(newRecentEmoji); + } + }); } if (widget.textEditingController != null) { @@ -356,6 +349,17 @@ class EmojiPickerState extends State { } } + bool _considerRecentEmojisStateUpdate(Category? category) { + switch (widget.config.categoryViewConfig.recentEmojisUpdatePolicy) { + case RecentEmojisStateUpdatePolicy.never: + return false; + case RecentEmojisStateUpdatePolicy.notFromRecent: + return category != Category.RECENT; + case RecentEmojisStateUpdatePolicy.always: + return true; + } + } + // Initialize emoji data Future _updateEmojis() async { _categoryEmoji.clear(); diff --git a/lib/src/emoji_picker_utils.dart b/lib/src/emoji_picker_utils.dart index 5bb27ef7..0f325183 100644 --- a/lib/src/emoji_picker_utils.dart +++ b/lib/src/emoji_picker_utils.dart @@ -176,7 +176,7 @@ class EmojiPickerUtils { {required GlobalKey key}) async { return await EmojiPickerInternalUtils() .clearRecentEmojisInLocalStorage() - .then((_) => key.currentState?.updateRecentEmoji([], refresh: true)); + .then((_) => key.currentState?.updateRecentEmoji([])); } /// Returns the emoji regex