initial commit
This commit is contained in:
commit
f053f85d95
23 changed files with 25934 additions and 0 deletions
3
lib/material_emoji_picker.dart
Normal file
3
lib/material_emoji_picker.dart
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export "src/models/emoji.dart";
|
||||
export "src/models/emoji_category.dart";
|
||||
export "src/widgets/emoji_picker.dart";
|
||||
41
lib/src/controllers/emoji.dart
Normal file
41
lib/src/controllers/emoji.dart
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import "dart:convert";
|
||||
|
||||
import "package:collection/collection.dart";
|
||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:flutter/services.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:material_emoji_picker/src/models/emoji.dart";
|
||||
import "package:material_emoji_picker/src/models/emoji_category.dart";
|
||||
|
||||
class EmojiController extends AsyncNotifier<IList<EmojiCategory>> {
|
||||
@override
|
||||
Future<IList<EmojiCategory>> build() async {
|
||||
final emojiJson = await rootBundle.loadString(
|
||||
"packages/material_emoji_picker/assets/emoji.json",
|
||||
);
|
||||
final decoded = (jsonDecode(emojiJson) as List<dynamic>)
|
||||
.cast<Map<String, Object?>>();
|
||||
|
||||
final entries = decoded.map(
|
||||
(json) =>
|
||||
(category: json["category"]! as String, emoji: Emoji.fromJson(json)),
|
||||
);
|
||||
|
||||
final grouped = groupBy(entries, (e) => e.category);
|
||||
|
||||
return .new(
|
||||
grouped.entries.map(
|
||||
(entry) => .new(
|
||||
icon: entry.value.first.emoji.widget,
|
||||
name: entry.key,
|
||||
emojis: .new(entry.value.map((e) => e.emoji)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static final provider =
|
||||
AsyncNotifierProvider<EmojiController, IList<EmojiCategory>>(
|
||||
EmojiController.new,
|
||||
);
|
||||
}
|
||||
25
lib/src/models/emoji.dart
Normal file
25
lib/src/models/emoji.dart
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:freezed_annotation/freezed_annotation.dart";
|
||||
import "package:flutter/widgets.dart";
|
||||
part "emoji.freezed.dart";
|
||||
part "emoji.g.dart";
|
||||
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
@JsonSerializable(createToJson: false)
|
||||
class Emoji({
|
||||
@JsonKey(
|
||||
fromJson: Emoji.widgetFromJson,
|
||||
readValue: Emoji.readWidgetValueFromJson,
|
||||
)
|
||||
required final Widget widget,
|
||||
@JsonKey(name: "emoji") required final String value,
|
||||
required final IList<String> aliases,
|
||||
required final String description,
|
||||
required final IList<String> tags,
|
||||
}) with _$Emoji {
|
||||
static Widget widgetFromJson(String emoji) => Text(emoji);
|
||||
|
||||
static String readWidgetValueFromJson(Map<dynamic, dynamic> json, _) =>
|
||||
json["emoji"];
|
||||
factory Emoji.fromJson(Map<String, Object?> json) => _$EmojiFromJson(json);
|
||||
}
|
||||
291
lib/src/models/emoji.freezed.dart
Normal file
291
lib/src/models/emoji.freezed.dart
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint, type=warning, deprecated_member_use, deprecated_member_use_from_same_package
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'emoji.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$Emoji {
|
||||
|
||||
|
||||
/// Create a copy of Emoji
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$EmojiCopyWith<Emoji> get copyWith => _$EmojiCopyWithImpl<Emoji>(this as Emoji, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
final _this = this as Emoji;
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is Emoji&&(identical(other.widget, _this.widget) || other.widget == _this.widget)&&(identical(other.value, _this.value) || other.value == _this.value)&&const DeepCollectionEquality().equals(other.aliases, _this.aliases)&&(identical(other.description, _this.description) || other.description == _this.description)&&const DeepCollectionEquality().equals(other.tags, _this.tags));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
final _this = this as Emoji;
|
||||
return Object.hash(runtimeType,_this.widget,_this.value,const DeepCollectionEquality().hash(_this.aliases),_this.description,const DeepCollectionEquality().hash(_this.tags));
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final _this = this as Emoji;
|
||||
return 'Emoji(widget: ${_this.widget}, value: ${_this.value}, aliases: ${_this.aliases}, description: ${_this.description}, tags: ${_this.tags})';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $EmojiCopyWith<$Res> {
|
||||
factory $EmojiCopyWith(Emoji value, $Res Function(Emoji) _then) = _$EmojiCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
@JsonKey(fromJson: Emoji.widgetFromJson, readValue: Emoji.readWidgetValueFromJson) Widget widget,@JsonKey(name: "emoji") String value, IList<String> aliases, String description, IList<String> tags
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$EmojiCopyWithImpl<$Res>
|
||||
implements $EmojiCopyWith<$Res> {
|
||||
_$EmojiCopyWithImpl(this._self, this._then);
|
||||
|
||||
final Emoji _self;
|
||||
final $Res Function(Emoji) _then;
|
||||
|
||||
/// Create a copy of Emoji
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? widget = null,Object? value = null,Object? aliases = null,Object? description = null,Object? tags = null,}) {
|
||||
return _then(Emoji(
|
||||
widget: null == widget ? _self.widget : widget // ignore: cast_nullable_to_non_nullable
|
||||
as Widget,value: null == value ? _self.value : value // ignore: cast_nullable_to_non_nullable
|
||||
as String,aliases: null == aliases ? _self.aliases : aliases // ignore: cast_nullable_to_non_nullable
|
||||
as IList<String>,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||
as String,tags: null == tags ? _self.tags : tags // ignore: cast_nullable_to_non_nullable
|
||||
as IList<String>,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [Emoji].
|
||||
extension EmojiPatterns on Emoji {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Emoji value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Emoji() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Emoji value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Emoji():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Emoji value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Emoji() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(fromJson: Emoji.widgetFromJson, readValue: Emoji.readWidgetValueFromJson) Widget widget, @JsonKey(name: "emoji") String value, IList<String> aliases, String description, IList<String> tags)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Emoji() when $default != null:
|
||||
return $default(_that.widget,_that.value,_that.aliases,_that.description,_that.tags);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(fromJson: Emoji.widgetFromJson, readValue: Emoji.readWidgetValueFromJson) Widget widget, @JsonKey(name: "emoji") String value, IList<String> aliases, String description, IList<String> tags) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Emoji():
|
||||
return $default(_that.widget,_that.value,_that.aliases,_that.description,_that.tags);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(fromJson: Emoji.widgetFromJson, readValue: Emoji.readWidgetValueFromJson) Widget widget, @JsonKey(name: "emoji") String value, IList<String> aliases, String description, IList<String> tags)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Emoji() when $default != null:
|
||||
return $default(_that.widget,_that.value,_that.aliases,_that.description,_that.tags);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _Emoji extends Emoji {
|
||||
_Emoji({@JsonKey(fromJson: Emoji.widgetFromJson, readValue: Emoji.readWidgetValueFromJson) required this.widget, @JsonKey(name: "emoji") required this.value, required this.aliases, required this.description, required this.tags}): super(widget: widget, value: value, aliases: aliases, description: description, tags: tags);
|
||||
|
||||
|
||||
@override@JsonKey(fromJson: Emoji.widgetFromJson, readValue: Emoji.readWidgetValueFromJson) final Widget widget;
|
||||
@override@JsonKey(name: "emoji") final String value;
|
||||
@override final IList<String> aliases;
|
||||
@override final String description;
|
||||
@override final IList<String> tags;
|
||||
|
||||
/// Create a copy of Emoji
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$EmojiCopyWith<_Emoji> get copyWith => __$EmojiCopyWithImpl<_Emoji>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Emoji&&(identical(other.widget, widget) || other.widget == widget)&&(identical(other.value, value) || other.value == value)&&const DeepCollectionEquality().equals(other.aliases, aliases)&&(identical(other.description, description) || other.description == description)&&const DeepCollectionEquality().equals(other.tags, tags));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(runtimeType,widget,value,const DeepCollectionEquality().hash(aliases),description,const DeepCollectionEquality().hash(tags));
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Emoji(widget: $widget, value: $value, aliases: $aliases, description: $description, tags: $tags)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$EmojiCopyWith<$Res> implements $EmojiCopyWith<$Res> {
|
||||
factory _$EmojiCopyWith(_Emoji value, $Res Function(_Emoji) _then) = __$EmojiCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
@JsonKey(fromJson: Emoji.widgetFromJson, readValue: Emoji.readWidgetValueFromJson) Widget widget,@JsonKey(name: "emoji") String value, IList<String> aliases, String description, IList<String> tags
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$EmojiCopyWithImpl<$Res>
|
||||
implements _$EmojiCopyWith<$Res> {
|
||||
__$EmojiCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _Emoji _self;
|
||||
final $Res Function(_Emoji) _then;
|
||||
|
||||
/// Create a copy of Emoji
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? widget = null,Object? value = null,Object? aliases = null,Object? description = null,Object? tags = null,}) {
|
||||
return _then(_Emoji(
|
||||
widget: null == widget ? _self.widget : widget // ignore: cast_nullable_to_non_nullable
|
||||
as Widget,value: null == value ? _self.value : value // ignore: cast_nullable_to_non_nullable
|
||||
as String,aliases: null == aliases ? _self.aliases : aliases // ignore: cast_nullable_to_non_nullable
|
||||
as IList<String>,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||
as String,tags: null == tags ? _self.tags : tags // ignore: cast_nullable_to_non_nullable
|
||||
as IList<String>,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
17
lib/src/models/emoji.g.dart
Normal file
17
lib/src/models/emoji.g.dart
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'emoji.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
Emoji _$EmojiFromJson(Map<String, dynamic> json) => Emoji(
|
||||
widget: Emoji.widgetFromJson(
|
||||
Emoji.readWidgetValueFromJson(json, 'widget') as String,
|
||||
),
|
||||
value: json['emoji'] as String,
|
||||
aliases: IList<String>.fromJson(json['aliases'], (value) => value as String),
|
||||
description: json['description'] as String,
|
||||
tags: IList<String>.fromJson(json['tags'], (value) => value as String),
|
||||
);
|
||||
12
lib/src/models/emoji_category.dart
Normal file
12
lib/src/models/emoji_category.dart
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:freezed_annotation/freezed_annotation.dart";
|
||||
import "package:material_emoji_picker/src/models/emoji.dart";
|
||||
import "package:flutter/widgets.dart";
|
||||
part "emoji_category.freezed.dart";
|
||||
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
class EmojiCategory({
|
||||
required final Widget icon,
|
||||
required final String name,
|
||||
required final IList<Emoji> emojis,
|
||||
}) with _$EmojiCategory;
|
||||
156
lib/src/models/emoji_category.freezed.dart
Normal file
156
lib/src/models/emoji_category.freezed.dart
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint, type=warning, deprecated_member_use, deprecated_member_use_from_same_package
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'emoji_category.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$EmojiCategory {
|
||||
|
||||
|
||||
/// Create a copy of EmojiCategory
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$EmojiCategoryCopyWith<EmojiCategory> get copyWith => _$EmojiCategoryCopyWithImpl<EmojiCategory>(this as EmojiCategory, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
final _this = this as EmojiCategory;
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is EmojiCategory&&(identical(other.icon, _this.icon) || other.icon == _this.icon)&&(identical(other.name, _this.name) || other.name == _this.name)&&const DeepCollectionEquality().equals(other.emojis, _this.emojis));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
final _this = this as EmojiCategory;
|
||||
return Object.hash(runtimeType,_this.icon,_this.name,const DeepCollectionEquality().hash(_this.emojis));
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final _this = this as EmojiCategory;
|
||||
return 'EmojiCategory(icon: ${_this.icon}, name: ${_this.name}, emojis: ${_this.emojis})';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $EmojiCategoryCopyWith<$Res> {
|
||||
factory $EmojiCategoryCopyWith(EmojiCategory value, $Res Function(EmojiCategory) _then) = _$EmojiCategoryCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
Widget icon, String name, IList<Emoji> emojis
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$EmojiCategoryCopyWithImpl<$Res>
|
||||
implements $EmojiCategoryCopyWith<$Res> {
|
||||
_$EmojiCategoryCopyWithImpl(this._self, this._then);
|
||||
|
||||
final EmojiCategory _self;
|
||||
final $Res Function(EmojiCategory) _then;
|
||||
|
||||
/// Create a copy of EmojiCategory
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? icon = null,Object? name = null,Object? emojis = null,}) {
|
||||
return _then(EmojiCategory(
|
||||
icon: null == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable
|
||||
as Widget,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||
as String,emojis: null == emojis ? _self.emojis : emojis // ignore: cast_nullable_to_non_nullable
|
||||
as IList<Emoji>,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _EmojiCategory extends EmojiCategory {
|
||||
_EmojiCategory({required this.icon, required this.name, required this.emojis}): super(icon: icon, name: name, emojis: emojis);
|
||||
|
||||
|
||||
@override final Widget icon;
|
||||
@override final String name;
|
||||
@override final IList<Emoji> emojis;
|
||||
|
||||
/// Create a copy of EmojiCategory
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$EmojiCategoryCopyWith<_EmojiCategory> get copyWith => __$EmojiCategoryCopyWithImpl<_EmojiCategory>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _EmojiCategory&&(identical(other.icon, icon) || other.icon == icon)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other.emojis, emojis));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(runtimeType,icon,name,const DeepCollectionEquality().hash(emojis));
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'EmojiCategory(icon: $icon, name: $name, emojis: $emojis)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$EmojiCategoryCopyWith<$Res> implements $EmojiCategoryCopyWith<$Res> {
|
||||
factory _$EmojiCategoryCopyWith(_EmojiCategory value, $Res Function(_EmojiCategory) _then) = __$EmojiCategoryCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
Widget icon, String name, IList<Emoji> emojis
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$EmojiCategoryCopyWithImpl<$Res>
|
||||
implements _$EmojiCategoryCopyWith<$Res> {
|
||||
__$EmojiCategoryCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _EmojiCategory _self;
|
||||
final $Res Function(_EmojiCategory) _then;
|
||||
|
||||
/// Create a copy of EmojiCategory
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? icon = null,Object? name = null,Object? emojis = null,}) {
|
||||
return _then(_EmojiCategory(
|
||||
icon: null == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable
|
||||
as Widget,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||
as String,emojis: null == emojis ? _self.emojis : emojis // ignore: cast_nullable_to_non_nullable
|
||||
as IList<Emoji>,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
182
lib/src/widgets/emoji_picker.dart
Normal file
182
lib/src/widgets/emoji_picker.dart
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
import "dart:async";
|
||||
|
||||
import "package:collection/collection.dart";
|
||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:flutter_hooks/flutter_hooks.dart";
|
||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||
import "package:material_ui/material_ui.dart";
|
||||
import "package:material_emoji_picker/src/controllers/emoji.dart";
|
||||
import "package:material_emoji_picker/src/models/emoji_category.dart";
|
||||
import "package:super_sliver_list/super_sliver_list.dart";
|
||||
|
||||
final class const EmojiPicker({
|
||||
final List<EmojiCategory> prependCategories = const [],
|
||||
final List<EmojiCategory> appendCategories = const [],
|
||||
required final FutureOr<void> Function(String value) onSelection,
|
||||
final bool allowFreeText = false,
|
||||
super.key,
|
||||
}) extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectedCategory = useState<int?>(null);
|
||||
final search = useState("");
|
||||
|
||||
final scrollController = useScrollController();
|
||||
final listController = useMemoized(ListController.new);
|
||||
|
||||
final categoryScrollController = useScrollController();
|
||||
final categoryListController = useMemoized(ListController.new);
|
||||
|
||||
useEffect(() {
|
||||
void onChange() {
|
||||
final range = listController.visibleRange;
|
||||
if (range != null) {
|
||||
final firstVisible = range.$1;
|
||||
if (selectedCategory.value != firstVisible) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
selectedCategory.value = firstVisible;
|
||||
categoryListController.animateToItem(
|
||||
index: firstVisible,
|
||||
scrollController: categoryScrollController,
|
||||
alignment: 0,
|
||||
duration: (_) => Duration(milliseconds: 300),
|
||||
curve: (_) => Curves.easeInOut,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
listController.addListener(onChange);
|
||||
return () => listController.removeListener(onChange);
|
||||
}, [listController]);
|
||||
|
||||
return Container(
|
||||
padding: .all(16),
|
||||
constraints: .loose(.new(500, 700)),
|
||||
child: switch (ref.watch(EmojiController.provider)) {
|
||||
AsyncError(:final error) => throw error,
|
||||
AsyncLoading _ => Center(child: CircularProgressIndicator()),
|
||||
AsyncData(value: final categories) => HookBuilder(
|
||||
builder: (context) {
|
||||
final combined = useMemoized(
|
||||
() => prependCategories
|
||||
.toIList()
|
||||
.addAll(categories)
|
||||
.addAll(appendCategories),
|
||||
);
|
||||
|
||||
return Column(
|
||||
spacing: 4,
|
||||
children: [
|
||||
SearchBar(
|
||||
hintText: "Search emoji",
|
||||
leading: Icon(Icons.search),
|
||||
onChanged: (value) => search.value = value,
|
||||
trailing: [
|
||||
if (allowFreeText)
|
||||
IconButton(
|
||||
onPressed: search.value.isEmpty
|
||||
? null
|
||||
: () => onSelection(search.value),
|
||||
icon: Icon(Icons.send),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 48,
|
||||
child: SuperListView(
|
||||
scrollDirection: .horizontal,
|
||||
controller: categoryScrollController,
|
||||
listController: categoryListController,
|
||||
children: combined
|
||||
.mapIndexed(
|
||||
(index, category) => Padding(
|
||||
padding: .symmetric(horizontal: 4),
|
||||
child: FilterChip(
|
||||
label: Text(category.name),
|
||||
avatar: category.icon,
|
||||
selected: selectedCategory.value == index,
|
||||
showCheckmark: false,
|
||||
onSelected: (_) {
|
||||
selectedCategory.value = index;
|
||||
|
||||
listController.animateToItem(
|
||||
index: index,
|
||||
scrollController: scrollController,
|
||||
curve: (_) => Curves.easeInOut,
|
||||
duration: (_) => Duration(milliseconds: 300),
|
||||
alignment: 0,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: SuperListView.builder(
|
||||
listController: listController,
|
||||
controller: scrollController,
|
||||
itemCount: combined.length,
|
||||
itemBuilder: (context, index) {
|
||||
final category = combined[index];
|
||||
final emojis = search.value.isEmpty
|
||||
? category.emojis
|
||||
: category.emojis
|
||||
.where(
|
||||
(emoji) =>
|
||||
emoji.aliases.join().contains(
|
||||
search.value,
|
||||
) ||
|
||||
emoji.description.contains(
|
||||
search.value,
|
||||
) ||
|
||||
emoji.tags.join().contains(search.value),
|
||||
)
|
||||
.toIList();
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
category.name,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
GridView.builder(
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 56,
|
||||
mainAxisExtent: 56,
|
||||
),
|
||||
itemCount: emojis.length,
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
final emoji = emojis[index];
|
||||
|
||||
return IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () => onSelection(emoji.value),
|
||||
icon: SizedBox.square(
|
||||
dimension: 36,
|
||||
child: FittedBox(child: emoji.widget),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue