Feature: Add support for emoji's, add emoji_picker

This commit is contained in:
Thomas Klein Langenhorst 2024-07-16 16:18:27 +02:00
parent 69f2d542bc
commit 2c95cf81e9
5 changed files with 173 additions and 62 deletions

View file

@ -2,8 +2,11 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import "package:emoji_picker_flutter/emoji_picker_flutter.dart";
import "package:flutter/foundation.dart" as foundation;
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_chat_view/flutter_chat_view.dart"; import "package:flutter_chat_view/flutter_chat_view.dart";
import "package:google_fonts/google_fonts.dart";
class ChatBottom extends StatefulWidget { class ChatBottom extends StatefulWidget {
const ChatBottom({ const ChatBottom({
@ -41,14 +44,32 @@ class ChatBottom extends StatefulWidget {
} }
class _ChatBottomState extends State<ChatBottom> { class _ChatBottomState extends State<ChatBottom> {
final TextEditingController _textEditingController = TextEditingController();
bool _isTyping = false; bool _isTyping = false;
bool _isSending = false; bool _isSending = false;
bool _emojiPickerShowing = false;
late final EmojiTextEditingController _emojiTextEditingController;
late final ScrollController _scrollController;
late final FocusNode _focusNode;
late final TextStyle _emojiTextStyle;
final bool isApple = [TargetPlatform.iOS, TargetPlatform.macOS]
.contains(foundation.defaultTargetPlatform);
@override @override
Widget build(BuildContext context) { void initState() {
_textEditingController.addListener(() { var fontSize = 24 * (isApple ? 1.2 : 1.0);
if (_textEditingController.text.isEmpty) { // Define Custom Emoji Font & Text Style
_emojiTextStyle = DefaultEmojiTextStyle.copyWith(
fontFamily: GoogleFonts.notoColorEmoji().fontFamily,
fontSize: fontSize,
);
_emojiTextEditingController = EmojiTextEditingController(emojiTextStyle: _emojiTextStyle);
_scrollController = ScrollController();
_focusNode = FocusNode();
_emojiTextEditingController.addListener(() {
if (_emojiTextEditingController.text.isEmpty) {
setState(() { setState(() {
_isTyping = false; _isTyping = false;
}); });
@ -58,56 +79,123 @@ class _ChatBottomState extends State<ChatBottom> {
}); });
} }
}); });
return Padding( super.initState();
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 16,
),
child: SizedBox(
height: 45,
child: widget.messageInputBuilder(
_textEditingController,
Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: widget.onPressSelectImage,
icon: Icon(
Icons.image_outlined,
color: widget.iconColor,
),
),
IconButton(
disabledColor: widget.iconDisabledColor,
color: widget.iconColor,
onPressed: _isTyping && !_isSending
? () async {
setState(() {
_isSending = true;
});
var value = _textEditingController.text;
if (value.isNotEmpty) {
await widget.onMessageSubmit(value);
_textEditingController.clear();
}
setState(() {
_isSending = false;
});
}
: null,
icon: const Icon(
Icons.send,
),
),
],
),
widget.translations,
context,
),
),
);
} }
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 16,
),
child: Column(
children: [
SizedBox(
height: 45,
child: widget.messageInputBuilder(
_emojiTextEditingController,
_focusNode,
Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
setState(() {
_emojiPickerShowing = !_emojiPickerShowing;
if (!_emojiPickerShowing) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_focusNode.requestFocus();
});
} else {
_focusNode.unfocus();
}
});
},
icon: Icon(
_emojiPickerShowing
? Icons.keyboard
: Icons.emoji_emotions_outlined,
),
),
IconButton(
onPressed: widget.onPressSelectImage,
icon: Icon(
Icons.image_outlined,
color: widget.iconColor,
),
),
IconButton(
disabledColor: widget.iconDisabledColor,
color: widget.iconColor,
onPressed: _isTyping && !_isSending
? () async {
setState(() {
_isSending = true;
});
var value = _emojiTextEditingController.text;
if (value.isNotEmpty) {
await widget.onMessageSubmit(value);
_emojiTextEditingController.clear();
}
setState(() {
_isSending = false;
});
}
: null,
icon: const Icon(
Icons.send,
),
),
],
),
widget.translations,
context,
),
),
Offstage(
offstage: !_emojiPickerShowing,
child: EmojiPicker(
textEditingController: _emojiTextEditingController,
scrollController: _scrollController,
config: Config(
height: 256,
checkPlatformCompatibility: true,
emojiTextStyle: _emojiTextStyle,
emojiViewConfig: const EmojiViewConfig(
backgroundColor: Colors.white,
),
swapCategoryAndBottomBar: true,
skinToneConfig: const SkinToneConfig(),
categoryViewConfig: const CategoryViewConfig(
backgroundColor: Colors.white,
dividerColor: Colors.white,
indicatorColor: Colors.blue,
iconColorSelected: Colors.black,
iconColor: Color(0xFF8B98A0),
categoryIcons: CategoryIcons(
recentIcon: Icons.access_time_outlined,
smileyIcon: Icons.emoji_emotions_outlined,
animalIcon: Icons.cruelty_free_outlined,
foodIcon: Icons.coffee_outlined,
activityIcon: Icons.sports_soccer_outlined,
travelIcon: Icons.directions_car_filled_outlined,
objectIcon: Icons.lightbulb_outline,
symbolIcon: Icons.emoji_symbols_outlined,
flagIcon: Icons.flag_outlined,
),
),
bottomActionBarConfig: const BottomActionBarConfig(
backgroundColor: Colors.white,
buttonColor: Colors.white,
buttonIconColor: Color(0xFF8B98A0),
),
),
),
),
],
),
);
} }

View file

@ -3,10 +3,13 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import "package:cached_network_image/cached_network_image.dart"; import "package:cached_network_image/cached_network_image.dart";
import "package:emoji_picker_flutter/emoji_picker_flutter.dart";
import "package:flutter/foundation.dart" as foundation;
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_chat_view/flutter_chat_view.dart"; import "package:flutter_chat_view/flutter_chat_view.dart";
import "package:flutter_chat_view/src/components/chat_image.dart"; import "package:flutter_chat_view/src/components/chat_image.dart";
import "package:flutter_chat_view/src/services/date_formatter.dart"; import "package:flutter_chat_view/src/services/date_formatter.dart";
import "package:google_fonts/google_fonts.dart";
class ChatDetailRow extends StatefulWidget { class ChatDetailRow extends StatefulWidget {
const ChatDetailRow({ const ChatDetailRow({
@ -43,10 +46,19 @@ class ChatDetailRow extends StatefulWidget {
class _ChatDetailRowState extends State<ChatDetailRow> { class _ChatDetailRowState extends State<ChatDetailRow> {
final DateFormatter _dateFormatter = DateFormatter(); final DateFormatter _dateFormatter = DateFormatter();
final _emojiUtils = EmojiPickerUtils();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var theme = Theme.of(context); var theme = Theme.of(context);
var isApple = [TargetPlatform.iOS, TargetPlatform.macOS]
.contains(foundation.defaultTargetPlatform);
var fontSize = 24 * (isApple ? 1.2 : 1.0);
var emojiTextStyle = DefaultEmojiTextStyle.copyWith(
fontFamily: GoogleFonts.notoColorEmoji().fontFamily,
fontSize: fontSize,
);
var isNewDate = widget.previousMessage != null && var isNewDate = widget.previousMessage != null &&
widget.message.timestamp.day != widget.previousMessage?.timestamp.day; widget.message.timestamp.day != widget.previousMessage?.timestamp.day;
@ -135,11 +147,16 @@ class _ChatDetailRowState extends State<ChatDetailRow> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Flexible( Flexible(
child: Text( child: RichText(
(widget.message as ChatTextMessageModel).text, text: TextSpan(
style: TextStyle( children: _emojiUtils.setEmojiTextStyle(
fontSize: 16, (widget.message as ChatTextMessageModel)
color: theme.textTheme.labelMedium?.color, .text,
emojiStyle: emojiTextStyle,),
style: TextStyle(
fontSize: 16,
color: theme.textTheme.labelMedium?.color,
),
), ),
), ),
), ),

View file

@ -91,7 +91,7 @@ class ChatRow extends StatelessWidget {
width: 20, width: 20,
height: 20, height: 20,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary, color: theme.colorScheme.primary,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: Center( child: Center(

View file

@ -2,6 +2,7 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import "package:emoji_picker_flutter/emoji_picker_flutter.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_chat_view/flutter_chat_view.dart"; import "package:flutter_chat_view/flutter_chat_view.dart";
import "package:flutter_chat_view/src/components/chat_image.dart"; import "package:flutter_chat_view/src/components/chat_image.dart";
@ -85,12 +86,14 @@ Widget _createNewChatButton(
Widget _createMessageInput( Widget _createMessageInput(
TextEditingController textEditingController, TextEditingController textEditingController,
FocusNode focusNode,
Widget suffixIcon, Widget suffixIcon,
ChatTranslations translations, ChatTranslations translations,
BuildContext context, BuildContext context,
) { ) {
var theme = Theme.of(context); var theme = Theme.of(context);
return TextField( return TextField(
focusNode: focusNode,
textCapitalization: TextCapitalization.sentences, textCapitalization: TextCapitalization.sentences,
controller: textEditingController, controller: textEditingController,
decoration: InputDecoration( decoration: InputDecoration(
@ -235,7 +238,8 @@ typedef ButtonBuilder = Widget Function(
); );
typedef TextInputBuilder = Widget Function( typedef TextInputBuilder = Widget Function(
TextEditingController textEditingController, EmojiTextEditingController textEditingController,
FocusNode focusNode,
Widget suffixIcon, Widget suffixIcon,
ChatTranslations translations, ChatTranslations translations,
BuildContext context, BuildContext context,

View file

@ -16,6 +16,8 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
intl: any intl: any
emoji_picker_flutter: ^2.2.0
google_fonts: ^6.2.1
flutter_chat_interface: flutter_chat_interface:
git: git:
url: https://github.com/Iconica-Development/flutter_chat url: https://github.com/Iconica-Development/flutter_chat