forked from Henry-Hiles/nexus
custom text message wrapper
This commit is contained in:
parent
983f6d6c18
commit
3e0d8304b6
6 changed files with 210 additions and 200 deletions
|
|
@ -104,7 +104,7 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
|
||||||
);
|
);
|
||||||
if (oldMessage == null || message == null || !ref.mounted) return;
|
if (oldMessage == null || message == null || !ref.mounted) return;
|
||||||
|
|
||||||
return await updateMessage(
|
return await controller.updateMessage(
|
||||||
oldMessage,
|
oldMessage,
|
||||||
message.copyWith(
|
message.copyWith(
|
||||||
id: oldMessage.id,
|
id: oldMessage.id,
|
||||||
|
|
@ -225,9 +225,6 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateMessage(Message message, Message newMessage) async =>
|
|
||||||
(await future).updateMessage(message, newMessage);
|
|
||||||
|
|
||||||
Future<void> send(
|
Future<void> send(
|
||||||
String message, {
|
String message, {
|
||||||
bool shouldMention = true,
|
bool shouldMention = true,
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,9 @@ import "package:flutter/material.dart";
|
||||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||||
import "package:flutter_chat_ui/flutter_chat_ui.dart";
|
import "package:flutter_chat_ui/flutter_chat_ui.dart";
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
import "package:flutter_link_previewer/flutter_link_previewer.dart";
|
|
||||||
import "package:flyer_chat_file_message/flyer_chat_file_message.dart";
|
import "package:flyer_chat_file_message/flyer_chat_file_message.dart";
|
||||||
import "package:flyer_chat_image_message/flyer_chat_image_message.dart";
|
import "package:flyer_chat_image_message/flyer_chat_image_message.dart";
|
||||||
import "package:flyer_chat_system_message/flyer_chat_system_message.dart";
|
import "package:flyer_chat_system_message/flyer_chat_system_message.dart";
|
||||||
import "package:flyer_chat_text_message/flyer_chat_text_message.dart";
|
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:nexus/controllers/client_controller.dart";
|
import "package:nexus/controllers/client_controller.dart";
|
||||||
import "package:nexus/controllers/client_state_controller.dart";
|
import "package:nexus/controllers/client_state_controller.dart";
|
||||||
|
|
@ -21,10 +19,10 @@ import "package:nexus/helpers/extensions/show_context_menu.dart";
|
||||||
import "package:nexus/models/relation_type.dart";
|
import "package:nexus/models/relation_type.dart";
|
||||||
import "package:nexus/models/requests/report_request.dart";
|
import "package:nexus/models/requests/report_request.dart";
|
||||||
import "package:nexus/widgets/chat_page/chat_box.dart";
|
import "package:nexus/widgets/chat_page/chat_box.dart";
|
||||||
import "package:nexus/widgets/chat_page/html/html.dart";
|
|
||||||
import "package:nexus/widgets/chat_page/member_list.dart";
|
import "package:nexus/widgets/chat_page/member_list.dart";
|
||||||
import "package:nexus/widgets/chat_page/message_wrapper.dart";
|
import "package:nexus/widgets/chat_page/message_wrapper.dart";
|
||||||
import "package:nexus/widgets/chat_page/room_appbar.dart";
|
import "package:nexus/widgets/chat_page/room_appbar.dart";
|
||||||
|
import "package:nexus/widgets/chat_page/text_message_wrapper.dart";
|
||||||
import "package:nexus/widgets/chat_page/top_widget.dart";
|
import "package:nexus/widgets/chat_page/top_widget.dart";
|
||||||
import "package:nexus/widgets/form_text_input.dart";
|
import "package:nexus/widgets/form_text_input.dart";
|
||||||
import "package:nexus/widgets/loading.dart";
|
import "package:nexus/widgets/loading.dart";
|
||||||
|
|
@ -187,6 +185,13 @@ class RoomChat extends HookConsumerWidget {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final chatTheme = ChatTheme.fromThemeData(theme).copyWith(
|
||||||
|
colors: ChatColors.fromThemeData(theme).copyWith(
|
||||||
|
primary: theme.colorScheme.primaryContainer,
|
||||||
|
onPrimary: theme.colorScheme.onPrimaryContainer,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: RoomAppbar(
|
appBar: RoomAppbar(
|
||||||
room,
|
room,
|
||||||
|
|
@ -208,12 +213,7 @@ class RoomChat extends HookConsumerWidget {
|
||||||
.betterWhen(
|
.betterWhen(
|
||||||
data: (controller) => Chat(
|
data: (controller) => Chat(
|
||||||
currentUserId: userId,
|
currentUserId: userId,
|
||||||
theme: ChatTheme.fromThemeData(theme).copyWith(
|
theme: chatTheme,
|
||||||
colors: ChatColors.fromThemeData(theme).copyWith(
|
|
||||||
primary: theme.colorScheme.primaryContainer,
|
|
||||||
onPrimary: theme.colorScheme.onPrimaryContainer,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onMessageSecondaryTap:
|
onMessageSecondaryTap:
|
||||||
(
|
(
|
||||||
context,
|
context,
|
||||||
|
|
@ -359,180 +359,123 @@ class RoomChat extends HookConsumerWidget {
|
||||||
index, {
|
index, {
|
||||||
required bool isSentByMe,
|
required bool isSentByMe,
|
||||||
MessageGroupStatus? groupStatus,
|
MessageGroupStatus? groupStatus,
|
||||||
}) {
|
}) => MessageWrapper(
|
||||||
final image =
|
message,
|
||||||
message.metadata?["image"]
|
TextMessageWrapper(
|
||||||
as ImageMessage?;
|
|
||||||
return MessageWrapper(
|
|
||||||
message,
|
message,
|
||||||
|
content: message.text,
|
||||||
|
groupStatus: groupStatus,
|
||||||
|
onTapReply: notifier.scrollToMessage,
|
||||||
|
updateMessage: controller.updateMessage,
|
||||||
|
isSentByMe: isSentByMe,
|
||||||
|
),
|
||||||
|
|
||||||
|
groupStatus,
|
||||||
|
),
|
||||||
|
imageMessageBuilder:
|
||||||
|
(
|
||||||
|
context,
|
||||||
|
message,
|
||||||
|
index, {
|
||||||
|
required bool isSentByMe,
|
||||||
|
MessageGroupStatus? groupStatus,
|
||||||
|
}) {
|
||||||
|
final text =
|
||||||
|
message.metadata?["text"] as TextMessage?;
|
||||||
|
return MessageWrapper(
|
||||||
|
text ?? message,
|
||||||
Column(
|
Column(
|
||||||
spacing: 4,
|
spacing: 4,
|
||||||
crossAxisAlignment:
|
crossAxisAlignment:
|
||||||
CrossAxisAlignment.start,
|
CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
FlyerChatTextMessage(
|
TextMessageWrapper(
|
||||||
showTime: true,
|
message,
|
||||||
showStatus: false,
|
content: message.text,
|
||||||
customWidget: Column(
|
groupStatus: groupStatus,
|
||||||
spacing: 4,
|
onTapReply: notifier.scrollToMessage,
|
||||||
crossAxisAlignment:
|
updateMessage:
|
||||||
CrossAxisAlignment.start,
|
controller.updateMessage,
|
||||||
children: [
|
isSentByMe: isSentByMe,
|
||||||
Column(
|
extra: InkWell(
|
||||||
crossAxisAlignment:
|
onTap: () => showDialog(
|
||||||
CrossAxisAlignment.start,
|
context: context,
|
||||||
children: [
|
builder: (_) => LayoutBuilder(
|
||||||
Html(
|
builder:
|
||||||
textStyle:
|
(
|
||||||
message.metadata?["big"] ==
|
context,
|
||||||
true
|
constraints,
|
||||||
? TextStyle(
|
) => Dialog(
|
||||||
fontSize: 32,
|
backgroundColor:
|
||||||
)
|
Colors.transparent,
|
||||||
: null,
|
insetPadding:
|
||||||
message.text
|
EdgeInsets.all(
|
||||||
.replaceAllMapped(
|
constraints
|
||||||
RegExp(
|
.maxWidth /
|
||||||
"(<a\\b[^>]*>.*?<\\/a>)|(\\bhttps?:\\/\\/[^\\s<]+)",
|
100,
|
||||||
caseSensitive:
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
(m) {
|
child: ConstrainedBox(
|
||||||
// If it's already an <a> tag, leave it unchanged
|
constraints:
|
||||||
if (m.group(1) !=
|
BoxConstraints(
|
||||||
null) {
|
minWidth: min(
|
||||||
return m.group(
|
constraints
|
||||||
1,
|
.maxWidth,
|
||||||
)!;
|
1000,
|
||||||
}
|
),
|
||||||
|
|
||||||
// Otherwise, wrap the bare URL
|
|
||||||
final url = m.group(
|
|
||||||
2,
|
|
||||||
)!;
|
|
||||||
return "<a href=\"$url\">$url</a>";
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.replaceAll(
|
|
||||||
"\n",
|
|
||||||
"<br class=\"fake-break\"/>",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (message.editedAt != null)
|
|
||||||
Text(
|
|
||||||
"(edited)",
|
|
||||||
style: theme
|
|
||||||
.textTheme
|
|
||||||
.labelSmall,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (image != null)
|
|
||||||
InkWell(
|
|
||||||
onTap: () => showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (_) => LayoutBuilder(
|
|
||||||
builder: (context, constraints) => Dialog(
|
|
||||||
backgroundColor:
|
|
||||||
Colors.transparent,
|
|
||||||
insetPadding:
|
|
||||||
EdgeInsets.all(
|
|
||||||
constraints
|
|
||||||
.maxWidth /
|
|
||||||
100,
|
|
||||||
),
|
),
|
||||||
child: ConstrainedBox(
|
child: InteractiveViewer(
|
||||||
constraints:
|
child: Image(
|
||||||
BoxConstraints(
|
fit: BoxFit.contain,
|
||||||
minWidth: min(
|
image: CachedNetworkImage(
|
||||||
constraints
|
message.source,
|
||||||
.maxWidth,
|
ref.watch(
|
||||||
1000,
|
CrossCacheController
|
||||||
),
|
.provider,
|
||||||
),
|
|
||||||
child: InteractiveViewer(
|
|
||||||
child: Image(
|
|
||||||
fit: BoxFit
|
|
||||||
.contain,
|
|
||||||
image: CachedNetworkImage(
|
|
||||||
image.source,
|
|
||||||
ref.watch(
|
|
||||||
CrossCacheController
|
|
||||||
.provider,
|
|
||||||
),
|
|
||||||
headers:
|
|
||||||
ref.headers,
|
|
||||||
),
|
),
|
||||||
|
headers:
|
||||||
|
ref.headers,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: FlyerChatImageMessage(
|
||||||
|
customImageProvider:
|
||||||
|
CachedNetworkImage(
|
||||||
|
message.source,
|
||||||
|
ref.watch(
|
||||||
|
CrossCacheController
|
||||||
|
.provider,
|
||||||
|
),
|
||||||
|
headers: ref.headers,
|
||||||
),
|
),
|
||||||
child: FlyerChatImageMessage(
|
errorBuilder:
|
||||||
customImageProvider:
|
(
|
||||||
CachedNetworkImage(
|
context,
|
||||||
image.source,
|
error,
|
||||||
ref.watch(
|
stackTrace,
|
||||||
CrossCacheController
|
) => Center(
|
||||||
.provider,
|
child: Text(
|
||||||
),
|
"Image Failed to Load",
|
||||||
headers: ref.headers,
|
style: TextStyle(
|
||||||
),
|
color: Theme.of(
|
||||||
errorBuilder:
|
|
||||||
(
|
|
||||||
context,
|
context,
|
||||||
error,
|
).colorScheme.error,
|
||||||
stackTrace,
|
),
|
||||||
) => Center(
|
),
|
||||||
child: Text(
|
|
||||||
"Image Failed to Load",
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.error,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
message: image,
|
|
||||||
index: index,
|
|
||||||
),
|
),
|
||||||
),
|
message: message,
|
||||||
],
|
index: index,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
topWidget: TopWidget(
|
|
||||||
message,
|
|
||||||
groupStatus: groupStatus,
|
|
||||||
onTapReply:
|
|
||||||
notifier.scrollToMessage,
|
|
||||||
),
|
|
||||||
message: message,
|
|
||||||
index: index,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
groupStatus,
|
groupStatus,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
linkPreviewBuilder: (_, message, isSentByMe) =>
|
|
||||||
LinkPreview(
|
|
||||||
text: message.text,
|
|
||||||
backgroundColor: isSentByMe
|
|
||||||
? theme.colorScheme.inversePrimary
|
|
||||||
: theme.colorScheme.surfaceContainerLow,
|
|
||||||
insidePadding: EdgeInsets.symmetric(
|
|
||||||
vertical: 8,
|
|
||||||
horizontal: 16,
|
|
||||||
),
|
|
||||||
linkPreviewData: message.linkPreviewData,
|
|
||||||
onLinkPreviewDataFetched: (linkPreviewData) =>
|
|
||||||
notifier.updateMessage(
|
|
||||||
message,
|
|
||||||
message.copyWith(
|
|
||||||
linkPreviewData: linkPreviewData,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
fileMessageBuilder:
|
fileMessageBuilder:
|
||||||
(
|
(
|
||||||
_,
|
_,
|
||||||
|
|
|
||||||
105
lib/widgets/chat_page/text_message_wrapper.dart
Normal file
105
lib/widgets/chat_page/text_message_wrapper.dart
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||||
|
import "package:flutter_link_previewer/flutter_link_previewer.dart";
|
||||||
|
import "package:nexus/widgets/chat_page/html/html.dart";
|
||||||
|
import "package:nexus/widgets/chat_page/top_widget.dart";
|
||||||
|
|
||||||
|
class TextMessageWrapper extends StatelessWidget {
|
||||||
|
final Message message;
|
||||||
|
final String? content;
|
||||||
|
final MessageGroupStatus? groupStatus;
|
||||||
|
final Future<void> Function(Message oldMessage, Message newMessage)
|
||||||
|
updateMessage;
|
||||||
|
final bool isSentByMe;
|
||||||
|
final Widget? extra;
|
||||||
|
final OnTapReply onTapReply;
|
||||||
|
|
||||||
|
const TextMessageWrapper(
|
||||||
|
this.message, {
|
||||||
|
this.content,
|
||||||
|
this.onTapReply,
|
||||||
|
required this.updateMessage,
|
||||||
|
required this.groupStatus,
|
||||||
|
required this.isSentByMe,
|
||||||
|
this.extra,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final colorScheme = theme.colorScheme;
|
||||||
|
final textMessage = message is TextMessage ? message as TextMessage : null;
|
||||||
|
|
||||||
|
return ClipRRect(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isSentByMe
|
||||||
|
? colorScheme.primaryContainer
|
||||||
|
: colorScheme.surfaceContainer,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
TopWidget(
|
||||||
|
message,
|
||||||
|
groupStatus: groupStatus,
|
||||||
|
onTapReply: onTapReply,
|
||||||
|
),
|
||||||
|
if (content != null)
|
||||||
|
Html(
|
||||||
|
textStyle: message.metadata?["big"] == true
|
||||||
|
? TextStyle(fontSize: 32)
|
||||||
|
: null,
|
||||||
|
content!
|
||||||
|
.replaceAllMapped(
|
||||||
|
RegExp(
|
||||||
|
"(<a\\b[^>]*>.*?<\\/a>)|(\\bhttps?:\\/\\/[^\\s<]+)",
|
||||||
|
caseSensitive: false,
|
||||||
|
),
|
||||||
|
(m) {
|
||||||
|
// If it's already an <a> tag, leave it unchanged
|
||||||
|
if (m.group(1) != null) {
|
||||||
|
return m.group(1)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, wrap the bare URL
|
||||||
|
final url = m.group(2)!;
|
||||||
|
return "<a href=\"$url\">$url</a>";
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.replaceAll("\n", "<br class=\"fake-break\"/>"),
|
||||||
|
),
|
||||||
|
if (textMessage?.editedAt != null)
|
||||||
|
Text("(edited)", style: theme.textTheme.labelSmall),
|
||||||
|
if (textMessage != null)
|
||||||
|
LinkPreview(
|
||||||
|
text: textMessage.text,
|
||||||
|
backgroundColor: isSentByMe
|
||||||
|
? colorScheme.inversePrimary
|
||||||
|
: colorScheme.surfaceContainerLow,
|
||||||
|
outsidePadding: EdgeInsets.only(top: 4),
|
||||||
|
insidePadding: EdgeInsets.symmetric(
|
||||||
|
vertical: 8,
|
||||||
|
horizontal: 16,
|
||||||
|
),
|
||||||
|
linkPreviewData: message.metadata?["linkPreviewData"],
|
||||||
|
onLinkPreviewDataFetched: (linkPreviewData) => updateMessage(
|
||||||
|
message,
|
||||||
|
message.copyWith(
|
||||||
|
metadata: {
|
||||||
|
...(message.metadata ?? {}),
|
||||||
|
"linkPreviewData": linkPreviewData,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (extra != null) extra!,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,11 +5,13 @@ import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||||
import "package:nexus/widgets/chat_page/html/quoted.dart";
|
import "package:nexus/widgets/chat_page/html/quoted.dart";
|
||||||
|
|
||||||
|
typedef OnTapReply = void Function(Message message)?;
|
||||||
|
|
||||||
class TopWidget extends ConsumerWidget {
|
class TopWidget extends ConsumerWidget {
|
||||||
final Message message;
|
final Message message;
|
||||||
final bool alwaysShow;
|
final bool alwaysShow;
|
||||||
final MessageGroupStatus? groupStatus;
|
final MessageGroupStatus? groupStatus;
|
||||||
final void Function(Message message)? onTapReply;
|
final OnTapReply onTapReply;
|
||||||
const TopWidget(
|
const TopWidget(
|
||||||
this.message, {
|
this.message, {
|
||||||
required this.groupStatus,
|
required this.groupStatus,
|
||||||
|
|
|
||||||
33
pubspec.lock
33
pubspec.lock
|
|
@ -509,14 +509,6 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_math_fork:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: flutter_math_fork
|
|
||||||
sha256: "6d5f2f1aa57ae539ffb0a04bb39d2da67af74601d685a161aff7ce5bda5fa407"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.7.4"
|
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -591,15 +583,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
flyer_chat_text_message:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
path: "packages/flyer_chat_text_message"
|
|
||||||
ref: HEAD
|
|
||||||
resolved-ref: "03be67c8c81c8f637672ee03dd8f082d2c223627"
|
|
||||||
url: "https://github.com/Henry-Hiles/flutter_chat_ui"
|
|
||||||
source: git
|
|
||||||
version: "2.6.0"
|
|
||||||
freezed:
|
freezed:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
|
@ -640,14 +623,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.3"
|
||||||
gpt_markdown:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: gpt_markdown
|
|
||||||
sha256: "9b88dfaffea644070b648c204ca4a55745a49f4ad0b58ed0ab70913ad593c7a1"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.5"
|
|
||||||
graphs:
|
graphs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1381,14 +1356,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.0+1"
|
version: "0.1.0+1"
|
||||||
tuple:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: tuple
|
|
||||||
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.2"
|
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -42,10 +42,6 @@ dependencies:
|
||||||
flyer_chat_image_message: ^2.2.2
|
flyer_chat_image_message: ^2.2.2
|
||||||
flyer_chat_system_message: ^2.1.13
|
flyer_chat_system_message: ^2.1.13
|
||||||
flyer_chat_file_message: ^2.3.1
|
flyer_chat_file_message: ^2.3.1
|
||||||
flyer_chat_text_message:
|
|
||||||
git:
|
|
||||||
url: https://github.com/Henry-Hiles/flutter_chat_ui
|
|
||||||
path: packages/flyer_chat_text_message
|
|
||||||
flutter_chat_ui:
|
flutter_chat_ui:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Henry-Hiles/flutter_chat_ui
|
url: https://github.com/Henry-Hiles/flutter_chat_ui
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue