add multi_trigger_autocomplete_plus

This commit is contained in:
DenserMeerkat
2025-02-15 20:54:01 +05:30
parent 21c65615e9
commit 2eedd1b41a
35 changed files with 3106 additions and 69 deletions

View File

@@ -0,0 +1,67 @@
import 'package:example/src/models.dart';
import 'package:flutter/material.dart';
import 'package:flutter_parsed_text/flutter_parsed_text.dart';
import 'package:google_fonts/google_fonts.dart';
class ChatMessageList extends StatelessWidget {
const ChatMessageList({
Key? key,
required this.messages,
}) : super(key: key);
final List<ChatMessage> messages;
@override
Widget build(BuildContext context) {
final messages = this.messages.reversed.toList();
return ListView.separated(
reverse: true,
itemCount: messages.length,
padding: const EdgeInsets.all(8),
separatorBuilder: (context, index) => const SizedBox(height: 8),
itemBuilder: (BuildContext context, int index) {
final message = messages[index];
return Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Flexible(
child: Container(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Color(0xFFE9EAF4),
borderRadius: BorderRadius.only(
topRight: Radius.circular(32.0),
topLeft: Radius.circular(32.0),
bottomLeft: Radius.circular(32.0),
),
),
child: ParsedText(
text: message.text,
style: GoogleFonts.robotoMono(
fontWeight: FontWeight.w500,
color: const Color(0xFF79708F),
),
parse: <MatchText>[
MatchText(
pattern: r"@[A-Za-z0-9_.-]*",
style: const TextStyle(color: Colors.green),
),
MatchText(
pattern: r"\B#+([\w]+)\b",
style: const TextStyle(color: Colors.blue),
),
],
),
),
),
const SizedBox(width: 8),
CircleAvatar(
backgroundImage: NetworkImage(message.sender.avatar),
),
],
);
},
);
}
}

View File

@@ -0,0 +1,67 @@
import 'package:example/src/models.dart';
import 'package:flutter/material.dart';
import 'dart:math' show Random;
import 'package:example/src/data.dart';
class ChatMessageTextField extends StatelessWidget {
const ChatMessageTextField({
Key? key,
required this.focusNode,
required this.controller,
required this.onSend,
}) : super(key: key);
final FocusNode focusNode;
final TextEditingController controller;
final ValueSetter<ChatMessage> onSend;
final _sender = const [sahil, avni, gaurav];
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.fromLTRB(16, 4, 4, 4),
decoration: const BoxDecoration(
color: Color(0xFFF7F7F8),
borderRadius: BorderRadius.all(Radius.circular(40.0)),
),
child: Row(
children: [
Expanded(
child: TextField(
focusNode: focusNode,
controller: controller,
decoration: const InputDecoration.collapsed(
hintText: "Type your message...",
),
),
),
IconButton(
onPressed: () {
final message = ChatMessage(
text: controller.text,
createdAt: DateTime.now(),
sender: _sender[Random().nextInt(_sender.length)],
);
onSend(message);
},
padding: const EdgeInsets.all(4),
icon: Container(
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF5B61B9),
),
alignment: Alignment.center,
child: const Icon(
Icons.send_rounded,
color: Colors.white,
size: 24,
),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,243 @@
import 'package:example/src/models.dart';
const sahil = User(
id: 'xsahil03x',
name: 'Sahil Kumar',
avatar: 'https://bit.ly/3yEVRrD',
);
const avni = User(
id: 'avu.saxena',
name: 'Avni Saxena',
avatar: 'https://bit.ly/3PbPBii',
);
const trapti = User(
id: 'trapti2711',
name: 'Trapti Gupta',
avatar: 'https://bit.ly/3aDHtba',
);
const gaurav = User(
id: 'itsmegb98',
name: 'Gaurav Bhadouriya',
avatar: 'https://bit.ly/3PmNdES',
);
const amit = User(
id: 'amitk_15',
name: 'Amit Kumar',
avatar: 'https://bit.ly/3P9GPB8',
);
const ayush = User(
id: 'ayushpgupta',
name: 'Ayush Gupta',
avatar: 'https://bit.ly/3Rw61Dv',
);
const shubham = User(
id: 'someshubham',
name: 'Shubham Jain',
avatar: 'https://bit.ly/3Rs3uud',
);
const kUsers = <User>[
sahil,
avni,
gaurav,
trapti,
amit,
ayush,
shubham,
];
const kHashtags = <Hashtag>[
Hashtag(
name: 'dart',
weight: 1,
description:
'Dart is a language for building fast, scalable and maintainable applications.',
image: 'https://dwglogo.com/wp-content/uploads/2018/03/Dart_logo.png',
),
Hashtag(
name: 'flutter',
weight: 2,
description:
'Flutter is a framework for building native Android and iOS applications for Google\'s mobile platforms.',
image:
'https://storage.googleapis.com/cms-storage-bucket/0dbfcc7a59cd1cf16282.png',
),
Hashtag(
name: 'firebase',
weight: 3,
description:
'Firebase is a cloud platform for building mobile and web apps.',
image:
'https://firebase.google.com/static/downloads/brand-guidelines/PNG/logo-logomark.png',
),
Hashtag(
name: 'google',
weight: 4,
description:
'Google is a company that builds products and services for the world\'s users.',
image: 'https://dwglogo.com/wp-content/uploads/2016/06/G-icon-1068x735.png',
),
Hashtag(
name: 'apple',
weight: 5,
description:
'Apple is a company that builds products and services for the world\'s users.',
image:
'https://dwglogo.com/wp-content/uploads/2016/02/Apple_logo-1068x601.png',
),
Hashtag(
name: 'microsoft',
weight: 6,
description:
'Microsoft is a company that builds products and services for the world\'s users.',
image:
'https://upload.wikimedia.org/wikipedia/commons/thumb/4/44/Microsoft_logo.svg/2048px-Microsoft_logo.svg.png',
),
Hashtag(
name: 'facebook',
weight: 7,
description:
'Facebook is a company that builds products and services for the world\'s users.',
image: 'https://www.facebook.com/images/fb_icon_325x325.png',
),
Hashtag(
name: 'twitter',
weight: 8,
description:
'Twitter is a company that builds products and services for the world\'s users.',
image:
'https://dwglogo.com/wp-content/uploads/2019/02/Twitter_logo-1024x705.png',
),
Hashtag(
name: 'instagram',
weight: 9,
description:
'Instagram is a company that builds products and services for the world\'s users.',
image:
'https://w7.pngwing.com/pngs/648/943/png-transparent-instagram-logo-logo-instagram-computer-icons-camera-instagram-logo-text-trademark-magenta.png',
),
Hashtag(
name: 'snapchat',
weight: 10,
description:
'Snapchat is a company that builds products and services for the world\'s users.',
image:
'https://dwglogo.com/wp-content/uploads/2016/06/dotted_logo_of_snapchat-1068x601.png',
),
Hashtag(
name: 'youtube',
weight: 11,
description:
'YouTube is a company that builds products and services for the world\'s users.',
image:
'https://dwglogo.com/wp-content/uploads/2020/05/1200px-YouTube_logo-1024x729.png',
),
];
const kEmojis = <Emoji>[
Emoji(
char: '😀',
shortName: ':grinning:',
unicode: '1f600',
),
Emoji(
char: '😂',
shortName: ':joy:',
unicode: '1f602',
),
Emoji(
char: '😃',
shortName: ':smiley:',
unicode: '1f603',
),
Emoji(
char: '😄',
shortName: ':smile:',
unicode: '1f604',
),
Emoji(
char: '😅',
shortName: ':sweat_smile:',
unicode: '1f605',
),
Emoji(
char: '😆',
shortName: ':laughing:',
unicode: '1f606',
),
Emoji(
char: '😇',
shortName: ':wink:',
unicode: '1f609',
),
Emoji(
char: '😈',
shortName: ':smirk:',
unicode: '1f60f',
),
Emoji(
char: '😉',
shortName: ':wink2:',
unicode: '1f609',
),
Emoji(
char: '😊',
shortName: ':blush:',
unicode: '1f60a',
),
Emoji(
char: '😋',
shortName: ':yum:',
unicode: '1f60b',
),
Emoji(
char: '😌',
shortName: ':relieved:',
unicode: '1f60c',
),
Emoji(
char: '😍',
shortName: ':heart_eyes:',
unicode: '1f60d',
),
];
final sampleGroupConversation = [
ChatMessage(
text: 'Hey there! What\'s up?',
createdAt: DateTime.now().subtract(const Duration(seconds: 1)),
sender: sahil,
),
ChatMessage(
text: 'Nothing. Just chilling and watching YouTube. What about you?',
createdAt: DateTime.now().subtract(const Duration(seconds: 2)),
sender: avni,
),
ChatMessage(
text: 'Yeah I know. I\'m in the same position 😂',
createdAt: DateTime.now().subtract(const Duration(seconds: 3)),
sender: sahil,
),
ChatMessage(
text: 'I\'m just trying to get some sleep',
createdAt: DateTime.now().subtract(const Duration(seconds: 4)),
sender: gaurav,
),
ChatMessage(
text:
'Same here! Been watching YouTube for the past 5 hours despite of having so much to do! 😅',
createdAt: DateTime.now().subtract(const Duration(seconds: 5)),
sender: trapti,
),
ChatMessage(
text: 'It\'s hard to be productive',
createdAt: DateTime.now().subtract(const Duration(seconds: 6)),
sender: avni,
),
];

View File

@@ -0,0 +1,49 @@
class Emoji {
const Emoji({
required this.char,
required this.shortName,
required this.unicode,
});
final String char;
final String shortName;
final String unicode;
}
class Hashtag {
const Hashtag({
required this.name,
required this.weight,
required this.description,
required this.image,
});
final String name;
final int weight;
final String description;
final String image;
}
class User {
const User({
required this.id,
required this.name,
required this.avatar,
});
final String id;
final String name;
final String avatar;
}
class ChatMessage {
const ChatMessage({
required this.text,
required this.createdAt,
required this.sender,
});
final String text;
final DateTime createdAt;
final User sender;
}

View File

@@ -0,0 +1,71 @@
import 'package:example/src/data.dart';
import 'package:flutter/material.dart';
import 'package:example/src/models.dart';
class EmojiAutocompleteOptions extends StatelessWidget {
const EmojiAutocompleteOptions({
Key? key,
required this.query,
required this.onEmojiTap,
}) : super(key: key);
final String query;
final ValueSetter<Emoji> onEmojiTap;
@override
Widget build(BuildContext context) {
final emojis = kEmojis.where((it) {
final normalizedOption = it.shortName.toLowerCase();
final normalizedQuery = query.toLowerCase();
return normalizedOption.contains(normalizedQuery);
});
if (emojis.isEmpty) return const SizedBox.shrink();
return Card(
margin: const EdgeInsets.all(8),
elevation: 2,
// color: _streamChatTheme.colorTheme.barsBg,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
clipBehavior: Clip.hardEdge,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
color: const Color(0xFFF7F7F8),
child: ListTile(
dense: true,
horizontalTitleGap: 0,
title: Text("Emoji's matching '$query'"),
),
),
const Divider(height: 0),
LimitedBox(
maxHeight: MediaQuery.of(context).size.height * 0.3,
child: ListView.separated(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: emojis.length,
separatorBuilder: (_, __) => const Divider(height: 0),
itemBuilder: (context, i) {
final emoji = emojis.elementAt(i);
return ListTile(
dense: true,
leading: Text(
emoji.char,
style: const TextStyle(fontSize: 24),
),
title: Text(emoji.shortName),
onTap: () => onEmojiTap(emoji),
);
},
),
),
],
),
);
}
}

View File

@@ -0,0 +1,78 @@
import 'package:example/src/data.dart';
import 'package:flutter/material.dart';
import 'package:example/src/models.dart';
class HashtagAutocompleteOptions extends StatelessWidget {
const HashtagAutocompleteOptions({
Key? key,
required this.query,
required this.onHashtagTap,
}) : super(key: key);
final String query;
final ValueSetter<Hashtag> onHashtagTap;
@override
Widget build(BuildContext context) {
final hashtags = kHashtags.where((it) {
final normalizedOption = it.name.toLowerCase();
final normalizedQuery = query.toLowerCase();
return normalizedOption.contains(normalizedQuery);
});
if (hashtags.isEmpty) return const SizedBox.shrink();
return Card(
margin: const EdgeInsets.all(8),
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
clipBehavior: Clip.hardEdge,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
color: const Color(0xFFE9EAF4),
child: ListTile(
dense: true,
horizontalTitleGap: 0,
title: Text("Hashtags matching '$query'"),
),
),
const Divider(height: 0),
LimitedBox(
maxHeight: MediaQuery.of(context).size.height * 0.3,
child: ListView.separated(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: hashtags.length,
separatorBuilder: (_, __) => const Divider(height: 0),
itemBuilder: (context, i) {
final hashtag = hashtags.elementAt(i);
return ListTile(
dense: true,
leading: CircleAvatar(
backgroundColor: const Color(0xFFF7F7F8),
backgroundImage: NetworkImage(
hashtag.image,
scale: 0.5,
),
),
title: Text('#${hashtag.name}'),
subtitle: Text(
hashtag.description,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
onTap: () => onHashtagTap(hashtag),
);
},
),
),
],
),
);
}
}

View File

@@ -0,0 +1,71 @@
import 'package:example/src/data.dart';
import 'package:flutter/material.dart';
import 'package:example/src/models.dart';
class MentionAutocompleteOptions extends StatelessWidget {
const MentionAutocompleteOptions({
Key? key,
required this.query,
required this.onMentionUserTap,
}) : super(key: key);
final String query;
final ValueSetter<User> onMentionUserTap;
@override
Widget build(BuildContext context) {
final users = kUsers.where((it) {
final normalizedId = it.id.toLowerCase();
final normalizedName = it.name.toLowerCase();
final normalizedQuery = query.toLowerCase();
return normalizedId.contains(normalizedQuery) ||
normalizedName.contains(normalizedQuery);
});
if (users.isEmpty) return const SizedBox.shrink();
return Card(
margin: const EdgeInsets.all(8),
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
clipBehavior: Clip.hardEdge,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
color: const Color(0xFFF7F7F8),
child: ListTile(
dense: true,
horizontalTitleGap: 0,
title: Text("Users matching '$query'"),
),
),
LimitedBox(
maxHeight: MediaQuery.of(context).size.height * 0.3,
child: ListView.separated(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: users.length,
separatorBuilder: (_, __) => const Divider(height: 0),
itemBuilder: (context, i) {
final user = users.elementAt(i);
return ListTile(
dense: true,
leading: CircleAvatar(
backgroundImage: NetworkImage(user.avatar),
),
title: Text(user.name),
subtitle: Text('@${user.id}'),
onTap: () => onMentionUserTap(user),
);
},
),
),
],
),
);
}
}

View File

@@ -0,0 +1,3 @@
export 'emoji_autocomplete_options.dart';
export 'hashtag_autocomplete_options.dart';
export 'mention_autocomplete_options.dart';