Skip to content

Commit ccb3d2e

Browse files
committed
fix example, add additional features to the package
Signed-off-by: xsahil03x <[email protected]>
1 parent c4d7f68 commit ccb3d2e

19 files changed

+1021
-237
lines changed

CHANGELOG.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
## 0.0.1
1+
## 1.0.0
22

3-
* TODO: Describe initial release.
3+
* Initial release.

LICENSE

+21-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,21 @@
1-
TODO: Add your license here.
1+
MIT License
2+
3+
Copyright (c) 2022 Sahil Kumar
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

example/android/app/src/main/AndroidManifest.xml

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
22
package="com.example.example">
3+
<uses-permission android:name="android.permission.INTERNET"/>
34
<application
45
android:label="example"
56
android:name="${applicationName}"

example/ios/Flutter/Debug.xcconfig

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
12
#include "Generated.xcconfig"

example/ios/Flutter/Release.xcconfig

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
12
#include "Generated.xcconfig"

example/lib/main.dart

+92-147
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
import 'package:example/src/chat_message_list.dart';
2+
import 'package:example/src/chat_message_text_field.dart';
3+
import 'package:example/src/data.dart';
4+
import 'package:example/src/options/options.dart';
15
import 'package:flutter/material.dart';
6+
import 'package:google_fonts/google_fonts.dart';
27
import 'package:multi_trigger_autocomplete/multi_trigger_autocomplete.dart';
38

49
void main() => runApp(const MyApp());
@@ -13,173 +18,113 @@ class MyApp extends StatelessWidget {
1318
child: MaterialApp(
1419
title: 'Flutter Demo',
1520
theme: ThemeData(
16-
primarySwatch: Colors.teal,
21+
platform: TargetPlatform.iOS,
22+
textTheme: GoogleFonts.robotoMonoTextTheme(
23+
Theme.of(context).textTheme,
24+
),
1725
),
1826
home: const MyHomePage(),
1927
),
2028
);
2129
}
2230
}
2331

24-
class MyHomePage extends StatelessWidget {
32+
class MyHomePage extends StatefulWidget {
2533
const MyHomePage({Key? key}) : super(key: key);
2634

27-
final mentionData = const [
28-
'Sahil',
29-
'Avni',
30-
'Trapti',
31-
'Gaurav',
32-
'Prateek',
33-
'Amit',
34-
'Ayush',
35-
'Shubham',
36-
];
35+
@override
36+
State<MyHomePage> createState() => _MyHomePageState();
37+
}
3738

38-
final hashtagData = const [
39-
'love',
40-
'instagood',
41-
'photooftheday',
42-
'fashion',
43-
'beautiful',
44-
'happy',
45-
'cute',
46-
'tbt',
47-
];
39+
class _MyHomePageState extends State<MyHomePage> {
40+
final messages = [...sampleGroupConversation];
4841

4942
@override
5043
Widget build(BuildContext context) {
5144
return Scaffold(
45+
backgroundColor: Colors.white,
5246
appBar: AppBar(
53-
title: const Text('Multi Trigger Autocomplete'),
47+
toolbarHeight: 60,
48+
backgroundColor: const Color(0xFF5B61B9),
49+
title: const Text(
50+
'Multi Trigger Autocomplete',
51+
style: TextStyle(
52+
fontSize: 16,
53+
fontWeight: FontWeight.bold,
54+
),
55+
),
5456
centerTitle: true,
55-
),
56-
body: Padding(
57-
padding: const EdgeInsets.all(8.0),
58-
child: MultiTriggerAutocomplete(
59-
debounceDuration: Duration.zero,
60-
autocompleteTriggers: [
61-
AutocompleteTrigger(
62-
trigger: '@',
63-
optionsViewBuilder: (_, query, controller, closeOptions) {
64-
return OptionsView(
65-
data: mentionData,
66-
controller: controller,
67-
autocompleteQuery: query,
68-
closeOptions: closeOptions,
69-
);
70-
},
71-
),
72-
AutocompleteTrigger(
73-
trigger: '#',
74-
optionsViewBuilder: (_, query, controller, closeOptions) {
75-
return OptionsView(
76-
data: hashtagData,
77-
controller: controller,
78-
autocompleteQuery: query,
79-
closeOptions: closeOptions,
80-
);
81-
},
82-
),
83-
],
84-
fieldViewBuilder: (context, controller, focusNode) {
85-
return TextField(
86-
controller: controller,
87-
focusNode: focusNode,
88-
decoration: const InputDecoration(
89-
border: OutlineInputBorder(),
90-
labelText: 'Write something...',
91-
),
92-
);
93-
},
57+
shape: const RoundedRectangleBorder(
58+
borderRadius: BorderRadius.only(
59+
bottomLeft: Radius.circular(24),
60+
bottomRight: Radius.circular(24),
61+
),
9462
),
9563
),
96-
);
97-
}
98-
}
99-
100-
class OptionsView<T extends Object> extends StatelessWidget {
101-
const OptionsView({
102-
super.key,
103-
required this.data,
104-
required this.autocompleteQuery,
105-
required this.controller,
106-
required this.closeOptions,
107-
});
108-
109-
final Iterable<T> data;
110-
final AutocompleteQuery autocompleteQuery;
111-
final TextEditingController controller;
112-
final VoidCallback closeOptions;
113-
114-
@override
115-
Widget build(BuildContext context) {
116-
final options = data.where((it) {
117-
final normalizedOption = it.toString().toLowerCase();
118-
final normalizedQuery = autocompleteQuery.query.toLowerCase();
119-
return normalizedOption.contains(normalizedQuery);
120-
});
121-
122-
if (options.isEmpty) return const SizedBox.shrink();
123-
124-
return Card(
125-
margin: const EdgeInsets.all(8),
126-
elevation: 2,
127-
// color: _streamChatTheme.colorTheme.barsBg,
128-
shape: RoundedRectangleBorder(
129-
borderRadius: BorderRadius.circular(8),
130-
),
131-
clipBehavior: Clip.hardEdge,
132-
child: Column(
133-
mainAxisSize: MainAxisSize.min,
64+
body: Column(
13465
children: [
135-
const ListTile(
136-
dense: true,
137-
horizontalTitleGap: 0,
138-
title: Text('Matching Options...'),
139-
),
140-
const Divider(height: 0),
141-
LimitedBox(
142-
maxHeight: MediaQuery.of(context).size.height * 0.5,
143-
child: ListView.builder(
144-
padding: EdgeInsets.zero,
145-
shrinkWrap: true,
146-
itemCount: options.length,
147-
itemBuilder: (context, i) {
148-
final option = options.elementAt(i);
149-
return ListTile(
150-
dense: true,
151-
leading: CircleAvatar(
152-
radius: 16,
153-
child: Text(option.toString()[0]),
154-
),
155-
title: Text(option.toString()),
156-
onTap: () {
157-
final text = controller.text;
158-
final querySelection = autocompleteQuery.selection;
159-
160-
final queryEndsWithSpace =
161-
text.substring(querySelection.end).startsWith(' ');
162-
163-
final newText = text.substring(0, querySelection.start) +
164-
option.toString() +
165-
(queryEndsWithSpace ? '' : ' ') +
166-
text.substring(querySelection.end);
167-
168-
final newCursorPosition =
169-
querySelection.start + option.toString().length + 1;
170-
171-
controller.value = TextEditingValue(
172-
text: newText,
173-
selection: TextSelection.collapsed(
174-
offset: newCursorPosition,
175-
),
176-
);
177-
178-
closeOptions();
66+
Expanded(child: ChatMessageList(messages: messages)),
67+
MultiTriggerAutocomplete(
68+
optionsAlignment: OptionsAlignment.above,
69+
autocompleteTriggers: [
70+
AutocompleteTrigger(
71+
trigger: '@',
72+
optionsViewBuilder: (context, autocompleteQuery, controller) {
73+
return MentionAutocompleteOptions(
74+
query: autocompleteQuery.query,
75+
onMentionUserTap: (user) {
76+
final autocomplete = MultiTriggerAutocomplete.of(context);
77+
return autocomplete.acceptAutocompleteOption(user.id);
78+
},
79+
);
80+
},
81+
),
82+
AutocompleteTrigger(
83+
trigger: '#',
84+
optionsViewBuilder: (context, autocompleteQuery, controller) {
85+
return HashtagAutocompleteOptions(
86+
query: autocompleteQuery.query,
87+
onHashtagTap: (hashtag) {
88+
final autocomplete = MultiTriggerAutocomplete.of(context);
89+
return autocomplete
90+
.acceptAutocompleteOption(hashtag.name);
91+
},
92+
);
93+
},
94+
),
95+
AutocompleteTrigger(
96+
trigger: ':',
97+
optionsViewBuilder: (context, autocompleteQuery, controller) {
98+
return EmojiAutocompleteOptions(
99+
query: autocompleteQuery.query,
100+
onEmojiTap: (emoji) {
101+
final autocomplete = MultiTriggerAutocomplete.of(context);
102+
return autocomplete.acceptAutocompleteOption(
103+
emoji.char,
104+
// Passing false as we don't want the trigger [:] to
105+
// get prefixed to the option in case of emoji.
106+
keepTrigger: false,
107+
);
108+
},
109+
);
110+
},
111+
),
112+
],
113+
fieldViewBuilder: (context, controller, focusNode) {
114+
return Padding(
115+
padding: const EdgeInsets.all(8.0),
116+
child: ChatMessageTextField(
117+
focusNode: focusNode,
118+
controller: controller,
119+
onSend: (message) {
120+
controller.clear();
121+
setState(() {
122+
messages.add(message);
123+
});
179124
},
180-
);
181-
},
182-
),
125+
),
126+
);
127+
},
183128
),
184129
],
185130
),
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import 'package:example/src/models.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter_parsed_text/flutter_parsed_text.dart';
4+
import 'package:google_fonts/google_fonts.dart';
5+
6+
class ChatMessageList extends StatelessWidget {
7+
const ChatMessageList({
8+
Key? key,
9+
required this.messages,
10+
}) : super(key: key);
11+
12+
final List<ChatMessage> messages;
13+
14+
@override
15+
Widget build(BuildContext context) {
16+
final messages = this.messages.reversed.toList();
17+
return ListView.separated(
18+
reverse: true,
19+
itemCount: messages.length,
20+
padding: const EdgeInsets.all(8),
21+
separatorBuilder: (context, index) => const SizedBox(height: 8),
22+
itemBuilder: (BuildContext context, int index) {
23+
final message = messages[index];
24+
return Row(
25+
mainAxisAlignment: MainAxisAlignment.end,
26+
crossAxisAlignment: CrossAxisAlignment.end,
27+
children: [
28+
Flexible(
29+
child: Container(
30+
padding: const EdgeInsets.all(16),
31+
decoration: const BoxDecoration(
32+
color: Color(0xFFE9EAF4),
33+
borderRadius: BorderRadius.only(
34+
topRight: Radius.circular(32.0),
35+
topLeft: Radius.circular(32.0),
36+
bottomLeft: Radius.circular(32.0),
37+
),
38+
),
39+
child: ParsedText(
40+
text: message.text,
41+
style: GoogleFonts.robotoMono(
42+
fontWeight: FontWeight.w500,
43+
color: const Color(0xFF79708F),
44+
),
45+
parse: <MatchText>[
46+
MatchText(
47+
pattern: r"@[A-Za-z0-9_.-]*",
48+
style: const TextStyle(color: Colors.green),
49+
),
50+
MatchText(
51+
pattern: r"\B#+([\w]+)\b",
52+
style: const TextStyle(color: Colors.blue),
53+
),
54+
],
55+
),
56+
),
57+
),
58+
const SizedBox(width: 8),
59+
CircleAvatar(
60+
backgroundImage: NetworkImage(message.sender.avatar),
61+
),
62+
],
63+
);
64+
},
65+
);
66+
}
67+
}

0 commit comments

Comments
 (0)