fix flutter issues

This commit is contained in:
otsmr 2026-10-05 22:54:46 +02:00
parent ced68167d1
commit 76a9455c7c
30 changed files with 149 additions and 138 deletions

View file

@ -1,3 +1,5 @@
// ignore_for_file: constant_identifier_names
/* /*
* Modified version of https://github.com/Fintasys/emoji_picker_flutter * Modified version of https://github.com/Fintasys/emoji_picker_flutter
* MIT License * MIT License

View file

@ -4,8 +4,8 @@
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
/// Template class for custom implementation /// Template class for custom implementation
/// Inhert this class to create your own Category view /// Inhert this class to create your own Category view
@ -89,7 +89,7 @@ class DefaultCategoryView extends CategoryView {
class DefaultCategoryViewState extends CategoryViewState<DefaultCategoryView> { class DefaultCategoryViewState extends CategoryViewState<DefaultCategoryView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return ColoredBox(
color: widget.config.categoryViewConfig.backgroundColor, color: widget.config.categoryViewConfig.backgroundColor,
child: Row( child: Row(
children: [ children: [

View file

@ -1,11 +1,13 @@
// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes
/* /*
* Modified version of https://github.com/Fintasys/emoji_picker_flutter * Modified version of https://github.com/Fintasys/emoji_picker_flutter
* MIT License * MIT License
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
/// Callback function for custom category view /// Callback function for custom category view
typedef CategoryViewBuilder = typedef CategoryViewBuilder =
@ -77,7 +79,7 @@ class CategoryViewConfig {
final CategoryViewBuilder? customCategoryView; final CategoryViewBuilder? customCategoryView;
@override @override
bool operator ==(other) { bool operator ==(Object other) {
return (other is CategoryViewConfig) && return (other is CategoryViewConfig) &&
other.tabBarHeight == tabBarHeight && other.tabBarHeight == tabBarHeight &&
other.tabIndicatorAnimDuration == tabIndicatorAnimDuration && other.tabIndicatorAnimDuration == tabIndicatorAnimDuration &&

View file

@ -1,12 +1,14 @@
// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes, prefer_asserts_with_message
/* /*
* Modified version of https://github.com/Fintasys/emoji_picker_flutter * Modified version of https://github.com/Fintasys/emoji_picker_flutter
* MIT License * MIT License
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:flutter/material.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart'; import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:twonly/src/visual/components/emoji_picker/locales/default_emoji_set_locale.dart'; import 'package:twonly/src/visual/components/emoji_picker/locales/default_emoji_set_locale.dart';
import 'package:flutter/material.dart';
/// Number of skin tone icons /// Number of skin tone icons
const kSkinToneCount = 6; const kSkinToneCount = 6;
@ -89,7 +91,7 @@ class Config {
final ResizeConfig resizeConfig; final ResizeConfig resizeConfig;
@override @override
bool operator ==(other) { bool operator ==(Object other) {
return (other is Config) && return (other is Config) &&
other.height == height && other.height == height &&
other.viewOrderConfig == viewOrderConfig && other.viewOrderConfig == viewOrderConfig &&
@ -143,7 +145,7 @@ class ViewOrderConfig {
final EmojiPickerItem bottom; final EmojiPickerItem bottom;
@override @override
bool operator ==(other) { bool operator ==(Object other) {
return other is ViewOrderConfig && return other is ViewOrderConfig &&
other.top == top && other.top == top &&
other.middle == middle && other.middle == middle &&

View file

@ -4,8 +4,6 @@
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
// ignore_for_file: lines_longer_than_80_chars
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart'; import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
/// List of categories of emojis /// List of categories of emojis

View file

@ -1,3 +1,5 @@
// ignore_for_file: avoid_dynamic_calls, prefer_constructors_over_static_methods
/* /*
* Modified version of https://github.com/Fintasys/emoji_picker_flutter * Modified version of https://github.com/Fintasys/emoji_picker_flutter
* MIT License * MIT License
@ -39,9 +41,7 @@ class Emoji {
return Emoji( return Emoji(
json['emoji'] as String, json['emoji'] as String,
json['name'] as String, json['name'] as String,
hasSkinTone: json['hasSkinTone'] != null hasSkinTone: json['hasSkinTone'] != null && json['hasSkinTone'] as bool,
? json['hasSkinTone'] as bool
: false,
); );
} }

View file

@ -4,7 +4,6 @@
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
export 'locales/emoji_set.dart';
export 'bottom_action_bar.dart'; export 'bottom_action_bar.dart';
export 'category_view/category.dart'; export 'category_view/category.dart';
export 'category_view/category_view.dart'; export 'category_view/category_view.dart';
@ -19,6 +18,7 @@ export 'emoji_text_editing_controller.dart';
export 'emoji_view/default_emoji_picker_view.dart'; export 'emoji_view/default_emoji_picker_view.dart';
export 'emoji_view/emoji_picker_view.dart'; export 'emoji_view/emoji_picker_view.dart';
export 'emoji_view/emoji_view_config.dart'; export 'emoji_view/emoji_view_config.dart';
export 'locales/emoji_set.dart';
export 'search_view.dart'; export 'search_view.dart';
export 'skin_tones/emoji_skin_tones.dart'; export 'skin_tones/emoji_skin_tones.dart';
export 'skin_tones/skin_tone_config.dart'; export 'skin_tones/skin_tone_config.dart';

View file

@ -1,12 +1,14 @@
// ignore_for_file: comment_references
/* /*
* Modified version of https://github.com/Fintasys/emoji_picker_flutter * Modified version of https://github.com/Fintasys/emoji_picker_flutter
* MIT License * MIT License
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:flutter/foundation.dart' hide Category;
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart' import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart'
show Category; show Category;
import 'package:flutter/foundation.dart' hide Category;
/// Controller for EmojiPicker widget that allows reading and changing /// Controller for EmojiPicker widget that allows reading and changing
/// the selected category programmatically. /// the selected category programmatically.

View file

@ -9,10 +9,10 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/foundation.dart' show visibleForTesting;
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
/// Initial value for RecentEmoji /// Initial value for RecentEmoji
const initVal = 1; const initVal = 1;
@ -107,11 +107,11 @@ class EmojiPickerInternalUtils {
return Future(() async { return Future(() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
var emojiJson = prefs.getString('recent'); final emojiJson = prefs.getString('recent');
if (emojiJson == null) { if (emojiJson == null) {
return _recentEmojis = []; return _recentEmojis = [];
} }
var json = jsonDecode(emojiJson) as List<dynamic>; final json = jsonDecode(emojiJson) as List<dynamic>;
return _recentEmojis = json return _recentEmojis = json
.map<RecentEmoji>(RecentEmoji.fromJson) .map<RecentEmoji>(RecentEmoji.fromJson)
.toList(); .toList();
@ -128,7 +128,7 @@ class EmojiPickerInternalUtils {
emoji = removeSkinTone(emoji); emoji = removeSkinTone(emoji);
} }
var recentEmoji = await getRecentEmojis(); var recentEmoji = await getRecentEmojis();
var recentEmojiIndex = recentEmoji.indexWhere( final recentEmojiIndex = recentEmoji.indexWhere(
(element) => element.emoji.emoji == emoji.emoji, (element) => element.emoji.emoji == emoji.emoji,
); );
if (recentEmojiIndex != -1) { if (recentEmojiIndex != -1) {
@ -147,7 +147,7 @@ class EmojiPickerInternalUtils {
// save locally // save locally
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
prefs.setString('recent', jsonEncode(recentEmoji)); unawaited(prefs.setString('recent', jsonEncode(recentEmoji)));
return _recentEmojis = recentEmoji; return _recentEmojis = recentEmoji;
} }
@ -162,7 +162,7 @@ class EmojiPickerInternalUtils {
emoji = removeSkinTone(emoji); emoji = removeSkinTone(emoji);
} }
var recentEmoji = await getRecentEmojis(); var recentEmoji = await getRecentEmojis();
var recentEmojiIndex = recentEmoji.indexWhere( final recentEmojiIndex = recentEmoji.indexWhere(
(element) => element.emoji.emoji == emoji.emoji, (element) => element.emoji.emoji == emoji.emoji,
); );
if (recentEmojiIndex != -1) { if (recentEmojiIndex != -1) {
@ -188,7 +188,7 @@ class EmojiPickerInternalUtils {
// save locally // save locally
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
prefs.setString('recent', jsonEncode(recentEmoji)); unawaited(prefs.setString('recent', jsonEncode(recentEmoji)));
return _recentEmojis = recentEmoji; return _recentEmojis = recentEmoji;
} }
@ -196,7 +196,7 @@ class EmojiPickerInternalUtils {
/// Clears the list of recent emojis in local storage /// Clears the list of recent emojis in local storage
Future<void> clearRecentEmojisInLocalStorage() async { Future<void> clearRecentEmojisInLocalStorage() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
prefs.setString('recent', jsonEncode([])); unawaited(prefs.setString('recent', jsonEncode([])));
_recentEmojis = []; _recentEmojis = [];
} }

View file

@ -6,13 +6,14 @@
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart'; import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_internal_utils.dart'; import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_internal_utils.dart';
import 'package:flutter/material.dart';
/// Emoji Regex /// Emoji Regex
/// Keycap Sequence '((\u0023|\u002a|[\u0030-\u0039])\ufe0f\u20e3){1}' /// Keycap Sequence '((\u0023|\u002a|[\u0030-\u0039])\ufe0f\u20e3){1}'
/// Issue: https://github.com/flutter/flutter/issues/36062 /// Issue: https://github.com/flutter/flutter/issues/36062
// ignore: constant_identifier_names
const EmojiRegex = const EmojiRegex =
r'((\u0023|\u002a|[\u0030-\u0039])\ufe0f\u20e3){1}|\p{Emoji}|\u200D|\uFE0F'; r'((\u0023|\u002a|[\u0030-\u0039])\ufe0f\u20e3){1}|\p{Emoji}|\u200D|\uFE0F';
@ -56,7 +57,7 @@ class EmojiPickerUtils {
: data; : data;
// Set all the emoji entities // Set all the emoji entities
for (var emojis in availableCategoryEmoji) { for (final emojis in availableCategoryEmoji) {
_allAvailableEmojiEntities.addAll(emojis.emoji); _allAvailableEmojiEntities.addAll(emojis.emoji);
} }
} }
@ -186,7 +187,7 @@ class EmojiPickerUtils {
/// instead of an invalid double-modifier one (e.g. 👋🏻🏽). /// instead of an invalid double-modifier one (e.g. 👋🏻🏽).
Emoji applySkinTone(Emoji emoji, String color) { Emoji applySkinTone(Emoji emoji, String color) {
final codeUnits = removeSkinTone(emoji).emoji.codeUnits; final codeUnits = removeSkinTone(emoji).emoji.codeUnits;
var result = List<int>.empty(growable: true) final result = List<int>.empty(growable: true)
// Basic emoji without gender (until char 2) // Basic emoji without gender (until char 2)
..addAll(codeUnits.sublist(0, min(codeUnits.length, 2))) ..addAll(codeUnits.sublist(0, min(codeUnits.length, 2)))
// Skin tone // Skin tone
@ -246,9 +247,9 @@ class EmojiPickerUtils {
Future<void> clearRecentEmojis({ Future<void> clearRecentEmojis({
required GlobalKey<EmojiPickerState> key, required GlobalKey<EmojiPickerState> key,
}) async { }) async {
return await EmojiPickerInternalUtils() return EmojiPickerInternalUtils().clearRecentEmojisInLocalStorage().then(
.clearRecentEmojisInLocalStorage() (_) => key.currentState?.updateRecentEmoji([], refresh: true),
.then((_) => key.currentState?.updateRecentEmoji([], refresh: true)); );
} }
/// Returns the emoji regex /// Returns the emoji regex

View file

@ -1,3 +1,5 @@
// ignore_for_file: constant_identifier_names
/* /*
* Modified version of https://github.com/Fintasys/emoji_picker_flutter * Modified version of https://github.com/Fintasys/emoji_picker_flutter
* MIT License * MIT License
@ -6,17 +8,17 @@
import 'dart:io'; import 'dart:io';
import 'package:twonly/src/visual/components/emoji_picker/locales/default_emoji_set_locale.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/visual/components/emoji_picker/category_view/category.dart'; import 'package:twonly/src/visual/components/emoji_picker/category_view/category.dart';
import 'package:twonly/src/visual/components/emoji_picker/config.dart'; import 'package:twonly/src/visual/components/emoji_picker/config.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji.dart'; import 'package:twonly/src/visual/components/emoji_picker/emoji.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_controller.dart'; import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_controller.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_internal_utils.dart'; import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_internal_utils.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_view/default_emoji_picker_view.dart'; import 'package:twonly/src/visual/components/emoji_picker/emoji_view/default_emoji_picker_view.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_view/emoji_view_config.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_view/emoji_picker_view.dart'; import 'package:twonly/src/visual/components/emoji_picker/emoji_view/emoji_picker_view.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_view/emoji_view_config.dart';
import 'package:twonly/src/visual/components/emoji_picker/locales/default_emoji_set_locale.dart';
import 'package:twonly/src/visual/components/emoji_picker/search_view.dart'; import 'package:twonly/src/visual/components/emoji_picker/search_view.dart';
import 'package:flutter/material.dart';
/// All the possible categories that [Emoji] can be put into /// All the possible categories that [Emoji] can be put into
/// ///
@ -342,7 +344,7 @@ class EmojiPickerState extends State<EmojiPicker> {
if (cursorPosition >= 0) { if (cursorPosition >= 0) {
final selection = controller.value.selection; final selection = controller.value.selection;
final newTextBeforeCursor = _deleteWordByWord( final newTextBeforeCursor = _deleteWordByWord(
selection.textBefore(text).toString(), selection.textBefore(text),
); );
controller.value = controller.value.copyWith( controller.value = controller.value.copyWith(
text: newTextBeforeCursor + selection.textAfter(text), text: newTextBeforeCursor + selection.textAfter(text),
@ -558,8 +560,7 @@ class EmojiPickerState extends State<EmojiPicker> {
final maximumHeight = _maximumPickerHeight(initialHeight); final maximumHeight = _maximumPickerHeight(initialHeight);
final nextHeight = final nextHeight =
((_currentPickerHeight ?? initialHeight) - details.delta.dy) ((_currentPickerHeight ?? initialHeight) - details.delta.dy)
.clamp(initialHeight, maximumHeight) .clamp(initialHeight, maximumHeight);
.toDouble();
if (nextHeight != _currentPickerHeight) { if (nextHeight != _currentPickerHeight) {
setState(() => _currentPickerHeight = nextHeight); setState(() => _currentPickerHeight = nextHeight);
} }
@ -596,12 +597,10 @@ class EmojiPickerState extends State<EmojiPicker> {
final resizeConfig = widget.config.resizeConfig; final resizeConfig = widget.config.resizeConfig;
final configuredMaximum = final configuredMaximum =
resizeConfig.maxHeight ?? safeHeight * resizeConfig.maxHeightFactor; resizeConfig.maxHeight ?? safeHeight * resizeConfig.maxHeightFactor;
return configuredMaximum return configuredMaximum.clamp(
.clamp(
initialHeight, initialHeight,
safeHeight < initialHeight ? initialHeight : safeHeight, safeHeight < initialHeight ? initialHeight : safeHeight,
) );
.toDouble();
} }
void _showSearchView() { void _showSearchView() {

View file

@ -1,11 +1,13 @@
// ignore_for_file: constant_identifier_names, prefer_asserts_with_message
/* /*
* Modified version of https://github.com/Fintasys/emoji_picker_flutter * Modified version of https://github.com/Fintasys/emoji_picker_flutter
* MIT License * MIT License
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
/// Default delimiter for regex /// Default delimiter for regex
const delimiter = '|'; const delimiter = '|';
@ -14,7 +16,7 @@ const delimiter = '|';
/// a particular style to emoji characters. /// a particular style to emoji characters.
class EmojiTextEditingController extends TextEditingController { class EmojiTextEditingController extends TextEditingController {
/// Constructor, requres emojiStyle, since otherwise this class has no effect /// Constructor, requres emojiStyle, since otherwise this class has no effect
EmojiTextEditingController({super.text, required this.emojiTextStyle}); EmojiTextEditingController({required this.emojiTextStyle, super.text});
/// The style used for the emoji characters /// The style used for the emoji characters
final TextStyle emojiTextStyle; final TextStyle emojiTextStyle;
@ -25,8 +27,8 @@ class EmojiTextEditingController extends TextEditingController {
@override @override
TextSpan buildTextSpan({ TextSpan buildTextSpan({
required BuildContext context, required BuildContext context,
TextStyle? style,
required bool withComposing, required bool withComposing,
TextStyle? style,
}) { }) {
assert( assert(
!value.composing.isValid || !withComposing || value.isComposingRangeValid, !value.composing.isValid || !withComposing || value.isComposingRangeValid,
@ -83,7 +85,6 @@ class EmojiTextEditingController extends TextEditingController {
/// Emoji text style providing commonly available fallback fonts. /// Emoji text style providing commonly available fallback fonts.
const DefaultEmojiTextStyle = TextStyle( const DefaultEmojiTextStyle = TextStyle(
inherit: true,
fontFamilyFallback: [ fontFamilyFallback: [
'Apple Color Emoji', 'Apple Color Emoji',
'Noto Color Emoji', 'Noto Color Emoji',

View file

@ -4,8 +4,8 @@
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
/// Default EmojiPicker Implementation /// Default EmojiPicker Implementation
class DefaultEmojiPickerView extends EmojiPickerView { class DefaultEmojiPickerView extends EmojiPickerView {
@ -254,7 +254,6 @@ class _DefaultEmojiPickerViewState extends State<DefaultEmojiPickerView>
), ),
sliver: SliverGrid.builder( sliver: SliverGrid.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 1,
crossAxisCount: viewConfig.columns, crossAxisCount: viewConfig.columns,
mainAxisSpacing: viewConfig.verticalSpacing, mainAxisSpacing: viewConfig.verticalSpacing,
crossAxisSpacing: viewConfig.horizontalSpacing, crossAxisSpacing: viewConfig.horizontalSpacing,

View file

@ -32,11 +32,11 @@ abstract class EmojiPickerView extends StatefulWidget {
class EmojiContainer extends StatelessWidget { class EmojiContainer extends StatelessWidget {
/// Constructor. /// Constructor.
const EmojiContainer({ const EmojiContainer({
super.key,
required this.color, required this.color,
required this.buttonMode, required this.buttonMode,
this.padding,
required this.child, required this.child,
super.key,
this.padding,
}); });
final Color color; final Color color;

View file

@ -1,3 +1,5 @@
// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes, constant_identifier_names
/* /*
* Modified version of https://github.com/Fintasys/emoji_picker_flutter * Modified version of https://github.com/Fintasys/emoji_picker_flutter
* MIT License * MIT License
@ -6,8 +8,8 @@
import 'dart:math'; import 'dart:math';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
/// Callback function for custom view /// Callback function for custom view
typedef EmojiViewBuilder = typedef EmojiViewBuilder =
@ -40,6 +42,7 @@ class EmojiViewConfig {
this.noRecents = DefaultNoRecentsWidget, this.noRecents = DefaultNoRecentsWidget,
this.loadingIndicator = const SizedBox.shrink(), this.loadingIndicator = const SizedBox.shrink(),
this.buttonMode = ButtonMode.MATERIAL, this.buttonMode = ButtonMode.MATERIAL,
// ignore: prefer_asserts_with_message
}) : assert(categorySpacing >= 0); }) : assert(categorySpacing >= 0);
/// Number of emojis per row /// Number of emojis per row
@ -95,7 +98,7 @@ class EmojiViewConfig {
} }
@override @override
bool operator ==(other) { bool operator ==(Object other) {
return (other is EmojiViewConfig) && return (other is EmojiViewConfig) &&
other.columns == columns && other.columns == columns &&
other.emojiSizeMax == emojiSizeMax && other.emojiSizeMax == emojiSizeMax &&

View file

@ -4,8 +4,8 @@
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
/// Default method for locale selection /// Default method for locale selection
List<CategoryEmoji> getDefaultEmojiLocale(Locale locale) { List<CategoryEmoji> getDefaultEmojiLocale(Locale locale) {

View file

@ -1,12 +1,14 @@
// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes
/* /*
* Modified version of https://github.com/Fintasys/emoji_picker_flutter * Modified version of https://github.com/Fintasys/emoji_picker_flutter
* MIT License * MIT License
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:flutter/material.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart'; import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_internal_utils.dart'; import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_internal_utils.dart';
import 'package:flutter/material.dart';
/// Template class for custom implementation /// Template class for custom implementation
/// Inhert this class to create your own search view /// Inhert this class to create your own search view
@ -138,7 +140,7 @@ class SearchViewState<T extends SearchView> extends State<T>
emojiBoxPosition, emojiBoxPosition,
emoji, emoji,
emojiSize, emojiSize,
null, // Todo: check if we can provide the category null,
widget.config, widget.config,
_onSkinTonedEmojiSelected, _onSkinTonedEmojiSelected,
links[emoji.emoji]!, links[emoji.emoji]!,
@ -205,7 +207,7 @@ class SearchViewConfig {
final SearchViewBuilder? customSearchView; final SearchViewBuilder? customSearchView;
@override @override
bool operator ==(other) { bool operator ==(Object other) {
return other is SearchViewConfig && return other is SearchViewConfig &&
other.backgroundColor == backgroundColor && other.backgroundColor == backgroundColor &&
other.buttonIconColor == buttonIconColor && other.buttonIconColor == buttonIconColor &&
@ -251,7 +253,7 @@ class DefaultSearchViewState extends SearchViewState<DefaultSearchView> {
constraints.maxWidth, constraints.maxWidth,
); );
return Container( return ColoredBox(
color: widget.config.searchViewConfig.backgroundColor, color: widget.config.searchViewConfig.backgroundColor,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,

View file

@ -24,5 +24,11 @@ class SkinTone {
static const String dark = '🏿'; static const String dark = '🏿';
/// Return all values as Array /// Return all values as Array
static const values = [light, mediumLight, medium, mediumDark, dark]; static const List<String> values = [
light,
mediumLight,
medium,
mediumDark,
dark,
];
} }

View file

@ -1,3 +1,5 @@
// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes
/* /*
* Modified version of https://github.com/Fintasys/emoji_picker_flutter * Modified version of https://github.com/Fintasys/emoji_picker_flutter
* MIT License * MIT License
@ -37,7 +39,7 @@ class SkinToneConfig {
final bool rememberSkinTone; final bool rememberSkinTone;
@override @override
bool operator ==(other) { bool operator ==(Object other) {
return (other is SkinToneConfig) && return (other is SkinToneConfig) &&
other.enabled == enabled && other.enabled == enabled &&
other.dialogBackgroundColor == dialogBackgroundColor && other.dialogBackgroundColor == dialogBackgroundColor &&

View file

@ -6,8 +6,8 @@
import 'dart:collection'; import 'dart:collection';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
/// Skin tone overlay mixin /// Skin tone overlay mixin
mixin SkinToneOverlayStateMixin<T extends StatefulWidget> on State<T> { mixin SkinToneOverlayStateMixin<T extends StatefulWidget> on State<T> {
@ -52,7 +52,7 @@ mixin SkinToneOverlayStateMixin<T extends StatefulWidget> on State<T> {
.toList(); .toList();
final screenWidth = MediaQuery.of(context).size.width; final screenWidth = MediaQuery.of(context).size.width;
final emojiPickerRenderbox = context.findRenderObject() as RenderBox; final emojiPickerRenderbox = context.findRenderObject()! as RenderBox;
final emojiBoxSize = config.emojiViewConfig.getEmojiBoxSize( final emojiBoxSize = config.emojiViewConfig.getEmojiBoxSize(
emojiPickerRenderbox.size.width, emojiPickerRenderbox.size.width,
); );
@ -74,9 +74,9 @@ mixin SkinToneOverlayStateMixin<T extends StatefulWidget> on State<T> {
child: TapRegion( child: TapRegion(
onTapOutside: (_) => closeSkinToneOverlay(), onTapOutside: (_) => closeSkinToneOverlay(),
child: Material( child: Material(
elevation: 4.0, elevation: 4,
child: Container( child: Container(
padding: const EdgeInsets.symmetric(vertical: 4.0), padding: const EdgeInsets.symmetric(vertical: 4),
color: config.skinToneConfig.dialogBackgroundColor, color: config.skinToneConfig.dialogBackgroundColor,
child: Row( child: Row(
children: [ children: [
@ -120,7 +120,7 @@ mixin SkinToneOverlayStateMixin<T extends StatefulWidget> on State<T> {
} }
double _calculateTopOffset(double emojiBoxSize) { double _calculateTopOffset(double emojiBoxSize) {
final verticalPaddingOverlay = 8.0; const verticalPaddingOverlay = 8.0;
final top = -emojiBoxSize - verticalPaddingOverlay; final top = -emojiBoxSize - verticalPaddingOverlay;
return top; return top;
} }

View file

@ -43,7 +43,7 @@ class _TriangleShapePainter extends BoxPainter {
// per documentation, the size should be always not null here, no need // per documentation, the size should be always not null here, no need
// for null checks // for null checks
final s = configuration.size!; final s = configuration.size!;
var path = Path() final path = Path()
..moveTo(s.width + offset.dx, s.height - _size + offset.dy) ..moveTo(s.width + offset.dx, s.height - _size + offset.dy)
..lineTo(s.width - _size + offset.dx, s.height + offset.dy) ..lineTo(s.width - _size + offset.dx, s.height + offset.dy)
..lineTo(s.width + offset.dx, s.height + offset.dy) ..lineTo(s.width + offset.dx, s.height + offset.dy)

View file

@ -6,8 +6,8 @@
import 'dart:async'; import 'dart:async';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
/// Backspace Button Widget /// Backspace Button Widget
class BackspaceButton extends StatefulWidget { class BackspaceButton extends StatefulWidget {
@ -72,7 +72,7 @@ class _BackspaceButtonState extends State<BackspaceButton> {
var millisecondsSincePressed = 0; var millisecondsSincePressed = 0;
// Callback function executed on each timer tick // Callback function executed on each timer tick
void _callback(Timer timer) { void callback(Timer timer) {
// Accumulate elapsed time since the last tick // Accumulate elapsed time since the last tick
millisecondsSincePressed += callbackInterval.inMilliseconds; millisecondsSincePressed += callbackInterval.inMilliseconds;
@ -87,7 +87,7 @@ class _BackspaceButtonState extends State<BackspaceButton> {
_onBackspacePressedCallbackTimer?.cancel(); _onBackspacePressedCallbackTimer?.cancel();
_onBackspacePressedCallbackTimer = Timer.periodic( _onBackspacePressedCallbackTimer = Timer.periodic(
callbackInterval, callbackInterval,
_callback, callback,
); );
// Reset the elapsed time for the new interval // Reset the elapsed time for the new interval
@ -105,7 +105,7 @@ class _BackspaceButtonState extends State<BackspaceButton> {
// Start the initial timer with the short-press interval // Start the initial timer with the short-press interval
_onBackspacePressedCallbackTimer = Timer.periodic( _onBackspacePressedCallbackTimer = Timer.periodic(
callbackInterval, callbackInterval,
_callback, callback,
); );
} }

View file

@ -4,9 +4,9 @@
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
/// A widget that represents an individual clickable emoji cell. /// A widget that represents an individual clickable emoji cell.
/// Can have a long pressed listener [onSkinToneDialogRequested] that /// Can have a long pressed listener [onSkinToneDialogRequested] that
@ -14,30 +14,30 @@ import 'package:flutter/material.dart';
class EmojiCell extends StatelessWidget { class EmojiCell extends StatelessWidget {
/// Constructor for manually setting all properties /// Constructor for manually setting all properties
const EmojiCell({ const EmojiCell({
super.key,
required this.emoji, required this.emoji,
required this.emojiSize, required this.emojiSize,
required this.emojiBoxSize, required this.emojiBoxSize,
this.categoryEmoji,
required this.buttonMode, required this.buttonMode,
required this.enableSkinTones, required this.enableSkinTones,
required this.textStyle, required this.textStyle,
required this.skinToneIndicatorColor, required this.skinToneIndicatorColor,
this.onSkinToneDialogRequested,
required this.onEmojiSelected, required this.onEmojiSelected,
super.key,
this.categoryEmoji,
this.onSkinToneDialogRequested,
}); });
/// Constructor that can retrieve as much information as possible from /// Constructor that can retrieve as much information as possible from
/// [Config] /// [Config]
EmojiCell.fromConfig({ EmojiCell.fromConfig({
super.key,
required this.emoji, required this.emoji,
required this.emojiSize, required this.emojiSize,
required this.emojiBoxSize, required this.emojiBoxSize,
this.categoryEmoji,
required this.onEmojiSelected, required this.onEmojiSelected,
this.onSkinToneDialogRequested,
required Config config, required Config config,
super.key,
this.categoryEmoji,
this.onSkinToneDialogRequested,
}) : buttonMode = config.emojiViewConfig.buttonMode, }) : buttonMode = config.emojiViewConfig.buttonMode,
enableSkinTones = config.skinToneConfig.enabled, enableSkinTones = config.skinToneConfig.enabled,
textStyle = config.emojiTextStyle, textStyle = config.emojiTextStyle,
@ -77,12 +77,12 @@ class EmojiCell extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
onPressed() { void onPressed() {
onEmojiSelected(categoryEmoji?.category, emoji); onEmojiSelected(categoryEmoji?.category, emoji);
} }
onLongPressed() { void onLongPressed() {
final renderBox = context.findRenderObject() as RenderBox; final renderBox = context.findRenderObject()! as RenderBox;
final emojiBoxPosition = renderBox.localToGlobal(Offset.zero); final emojiBoxPosition = renderBox.localToGlobal(Offset.zero);
onSkinToneDialogRequested?.call( onSkinToneDialogRequested?.call(
emojiBoxPosition, emojiBoxPosition,
@ -106,8 +106,8 @@ class EmojiCell extends StatelessWidget {
/// Build different Button based on ButtonMode /// Build different Button based on ButtonMode
Widget _buildButtonWidget({ Widget _buildButtonWidget({
required VoidCallback onPressed, required VoidCallback onPressed,
VoidCallback? onLongPressed,
required Widget child, required Widget child,
VoidCallback? onLongPressed,
}) { }) {
if (buttonMode == ButtonMode.MATERIAL) { if (buttonMode == ButtonMode.MATERIAL) {
return MaterialButton( return MaterialButton(
@ -116,7 +116,7 @@ class EmojiCell extends StatelessWidget {
elevation: 0, elevation: 0,
highlightElevation: 0, highlightElevation: 0,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero), shape: const RoundedRectangleBorder(),
child: child, child: child,
); );
} }
@ -126,7 +126,6 @@ class EmojiCell extends StatelessWidget {
child: CupertinoButton( child: CupertinoButton(
onPressed: onPressed, onPressed: onPressed,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
alignment: Alignment.center,
child: child, child: child,
), ),
); );
@ -142,7 +141,7 @@ class EmojiCell extends StatelessWidget {
Widget _buildEmoji() { Widget _buildEmoji() {
final emojiText = Text( final emojiText = Text(
emoji.emoji, emoji.emoji,
textScaler: const TextScaler.linear(1.0), textScaler: TextScaler.noScaling,
style: _getEmojiTextStyle(), style: _getEmojiTextStyle(),
); );
@ -152,7 +151,7 @@ class EmojiCell extends StatelessWidget {
? Container( ? Container(
decoration: TriangleDecoration( decoration: TriangleDecoration(
color: skinToneIndicatorColor, color: skinToneIndicatorColor,
size: 8.0, size: 8,
), ),
child: emojiText, child: emojiText,
) )

View file

@ -143,7 +143,6 @@ class _StartNewChatView extends State<StartNewChatView> {
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
bottom: 40, bottom: 40,
left: 10, left: 10,
top: 0,
right: 10, right: 10,
), ),
child: Column( child: Column(

View file

@ -11,6 +11,7 @@ use crate::error::{Result, TwonlyError};
pub(crate) struct GroupRecord { pub(crate) struct GroupRecord {
pub group_id: String, pub group_id: String,
pub group_name: String, pub group_name: String,
#[allow(dead_code)]
pub state_version_id: i64, pub state_version_id: i64,
pub state_encryption_key: Option<Vec<u8>>, pub state_encryption_key: Option<Vec<u8>>,
pub my_group_private_key: Option<Vec<u8>>, pub my_group_private_key: Option<Vec<u8>>,

View file

@ -4,9 +4,9 @@
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:flutter_test/flutter_test.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart'; import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_internal_utils.dart'; import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_internal_utils.dart';
import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
skinToneTests(); skinToneTests();
@ -100,7 +100,7 @@ void skinToneTests() {
utils utils
.applyDisplaySkinTone( .applyDisplaySkinTone(
toneable, toneable,
const SkinToneConfig(rememberSkinTone: false), const SkinToneConfig(),
SkinTone.medium, SkinTone.medium,
) )
.emoji, .emoji,
@ -123,7 +123,7 @@ void skinToneTests() {
void emojiModelTests() { void emojiModelTests() {
test('encode Emoji', () { test('encode Emoji', () {
final encode = const Emoji('🤣', 'name'); const encode = Emoji('🤣', 'name');
expect(encode.toJson(), <String, dynamic>{ expect(encode.toJson(), <String, dynamic>{
'emoji': '🤣', 'emoji': '🤣',
'name': 'name', 'name': 'name',

View file

@ -4,10 +4,10 @@
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_internal_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_internal_utils.dart';
// Use for golden tests, helpful in debugging // Use for golden tests, helpful in debugging
// await expectLater( // await expectLater(
@ -21,24 +21,23 @@ void main() {
setUp(EmojiPickerInternalUtils.resetCaches); setUp(EmojiPickerInternalUtils.resetCaches);
testWidgets('Should allow user to select an emoji', ( testWidgets('Should allow user to select an emoji', (
WidgetTester tester, tester,
) async { ) async {
final _controller = TextEditingController(); final controller0 = TextEditingController();
Emoji? _emojiSelected; Emoji? emojiSelected;
Category? _categorySelected; Category? categorySelected;
// Build our app and trigger a frame. // Build our app and trigger a frame.
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Scaffold( home: Scaffold(
body: EmojiPicker( body: EmojiPicker(
textEditingController: _controller, textEditingController: controller0,
onEmojiSelected: (category, emoji) { onEmojiSelected: (category, emoji) {
_emojiSelected = emoji; emojiSelected = emoji;
_categorySelected = category; categorySelected = category;
}, },
config: const Config( config: const Config(
height: 256,
categoryViewConfig: CategoryViewConfig( categoryViewConfig: CategoryViewConfig(
recentTabBehavior: RecentTabBehavior.NONE, recentTabBehavior: RecentTabBehavior.NONE,
), ),
@ -64,20 +63,20 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Check if the emoji is added to the text controller // Check if the emoji is added to the text controller
expect(_controller.text, contains('🙂')); expect(controller0.text, contains('🙂'));
// Check if the emoji been passed to the 'onEmojiSelected' callback // Check if the emoji been passed to the 'onEmojiSelected' callback
expect( expect(
_emojiSelected, emojiSelected,
equals(const Emoji('🙂', 'face | happy | slightly | smile | smiling')), equals(const Emoji('🙂', 'face | happy | slightly | smile | smiling')),
); );
// Check if the category been passed to the 'onEmojiSelected' callback // Check if the category been passed to the 'onEmojiSelected' callback
expect(_categorySelected, equals(Category.SMILEYS)); expect(categorySelected, equals(Category.SMILEYS));
}); });
testWidgets('Shows the redesigned header and one continuous emoji list', ( testWidgets('Shows the redesigned header and one continuous emoji list', (
WidgetTester tester, tester,
) async { ) async {
var stickerPressed = false; var stickerPressed = false;
final controller = EmojiPickerController( final controller = EmojiPickerController(
@ -137,7 +136,7 @@ void main() {
}); });
testWidgets('Drag handle grows picker from its configured height', ( testWidgets('Drag handle grows picker from its configured height', (
WidgetTester tester, tester,
) async { ) async {
var showing = true; var showing = true;
late StateSetter setHarnessState; late StateSetter setHarnessState;
@ -158,7 +157,6 @@ void main() {
child: EmojiPicker( child: EmojiPicker(
controller: pickerController, controller: pickerController,
config: const Config( config: const Config(
height: 256,
resizeConfig: ResizeConfig(maxHeight: 420), resizeConfig: ResizeConfig(maxHeight: 420),
emojiViewConfig: EmojiViewConfig( emojiViewConfig: EmojiViewConfig(
backgroundColor: pickerColor, backgroundColor: pickerColor,
@ -192,7 +190,7 @@ void main() {
final dragIndicator = tester.widget<Container>( final dragIndicator = tester.widget<Container>(
find.byKey(const Key('emojiPickerDragIndicator')), find.byKey(const Key('emojiPickerDragIndicator')),
); );
final dragDecoration = dragIndicator.decoration as BoxDecoration; final dragDecoration = dragIndicator.decoration! as BoxDecoration;
expect(dragDecoration.color, ThemeData.dark().colorScheme.outline); expect(dragDecoration.color, ThemeData.dark().colorScheme.outline);
expect(dragDecoration.borderRadius, BorderRadius.circular(32)); expect(dragDecoration.borderRadius, BorderRadius.circular(32));
@ -212,13 +210,12 @@ void main() {
expect(tester.getSize(picker).height, 256); expect(tester.getSize(picker).height, 256);
}); });
testWidgets('Drag handle can be hidden', (WidgetTester tester) async { testWidgets('Drag handle can be hidden', (tester) async {
await tester.pumpWidget( await tester.pumpWidget(
const MaterialApp( const MaterialApp(
home: Scaffold( home: Scaffold(
body: EmojiPicker( body: EmojiPicker(
config: Config( config: Config(
height: 256,
resizeConfig: ResizeConfig(showDragHandle: false), resizeConfig: ResizeConfig(showDragHandle: false),
categoryViewConfig: CategoryViewConfig( categoryViewConfig: CategoryViewConfig(
recentTabBehavior: RecentTabBehavior.NONE, recentTabBehavior: RecentTabBehavior.NONE,
@ -240,25 +237,25 @@ void main() {
}); });
testWidgets('Should allow to select an emoji with skintone on longPress', ( testWidgets('Should allow to select an emoji with skintone on longPress', (
WidgetTester tester, tester,
) async { ) async {
final _controller = TextEditingController(); final controller0 = TextEditingController();
final _utils = EmojiPickerUtils(); final utils = EmojiPickerUtils();
final emoji = const Emoji('👍', 'Thumbs Up', hasSkinTone: true); const emoji = Emoji('👍', 'Thumbs Up', hasSkinTone: true);
Emoji? _emojiSelected; Emoji? emojiSelected;
Category? _categorySelected; Category? categorySelected;
// Build our app and trigger a frame. // Build our app and trigger a frame.
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Scaffold( home: Scaffold(
body: Padding( body: Padding(
padding: const EdgeInsets.only(top: 64.0), padding: const EdgeInsets.only(top: 64),
child: EmojiPicker( child: EmojiPicker(
textEditingController: _controller, textEditingController: controller0,
onEmojiSelected: (category, emoji) { onEmojiSelected: (category, emoji) {
_emojiSelected = emoji; emojiSelected = emoji;
_categorySelected = category; categorySelected = category;
}, },
config: const Config( config: const Config(
height: 500, height: 500,
@ -298,7 +295,7 @@ void main() {
Finder? skinToneVariantToFind; Finder? skinToneVariantToFind;
for (var i = 0; i < SkinTone.values.length; i++) { for (var i = 0; i < SkinTone.values.length; i++) {
skinToneVariantToFind = find.text( skinToneVariantToFind = find.text(
_utils.applySkinTone(emoji, SkinTone.values[i]).emoji, utils.applySkinTone(emoji, SkinTone.values[i]).emoji,
); );
// Verify if we can find the skintone variant // Verify if we can find the skintone variant
expect(skinToneVariantToFind, findsOneWidget); expect(skinToneVariantToFind, findsOneWidget);
@ -308,24 +305,24 @@ void main() {
await tester.tap(skinToneVariantToFind!); await tester.tap(skinToneVariantToFind!);
// Check if the emoji is added to the text controller // Check if the emoji is added to the text controller
expect(_controller.text, contains('👍🏿')); expect(controller0.text, contains('👍🏿'));
// Check if the emoji been passed to the 'onEmojiSelected' callback // Check if the emoji been passed to the 'onEmojiSelected' callback
expect(_emojiSelected?.emoji, equals('👍🏿')); expect(emojiSelected?.emoji, equals('👍🏿'));
expect( expect(
_emojiSelected?.name, emojiSelected?.name,
equals('+1 | good | hand | like | thumb | up | yes'), equals('+1 | good | hand | like | thumb | up | yes'),
); );
expect(_emojiSelected?.hasSkinTone, equals(true)); expect(emojiSelected?.hasSkinTone, equals(true));
// Check if the category been passed to the 'onEmojiSelected' callback // Check if the category been passed to the 'onEmojiSelected' callback
expect(_categorySelected, equals(Category.SMILEYS)); expect(categorySelected, equals(Category.SMILEYS));
}); });
testWidgets('Clips overflow when constrained tighter than natural height', ( testWidgets('Clips overflow when constrained tighter than natural height', (
WidgetTester tester, tester,
) async { ) async {
final _controller = TextEditingController(); final controller0 = TextEditingController();
// Constrain the picker far below the natural sum of the category bar // Constrain the picker far below the natural sum of the category bar
// and bottom action bar to force the inner Column to overflow (#256). // and bottom action bar to force the inner Column to overflow (#256).
@ -337,7 +334,7 @@ void main() {
width: 300, width: 300,
height: 30, height: 30,
child: EmojiPicker( child: EmojiPicker(
textEditingController: _controller, textEditingController: controller0,
config: const Config( config: const Config(
height: 30, height: 30,
categoryViewConfig: CategoryViewConfig( categoryViewConfig: CategoryViewConfig(

View file

@ -4,18 +4,18 @@
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
void main() { void main() {
group('EmojiTextEditingController', () { group('EmojiTextEditingController', () {
testWidgets('should apply emojiTextStyle to emojis', (tester) async { testWidgets('should apply emojiTextStyle to emojis', (tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Builder( Builder(
builder: (BuildContext context) { builder: (context) {
final emojiStyle = const TextStyle(color: Colors.red); const emojiStyle = TextStyle(color: Colors.red);
final regularStyle = const TextStyle(color: Colors.black); const regularStyle = TextStyle(color: Colors.black);
final controller = EmojiTextEditingController( final controller = EmojiTextEditingController(
text: 'Hello 👋 World', text: 'Hello 👋 World',
emojiTextStyle: emojiStyle, emojiTextStyle: emojiStyle,

View file

@ -4,17 +4,17 @@
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
void main() { void main() {
group('EmojiTextStyle', () { group('EmojiTextStyle', () {
testWidgets('should apply EmojiTextStyle to emoji in text', (tester) async { testWidgets('should apply EmojiTextStyle to emoji in text', (tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Builder( Builder(
builder: (BuildContext context) { builder: (context) {
final text = 'Hello 👋 World'; const text = 'Hello 👋 World';
final result = EmojiPickerUtils().setEmojiTextStyle( final result = EmojiPickerUtils().setEmojiTextStyle(
text, text,
emojiStyle: const TextStyle(color: Colors.red), emojiStyle: const TextStyle(color: Colors.red),

View file

@ -4,19 +4,19 @@
* Copyright (c) 2024 Stefan Humm * Copyright (c) 2024 Stefan Humm
*/ */
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_internal_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker.dart';
import 'package:twonly/src/visual/components/emoji_picker/emoji_picker_internal_utils.dart';
/// Test harness that lets us trigger a rebuild of [EmojiPicker] from its /// Test harness that lets us trigger a rebuild of [EmojiPicker] from its
/// parent, mimicking an unrelated `setState()` higher up in the widget tree. /// parent, mimicking an unrelated `setState()` higher up in the widget tree.
class _RebuildHarness extends StatefulWidget { class _RebuildHarness extends StatefulWidget {
const _RebuildHarness({ const _RebuildHarness({
super.key,
required this.pickerKey, required this.pickerKey,
required this.config, required this.config,
super.key,
}); });
final GlobalKey<EmojiPickerState> pickerKey; final GlobalKey<EmojiPickerState> pickerKey;
@ -59,10 +59,6 @@ void main() {
const config = Config( const config = Config(
height: 400, height: 400,
categoryViewConfig: CategoryViewConfig(
initCategory: Category.RECENT,
recentTabBehavior: RecentTabBehavior.RECENT,
),
); );
// Deliberately not part of the built-in set. In the continuous picker a // Deliberately not part of the built-in set. In the continuous picker a