├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ └── question.md
└── workflows
│ └── greetings.yml
├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── example
│ │ │ │ │ └── MainActivity.java
│ │ │ └── res
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── ios
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── Runner
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── main.m
├── lib
│ └── main.dart
├── pubspec.lock
├── pubspec.yaml
└── test
│ └── widget_test.dart
├── lib
├── dash_chat.dart
└── src
│ ├── chat_input_toolbar.dart
│ ├── chat_view.dart
│ ├── message_listview.dart
│ ├── models
│ ├── chat_message.dart
│ ├── chat_user.dart
│ ├── quick_replies.dart
│ ├── reply.dart
│ └── scroll_to_bottom_style.dart
│ └── widgets
│ ├── avatar_container.dart
│ ├── custom_scroll_behaviour.dart
│ ├── date_builder.dart
│ ├── load_earlier.dart
│ ├── message_container.dart
│ ├── quick_reply.dart
│ └── scroll_to_bottom.dart
├── pubspec.lock
├── pubspec.yaml
└── test
├── chat_user_test.dart
└── dash_chat_test.dart
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve this project
4 | title: "[Bug] Your title"
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | ⚠️ V1 is not maintained anymore, check the V2 on: https://github.com/molteo-engineering-team/Dash-Chat-2 ⚠️
11 |
12 | **Describe the bug**
13 | A clear and concise description of what the bug is.
14 |
15 | **To Reproduce**
16 | Steps to reproduce the behavior:
17 | 1. Go to '...'
18 | 2. Click on '....'
19 | 3. Scroll down to '....'
20 | 4. See error
21 |
22 | **Expected behavior**
23 | A clear and concise description of what you expected to happen.
24 |
25 | **Screenshots**
26 | If applicable, add screenshots to help explain your problem.
27 |
28 | **Desktop (please complete the following information):**
29 | - OS: [e.g. Mac, Windows, Ubuntu]
30 | - Device: [e.g. iPhone6] | Emulator
31 | - Flutter Version: [e.g. 1.7.8]
32 | - Dart Version: [e.g 2.4.0]
33 | - Version [e.g. 1.1.7]
34 |
35 | **Additional context**
36 | Add any other context about the problem here.
37 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[Feature request] Your title"
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | ⚠️ V1 is not maintained anymore, check the V2 on: https://github.com/molteo-engineering-team/Dash-Chat-2 ⚠️
11 |
12 | **Is your feature request related to a problem? Please describe.**
13 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
14 |
15 | **Describe the solution you'd like**
16 | A clear and concise description of what you want to happen.
17 |
18 | **Describe alternatives you've considered**
19 | A clear and concise description of any alternative solutions or features you've considered.
20 |
21 | **Additional context**
22 | Add any other context or screenshots about the feature request here.
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Question
3 | about: Ask a question about this project
4 | title: "[Question] Your question ?"
5 | labels: question
6 | assignees: ''
7 |
8 | ---
9 |
10 | ⚠️ V1 is not maintained anymore, check the V2 on: https://github.com/molteo-engineering-team/Dash-Chat-2 ⚠️
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.github/workflows/greetings.yml:
--------------------------------------------------------------------------------
1 | name: Greetings
2 |
3 | on: [pull_request_target, issues]
4 |
5 | jobs:
6 | greeting:
7 | runs-on: ubuntu-latest
8 | permissions:
9 | issues: write
10 | pull-requests: write
11 | steps:
12 | - uses: actions/first-interaction@v1
13 | with:
14 | repo-token: ${{ secrets.GITHUB_TOKEN }}
15 | issue-message: "Hey, this package is discontinued, Please check https://github.com/SebastienBtr/Dash-Chat-2 instead"
16 | pr-message: "Hey, this package is discontinued, please check https://github.com/SebastienBtr/Dash-Chat-2 instead"
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .packages
28 | .pub-cache/
29 | .pub/
30 | build/
31 | FUTURE_RELEASE.md
32 |
33 | # Android related
34 | **/android/**/gradle-wrapper.jar
35 | **/android/.gradle
36 | **/android/captures/
37 | **/android/gradlew
38 | **/android/gradlew.bat
39 | **/android/local.properties
40 | **/android/**/GeneratedPluginRegistrant.java
41 |
42 | # iOS/XCode related
43 | **/ios/**/*.mode1v3
44 | **/ios/**/*.mode2v3
45 | **/ios/**/*.moved-aside
46 | **/ios/**/*.pbxuser
47 | **/ios/**/*.perspectivev3
48 | **/ios/**/*sync/
49 | **/ios/**/.sconsign.dblite
50 | **/ios/**/.tags*
51 | **/ios/**/.vagrant/
52 | **/ios/**/DerivedData/
53 | **/ios/**/Icon?
54 | **/ios/**/Pods/
55 | **/ios/**/.symlinks/
56 | **/ios/**/profile
57 | **/ios/**/xcuserdata
58 | **/ios/.generated/
59 | **/ios/Flutter/App.framework
60 | **/ios/Flutter/Flutter.framework
61 | **/ios/Flutter/Generated.xcconfig
62 | **/ios/Flutter/app.flx
63 | **/ios/Flutter/app.zip
64 | **/ios/Flutter/flutter_assets/
65 | **/ios/ServiceDefinitions.json
66 | **/ios/Runner/GeneratedPluginRegistrant.*
67 |
68 | # Exceptions to above rules.
69 | !**/ios/**/default.mode1v3
70 | !**/ios/**/default.mode2v3
71 | !**/ios/**/default.pbxuser
72 | !**/ios/**/default.perspectivev3
73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
74 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: b712a172f9694745f50505c93340883493b505e5
8 | channel: stable
9 |
10 | project_type: package
11 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [1.1.16]
2 | - Pubspec updated to work with new UUID package [#172] https://github.com/fayeed/dash_chat/pull/172 by [@GonzaloAldana](https://github.com/GonzaloAldana)
3 |
4 | ## [1.1.15-nullsafety]
5 |
6 | - Support for sound null-safety added [#164](https://github.com/fayeed/dash_chat/pull/164) by [@diegogarciar](https://github.com/diegogarciar)
7 |
8 | ## [1.1.15]
9 |
10 | - Memory leaked issue fixed, PR [#133](https://github.com/fayeed/dash_chat/pull/133) by [@peter-sg](https://github.com/peter-sg) & [@pycstitch](https://github.com/pycstitch)
11 |
12 | ## [1.1.14]
13 |
14 | - `messageDecorationBuilder` property added.
15 |
16 | ## [1.1.13]
17 |
18 | - Fixed a bug from previous update.
19 |
20 | ## [1.1.12]
21 |
22 | - `inputDisabled` property added.
23 |
24 | ## [1.1.11]
25 |
26 | - Fixed a bug with previous version causing the widget to break.
27 |
28 | ## [1.1.10]
29 |
30 | - Add quick reply horizontal scroll and padding options
31 |
32 | ## [1.1.9]
33 |
34 | - Fixed issue #91, where setting the name was not handled properly
35 |
36 | ## [1.1.8]
37 |
38 | - Fixed issue #78, scrollToBottom widget not scrolling to maxExtent properly
39 |
40 | ## [1.1.7]
41 |
42 | - Fixed issue #30 where scrollToBottom widget was visible when the widget was dismounted.
43 |
44 | ## [1.1.6]
45 |
46 | - Added `firstName` & `lastName` property to User.
47 | - Added `avatarMaxSize` property to avatar Container.
48 | - Fix an issue with text Alignment when buttons are set in message container.
49 |
50 | ## [1.1.5]
51 |
52 | - Added a new property `shouldStartMessagesFromTop` to Dashchat.
53 |
54 | ## [1.1.4]
55 |
56 | - Fixed an issue with date builder and inverted list.
57 | - Replaced SizedBox with Opacity for User Avatar.
58 | - Custom Message padding property added.
59 | - Fixed an issue with avatar position when the list is inverted.
60 | - Added a new property `customProperties` to ChatUser model.
61 |
62 | ## [1.1.3]
63 |
64 | - A small bug fixed with inverted message list.
65 |
66 | ## [1.1.2]
67 |
68 | - Correct alignmnet to message buttons.
69 | - Added readonly mode to dashchat which hides the inputbar.
70 | - Fix avatar exception when username is null.
71 |
72 | ## [1.1.1]
73 |
74 | - Fixed an issue where an conversion `toJson` for class `ChatUser` was crashing the app.
75 |
76 | ## [1.1.0]
77 |
78 | - Ability to send message on Enter or input action keyboard.
79 | - `MediaQuery` replaced with `LayoutBuilder`.
80 | - Added optional paramter for `ChatMessage`
81 | - Ability to have action buttons in ChatMessages.
82 | - Change Avatar `Boxfit.contain` to `Boxfit.cover`.
83 |
84 | ## [1.0.20]
85 |
86 | - `customProperties` property added to ChatMessgae class.
87 | - Color serialization error fixed.
88 |
89 | ## [1.0.19]
90 |
91 | - Fixed issue #51
92 | - fix scroll to top when inverted and send
93 |
94 | ## [1.0.18]
95 |
96 | - `inputTextDirection` property added.
97 |
98 | ## [1.0.17]
99 |
100 | - Ability to set custom focus node for input.
101 |
102 | - Removed SingleChildScrollView from root.
103 |
104 | - Added `WidgetsBinding.instance.addPostFrameCallback` to prevent scrolling before the messages are built.
105 |
106 | ## [1.0.16]
107 |
108 | - removed unnecessary print from the `message_listview`
109 |
110 | - changed 'vedio' field to 'video'
111 |
112 | - nullpointer on parsing quick replies; more detailed error reporting for ChatMessage parsing
113 |
114 | ## [1.0.15]
115 |
116 | - If `inverted` is true, it will no longer scroll.
117 |
118 | ## [1.0.14]
119 |
120 | - `textController` property added.
121 |
122 | ## [1.0.13]
123 |
124 | - `textCapitalization` property null error.
125 |
126 | ## [1.0.12]
127 |
128 | - `textCapitalization` property added for Input Toolbar.
129 |
130 | ## [1.0.11]
131 |
132 | - Fixed an issue where `dateFormat` property was not working.
133 | - Fixed an issue where input value was not cleared.
134 | - Fixed an issue where the whole screen incluing the input was scrollable.
135 | - Few improvemnets to Bottom Scroll Button.
136 |
137 | ## [1.0.10]
138 |
139 | - Updated dependency intl to `0.16.0`.
140 |
141 | ## [1.0.9]
142 |
143 | - ScrollController not attached issue fixed.
144 |
145 | ## [1.0.8]
146 |
147 | - Made the default avatar responsive for the current user.
148 |
149 | ## [1.0.7]
150 |
151 | - Made the default avatar responsive.
152 | - Two new property `inputToolbarPadding` & `inputToolbarMargin` added.
153 |
154 | ## [1.0.6]
155 |
156 | - Change the default message container size to fix the overflowing of text.
157 | - Added a new feature to close the keyboard if tap anywhere else on the screen.
158 |
159 | ## [1.0.5]
160 |
161 | - Fixed an issue where `scrollToBottom` widget was not disabled.
162 | - Fixed an issue where message container was not resized properly based on screen width.
163 | - Fixed a performance issue when scroll down.
164 | - Fixed an issue where `onLoadEarlier` was being called every time the listview moved.
165 |
166 | ## [1.0.4]
167 |
168 | - Models updated
169 |
170 | ## [1.0.3]
171 |
172 | - Small fixes to the InputToolbar and ChatMessage class
173 |
174 | ## [1.0.2]
175 |
176 | - ChatMessage serialization added
177 |
178 | ## [1.0.1]
179 |
180 | - Description updated and small fix
181 |
182 | ## [1.0.0]
183 |
184 | - Minor fixes and first release
185 |
186 | ## [0.1.6]
187 |
188 | - LoadEarlier Widget functionality added
189 |
190 | ## [0.1.5]
191 |
192 | - ScrollToBottom Widget functionality added
193 |
194 | ## [0.1.4]
195 |
196 | - `showTraillingBeforeSend` property added
197 |
198 | ## [0.1.3]
199 |
200 | - Quick Reply functionality added.
201 |
202 | ## [0.1.2]
203 |
204 | - Chat Bubble are more customizable now.
205 |
206 | ## [0.1.1]
207 |
208 | - User avatar now show user initials while loading & if not avatar is passed
209 |
210 | ## [0.1.0]
211 |
212 | - Initial Release
213 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Fayeed Pawaskar
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
⚠️ V1 is not maintained anymore, check the V2 on: https://github.com/molteo-engineering-team/Dash-Chat-2 ⚠️
3 |
4 |
5 |
6 |
7 |
💬 Dash Chat
8 | The most complete Chat UI for flutter
9 | Inspired by react-native-gifted-chat.
10 | Highly customizable and helps developing chat UI faster.
11 |
12 |
13 |
14 | ## Usage 💻
15 |
16 | To use this package, add `dash_chat` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/).
17 |
18 | ## Features 🔮
19 |
20 | - Fully customizable components
21 | - Copy messages to clipboard
22 | - Multi-line TextInput
23 | - Touchable links using [flutter_parsed_text](https://pub.dev/packages/flutter_parsed_text)
24 | - Avatar as user's initials
25 | - Quick Reply messages
26 | - Load earlier messages
27 | - Scroll to bottom Widget
28 | - Composer actions (to attach photos, etc.) - WIP
29 |
30 | ### Message object 📦
31 |
32 | > example, Chat Message
33 |
34 | ```dart
35 | ChatMessage(
36 | text: "Hello",
37 | user: ChatUser(
38 | name: "Fayeed",
39 | uid: "123456789",
40 | avatar: "https://www.wrappixel.com/ampleadmin/assets/images/users/4.jpg",
41 | ),
42 | createdAt: DateTime.now(),
43 | image: "http://www.sclance.com/images/picture/Picture_753248.jpg",
44 | );
45 | ```
46 |
47 | > example, Chat Message with Quick Replies
48 |
49 | ```dart
50 | ChatMessage(
51 | text: "This is a quick reply example.",
52 | user: ChatUser(),
53 | createdAt: DateTime.now(),
54 | quickReplies: QuickReplies(
55 | values: [
56 | Reply(
57 | title: "😋 Yes",
58 | value: "Yes",
59 | ),
60 | Reply(
61 | title: "😞 Nope. What?",
62 | value: "no",
63 | ),
64 | ],
65 | ),
66 | ),
67 | ```
68 |
69 | ## Parameters ⚙️
70 |
71 | - `messageContainerFlex` (int) - Flex value for the messeage container defaults to `1`
72 | - `height` (double) - Height for the Dash chat Widget.
73 | - `width` (double) - Width for the Dash chat Widget.
74 | - `messages` (List) - List of messages to display in the chat.
75 | - `text` (String) - [optional parameter] If provided will stop using the default controller.
76 | - `onTextChange` (Function(String)) - If the text parameter is passed then onTextChange must also be passed.
77 | - `inputDecoration` (InputDecoration) - Used to provide input decoration to the text.
78 | - `messageIdGenerator` (String Function) - Usually new message added by the user gets UUID v4 String generater by [uuid](https://pub.dev/packages/uuid).
79 | - `user` (ChatUser) - The current user object.
80 |
81 | ```dart
82 | DashChat(
83 | user: ChatUser(
84 | name: "Jhon Doe",
85 | uid: "xxxxxxxxx",
86 | avatar: "https://www.wrappixel.com/ampleadmin/assets/images/users/4.jpg",
87 | )
88 | );
89 | ```
90 |
91 | - `onSend` (Function(ChatMessage)) - Callback when sending a message.
92 | - `alwaysShowSend` (bool) - Should the send button be always active defaults to false.
93 | - `avatarMaxSize` (double) - Sets the default Avatar's max size, default is `30.0`.
94 | - `dateFormat` (DateFormat) - Format to use for rendering date default is `yyyy-MM-dd`.
95 | - `timeFormat` (DateFormat) - Format to use for rendering time default is `HH:mm:ss`.
96 | - `showUserAvatar` (bool) - Should the user avatar be shown.
97 | - `showAvatarForEveryMessage` (bool) - Should the avatar be shown for every message defaults to false.
98 | - `onPressAvatar` (Function(ChatUser)) - Callback funtion when avatar is tapped on.
99 | - `onLongPressAvatar` (Function(ChatUser)) - Callback funtion when avatar is long pressed on.
100 | - `onLongPressMessage` (Function(ChatUser)) - Callback funtion when message is long pressed on.
101 | - `inverted` (bool) - Should the messages be shown in reversed order
102 | - `avatarBuilder` (Widget Function(ChatUser)) - Will override the the default avatar.
103 | - `messageBuilder` (Widget Function(ChatMessage)) - Will override the the default message widget.
104 | - `messageTextBuilder` (Widget Function(String)) - Will override the the default message text widget.
105 | - `messageImageBuilder` (Widget Function(String)) - Will override the the default message imaeg widget
106 | - `messageTimeBuilder` (Widget Function(String)) - Will override the the default message time widget.
107 | - `dateBuilder` (Widget Function(String)) - Will override the the default chat view date widget.
108 | - `sendButtonBuilder` (Widget Function(Function)) - Will override the the default send button widget.
109 | - `chatFooterBuilder` (Widget Function) - A Widget that will be shown below the MessageListView like you can a "tying..." Text Widget at the end.
110 | - `inputFooterBuilder` (Widget Function) - A Widget that will be shown below the ChatInputToolbar.
111 | - `maxInputLength` (int) - Main input length of the input text box defaulst to no limit.
112 | - `parsePatterns` (List) - Used to parse text to make a linkified text uses [flutter_parsed_text](https://pub.dev/packages/flutter_parsed_text).
113 |
114 | ```dart
115 | DashChat(
116 | parsePatterns: [
117 | MatchText(
118 | type: "email",
119 | onTap: (String value) {}
120 | ),
121 | MatchText(
122 | pattern: r"\B#+([\w]+)\b",
123 | style: TextStyle(
124 | color: Colors.pink,
125 | fontSize: 24,
126 | ),
127 | onTap: (String value) {}
128 | ),
129 | ]
130 | );
131 | ```
132 |
133 | - `messageContainerDecoration` (BoxDecoration) - Provides a custom style to the message container.
134 | - `leading` (List) - List of Widget to show before the TextField.
135 | - `trailing` (List) - List of Widget to show after the TextField will remove the send button.
136 | - `readOnly` (bool) - Hides the input bar, defaults to `false`.
137 | - `showTraillingBeforeSend` - Should the trailling widgets be shown before the send button defaults to `true`.
138 | - `inputTextStyle` (TextStyle) - Style for the TextField.
139 | - `inputContainerStyle` (BoxDecoration) - TextField container style.
140 | - `inputMaxLines` (int) - Max length of the input lines default to 1.
141 | - `showInputCursor` (bool) - Should the input cursor be shown defaults to true.
142 | - `inputCursorWidth` (double) - Width of the text input defaults to 2.0.
143 | - `inputCursorColor` (Color) - Color of the input cursor defaults to theme.
144 | - `scrollController` (ScrollController) - ScrollController for the MessageListView.
145 | - `messageContainerPadding` (EdgeInsetsGeometry) - Padding for the MessageListView.
146 | - `messagePadding` (EdgeInsets) - Padding for the MessageContainer.
147 | - `onQuickReply` (Funtion(Reply)) - Callback method when the quickReply was tapped on.
148 | - `quickReplyStyle` (BoxDecoration) - Container style for the QuickReply Container.
149 | - `quickReplyTextStyle` (TextStyle) - QuickReply text style.
150 | - `quickReplyBuilder` (Widget Function(Reply)) - Will override the the default QuickReply Widget.
151 | - `scrollToBottom` (bool) - Should the scroll to bottom widget be shown defaults to `true`.
152 | - `scrollToBottomStyle` (ScrollToBottomStyle) - sets the style & position for the scrollToBottom widget.
153 | - `scrollToBottomWidget` (Widget Function()) - Overrides the default scrollToBottomWidget with a custom widget.
154 | - `onScrollToBottomPress` (Function) - override the default behaviour of the onScrollToBottom Widget.
155 | - `shouldShowLoadEarlier` (bool) - Should the LoadEarlier Floating widget be shown or use load as you scroll scheme whcih will call the onLoadEarlier defaults to false which is this scheme.
156 | - `showLoadEarlierWidget` (Widget Function()) - Override the default behaviour of the onScrollToBottom Widget.
157 | - `onLoadEarlier` (Function) - Override the default behaviour of the onLoadEarleir Widget or used as a callback when the listView reaches the top.
158 | - `inputToolbarPadding` (EdgeInsets) - Can be used to add padding to the input toolbar.
159 | - `inputToolbarMargin` (EdgeInsets) - Can be used to add margin to the input toolbar.
160 | - `shouldStartMessagesFromTop` (bool) - Can be used to align messages so that it starts from top to bottom instead of the default bottom to top.
161 | - `textBeforeImage` (bool) - Can be used to set the order of the text and the image inside a message defaults to `true`.
162 | - `quickReplyScroll` (bool) - Should the quick reply options be horizontally scrollable
163 | - `quickReplyPadding` (EdgeInsetsGeometry) - Padding for QuickReply
164 | - `inputDisabled` (bool) - Should the input TextField be disabled, defaults to `false`
165 | - `messageDecorationBuilder` (BoxDecoration Function(ChatMessage, isUser) - Override the message container decoration. [Note: This will override the messageContainerDecoration ]
166 |
167 | ```dart
168 | DashChat(
169 | ...
170 | messageDecorationBuilder: (ChatMessage msg, bool isUser){
171 | return BoxDecoration(
172 | ...
173 | color: isUser ? Colors.red : Colors.blue, // example
174 | ...
175 | );
176 | },
177 | ...
178 | );
179 | ```
180 |
181 | ## Credits 👨🏻💻
182 |
183 | - Transparent Image - [Brian Egan](https://github.com/brianegan)
184 | - uuid - [Yulian Kuncheff](https://github.com/Daegalus)
185 |
186 | ## Found this project useful? ❤️
187 |
188 | If you found this project useful, then please consider giving it a ⭐️ on Github and sharing it with your friends via social media.
189 |
190 | ## License ⚖️
191 |
192 | - [MIT](https://github.com/fayeed/dash_chat/blob/master/LICENSE)
193 |
194 | ## API details 📝
195 |
196 | See the [dash_chat.dart](https://github.com/fayeed/dash_chat/blob/master/lib/dash_chat.dart) for more API details
197 |
198 | ## Issues and feedback 💭
199 |
200 | If you have any suggestion for including a feature or if something doesn't work, feel free to open a Github [issue](https://github.com/fayeed/dash_chat/issues) for us to have a discussion on it.
201 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | /build/
32 |
33 | # Android related
34 | **/android/**/gradle-wrapper.jar
35 | **/android/.gradle
36 | **/android/captures/
37 | **/android/gradlew
38 | **/android/gradlew.bat
39 | **/android/local.properties
40 | **/android/**/GeneratedPluginRegistrant.java
41 |
42 | # iOS/XCode related
43 | **/ios/**/*.mode1v3
44 | **/ios/**/*.mode2v3
45 | **/ios/**/*.moved-aside
46 | **/ios/**/*.pbxuser
47 | **/ios/**/*.perspectivev3
48 | **/ios/**/*sync/
49 | **/ios/**/.sconsign.dblite
50 | **/ios/**/.tags*
51 | **/ios/**/.vagrant/
52 | **/ios/**/DerivedData/
53 | **/ios/**/Icon?
54 | **/ios/**/Pods/
55 | **/ios/**/.symlinks/
56 | **/ios/**/profile
57 | **/ios/**/xcuserdata
58 | **/ios/.generated/
59 | **/ios/Flutter/App.framework
60 | **/ios/Flutter/Flutter.framework
61 | **/ios/Flutter/Generated.xcconfig
62 | **/ios/Flutter/Flutter.podspec
63 | **/ios/Flutter/flutter_export_environment.sh
64 | **/ios/Flutter/app.flx
65 | **/ios/Flutter/app.zip
66 | **/ios/Flutter/flutter_assets/
67 | **/ios/ServiceDefinitions.json
68 | **/ios/Runner/GeneratedPluginRegistrant.*
69 |
70 | # Exceptions to above rules.
71 | !**/ios/**/default.mode1v3
72 | !**/ios/**/default.mode2v3
73 | !**/ios/**/default.pbxuser
74 | !**/ios/**/default.perspectivev3
75 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
76 |
77 | google-services.json
78 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: b712a172f9694745f50505c93340883493b505e5
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # example
2 |
3 | A new Flutter project.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
26 |
27 | android {
28 | compileSdkVersion 28
29 |
30 | lintOptions {
31 | disable 'InvalidPackage'
32 | }
33 |
34 | defaultConfig {
35 | multiDexEnabled true
36 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
37 | applicationId "com.example.dash_chat_tutorial"
38 | minSdkVersion 16
39 | targetSdkVersion 28
40 | versionCode flutterVersionCode.toInteger()
41 | versionName flutterVersionName
42 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
43 | }
44 |
45 | buildTypes {
46 | release {
47 | // TODO: Add your own signing config for the release build.
48 | // Signing with the debug keys for now, so `flutter run --release` works.
49 | signingConfig signingConfigs.debug
50 | }
51 | }
52 | }
53 |
54 | flutter {
55 | source '../..'
56 | }
57 |
58 | dependencies {
59 | testImplementation 'junit:junit:4.12'
60 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
61 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
62 | implementation 'com.google.firebase:firebase-core:17.0.0'
63 | }
64 |
65 | apply plugin: 'com.google.gms.google-services'
66 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
13 |
20 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/example/example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.dash_chat_tutorial;
2 |
3 | import android.os.Bundle;
4 | import io.flutter.app.FlutterActivity;
5 | import io.flutter.plugins.GeneratedPluginRegistrant;
6 |
7 | public class MainActivity extends FlutterActivity {
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | GeneratedPluginRegistrant.registerWith(this);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.2.1'
9 | classpath 'com.google.gms:google-services:4.2.0'
10 | }
11 | }
12 |
13 | allprojects {
14 | repositories {
15 | google()
16 | jcenter()
17 | }
18 | }
19 |
20 | rootProject.buildDir = '../build'
21 | subprojects {
22 | project.buildDir = "${rootProject.buildDir}/${project.name}"
23 | }
24 | subprojects {
25 | project.evaluationDependsOn(':app')
26 | }
27 |
28 | task clean(type: Delete) {
29 | delete rootProject.buildDir
30 | }
31 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
7 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
32 | end
33 |
34 | post_install do |installer|
35 | installer.pods_project.targets.each do |target|
36 | flutter_additional_ios_build_settings(target)
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - abseil/algorithm (0.20200225.0):
3 | - abseil/algorithm/algorithm (= 0.20200225.0)
4 | - abseil/algorithm/container (= 0.20200225.0)
5 | - abseil/algorithm/algorithm (0.20200225.0):
6 | - abseil/base/config
7 | - abseil/algorithm/container (0.20200225.0):
8 | - abseil/algorithm/algorithm
9 | - abseil/base/core_headers
10 | - abseil/meta/type_traits
11 | - abseil/base (0.20200225.0):
12 | - abseil/base/atomic_hook (= 0.20200225.0)
13 | - abseil/base/base (= 0.20200225.0)
14 | - abseil/base/base_internal (= 0.20200225.0)
15 | - abseil/base/bits (= 0.20200225.0)
16 | - abseil/base/config (= 0.20200225.0)
17 | - abseil/base/core_headers (= 0.20200225.0)
18 | - abseil/base/dynamic_annotations (= 0.20200225.0)
19 | - abseil/base/endian (= 0.20200225.0)
20 | - abseil/base/errno_saver (= 0.20200225.0)
21 | - abseil/base/exponential_biased (= 0.20200225.0)
22 | - abseil/base/log_severity (= 0.20200225.0)
23 | - abseil/base/malloc_internal (= 0.20200225.0)
24 | - abseil/base/periodic_sampler (= 0.20200225.0)
25 | - abseil/base/pretty_function (= 0.20200225.0)
26 | - abseil/base/raw_logging_internal (= 0.20200225.0)
27 | - abseil/base/spinlock_wait (= 0.20200225.0)
28 | - abseil/base/throw_delegate (= 0.20200225.0)
29 | - abseil/base/atomic_hook (0.20200225.0):
30 | - abseil/base/config
31 | - abseil/base/core_headers
32 | - abseil/base/base (0.20200225.0):
33 | - abseil/base/atomic_hook
34 | - abseil/base/base_internal
35 | - abseil/base/config
36 | - abseil/base/core_headers
37 | - abseil/base/dynamic_annotations
38 | - abseil/base/log_severity
39 | - abseil/base/raw_logging_internal
40 | - abseil/base/spinlock_wait
41 | - abseil/meta/type_traits
42 | - abseil/base/base_internal (0.20200225.0):
43 | - abseil/base/config
44 | - abseil/meta/type_traits
45 | - abseil/base/bits (0.20200225.0):
46 | - abseil/base/config
47 | - abseil/base/core_headers
48 | - abseil/base/config (0.20200225.0)
49 | - abseil/base/core_headers (0.20200225.0):
50 | - abseil/base/config
51 | - abseil/base/dynamic_annotations (0.20200225.0)
52 | - abseil/base/endian (0.20200225.0):
53 | - abseil/base/config
54 | - abseil/base/core_headers
55 | - abseil/base/errno_saver (0.20200225.0):
56 | - abseil/base/config
57 | - abseil/base/exponential_biased (0.20200225.0):
58 | - abseil/base/config
59 | - abseil/base/core_headers
60 | - abseil/base/log_severity (0.20200225.0):
61 | - abseil/base/config
62 | - abseil/base/core_headers
63 | - abseil/base/malloc_internal (0.20200225.0):
64 | - abseil/base/base
65 | - abseil/base/base_internal
66 | - abseil/base/config
67 | - abseil/base/core_headers
68 | - abseil/base/dynamic_annotations
69 | - abseil/base/raw_logging_internal
70 | - abseil/base/periodic_sampler (0.20200225.0):
71 | - abseil/base/core_headers
72 | - abseil/base/exponential_biased
73 | - abseil/base/pretty_function (0.20200225.0)
74 | - abseil/base/raw_logging_internal (0.20200225.0):
75 | - abseil/base/atomic_hook
76 | - abseil/base/config
77 | - abseil/base/core_headers
78 | - abseil/base/log_severity
79 | - abseil/base/spinlock_wait (0.20200225.0):
80 | - abseil/base/base_internal
81 | - abseil/base/core_headers
82 | - abseil/base/errno_saver
83 | - abseil/base/throw_delegate (0.20200225.0):
84 | - abseil/base/config
85 | - abseil/base/raw_logging_internal
86 | - abseil/container/compressed_tuple (0.20200225.0):
87 | - abseil/utility/utility
88 | - abseil/container/inlined_vector (0.20200225.0):
89 | - abseil/algorithm/algorithm
90 | - abseil/base/core_headers
91 | - abseil/base/throw_delegate
92 | - abseil/container/inlined_vector_internal
93 | - abseil/memory/memory
94 | - abseil/container/inlined_vector_internal (0.20200225.0):
95 | - abseil/base/core_headers
96 | - abseil/container/compressed_tuple
97 | - abseil/memory/memory
98 | - abseil/meta/type_traits
99 | - abseil/types/span
100 | - abseil/memory (0.20200225.0):
101 | - abseil/memory/memory (= 0.20200225.0)
102 | - abseil/memory/memory (0.20200225.0):
103 | - abseil/base/core_headers
104 | - abseil/meta/type_traits
105 | - abseil/meta (0.20200225.0):
106 | - abseil/meta/type_traits (= 0.20200225.0)
107 | - abseil/meta/type_traits (0.20200225.0):
108 | - abseil/base/config
109 | - abseil/numeric/int128 (0.20200225.0):
110 | - abseil/base/config
111 | - abseil/base/core_headers
112 | - abseil/strings/internal (0.20200225.0):
113 | - abseil/base/config
114 | - abseil/base/core_headers
115 | - abseil/base/endian
116 | - abseil/base/raw_logging_internal
117 | - abseil/meta/type_traits
118 | - abseil/strings/str_format (0.20200225.0):
119 | - abseil/strings/str_format_internal
120 | - abseil/strings/str_format_internal (0.20200225.0):
121 | - abseil/base/config
122 | - abseil/base/core_headers
123 | - abseil/meta/type_traits
124 | - abseil/numeric/int128
125 | - abseil/strings/strings
126 | - abseil/types/span
127 | - abseil/strings/strings (0.20200225.0):
128 | - abseil/base/base
129 | - abseil/base/bits
130 | - abseil/base/config
131 | - abseil/base/core_headers
132 | - abseil/base/endian
133 | - abseil/base/raw_logging_internal
134 | - abseil/base/throw_delegate
135 | - abseil/memory/memory
136 | - abseil/meta/type_traits
137 | - abseil/numeric/int128
138 | - abseil/strings/internal
139 | - abseil/time (0.20200225.0):
140 | - abseil/time/internal (= 0.20200225.0)
141 | - abseil/time/time (= 0.20200225.0)
142 | - abseil/time/internal (0.20200225.0):
143 | - abseil/time/internal/cctz (= 0.20200225.0)
144 | - abseil/time/internal/cctz (0.20200225.0):
145 | - abseil/time/internal/cctz/civil_time (= 0.20200225.0)
146 | - abseil/time/internal/cctz/time_zone (= 0.20200225.0)
147 | - abseil/time/internal/cctz/civil_time (0.20200225.0):
148 | - abseil/base/config
149 | - abseil/time/internal/cctz/time_zone (0.20200225.0):
150 | - abseil/base/config
151 | - abseil/time/internal/cctz/civil_time
152 | - abseil/time/time (0.20200225.0):
153 | - abseil/base/base
154 | - abseil/base/core_headers
155 | - abseil/base/raw_logging_internal
156 | - abseil/numeric/int128
157 | - abseil/strings/strings
158 | - abseil/time/internal/cctz/civil_time
159 | - abseil/time/internal/cctz/time_zone
160 | - abseil/types (0.20200225.0):
161 | - abseil/types/any (= 0.20200225.0)
162 | - abseil/types/bad_any_cast (= 0.20200225.0)
163 | - abseil/types/bad_any_cast_impl (= 0.20200225.0)
164 | - abseil/types/bad_optional_access (= 0.20200225.0)
165 | - abseil/types/bad_variant_access (= 0.20200225.0)
166 | - abseil/types/compare (= 0.20200225.0)
167 | - abseil/types/optional (= 0.20200225.0)
168 | - abseil/types/span (= 0.20200225.0)
169 | - abseil/types/variant (= 0.20200225.0)
170 | - abseil/types/any (0.20200225.0):
171 | - abseil/base/config
172 | - abseil/base/core_headers
173 | - abseil/meta/type_traits
174 | - abseil/types/bad_any_cast
175 | - abseil/utility/utility
176 | - abseil/types/bad_any_cast (0.20200225.0):
177 | - abseil/base/config
178 | - abseil/types/bad_any_cast_impl
179 | - abseil/types/bad_any_cast_impl (0.20200225.0):
180 | - abseil/base/config
181 | - abseil/base/raw_logging_internal
182 | - abseil/types/bad_optional_access (0.20200225.0):
183 | - abseil/base/config
184 | - abseil/base/raw_logging_internal
185 | - abseil/types/bad_variant_access (0.20200225.0):
186 | - abseil/base/config
187 | - abseil/base/raw_logging_internal
188 | - abseil/types/compare (0.20200225.0):
189 | - abseil/base/core_headers
190 | - abseil/meta/type_traits
191 | - abseil/types/optional (0.20200225.0):
192 | - abseil/base/base_internal
193 | - abseil/base/config
194 | - abseil/base/core_headers
195 | - abseil/memory/memory
196 | - abseil/meta/type_traits
197 | - abseil/types/bad_optional_access
198 | - abseil/utility/utility
199 | - abseil/types/span (0.20200225.0):
200 | - abseil/algorithm/algorithm
201 | - abseil/base/core_headers
202 | - abseil/base/throw_delegate
203 | - abseil/meta/type_traits
204 | - abseil/types/variant (0.20200225.0):
205 | - abseil/base/base_internal
206 | - abseil/base/config
207 | - abseil/base/core_headers
208 | - abseil/meta/type_traits
209 | - abseil/types/bad_variant_access
210 | - abseil/utility/utility
211 | - abseil/utility/utility (0.20200225.0):
212 | - abseil/base/base_internal
213 | - abseil/base/config
214 | - abseil/meta/type_traits
215 | - BoringSSL-GRPC (0.0.7):
216 | - BoringSSL-GRPC/Implementation (= 0.0.7)
217 | - BoringSSL-GRPC/Interface (= 0.0.7)
218 | - BoringSSL-GRPC/Implementation (0.0.7):
219 | - BoringSSL-GRPC/Interface (= 0.0.7)
220 | - BoringSSL-GRPC/Interface (0.0.7)
221 | - cloud_firestore (1.0.0):
222 | - Firebase/Firestore (= 7.3.0)
223 | - firebase_core
224 | - Flutter
225 | - Firebase/Auth (7.3.0):
226 | - Firebase/CoreOnly
227 | - FirebaseAuth (~> 7.3.0)
228 | - Firebase/CoreOnly (7.3.0):
229 | - FirebaseCore (= 7.3.0)
230 | - Firebase/Firestore (7.3.0):
231 | - Firebase/CoreOnly
232 | - FirebaseFirestore (~> 7.3.0)
233 | - Firebase/Storage (7.3.0):
234 | - Firebase/CoreOnly
235 | - FirebaseStorage (~> 7.3.0)
236 | - firebase_auth (1.0.0):
237 | - Firebase/Auth (= 7.3.0)
238 | - firebase_core
239 | - Flutter
240 | - firebase_core (1.0.0):
241 | - Firebase/CoreOnly (= 7.3.0)
242 | - Flutter
243 | - firebase_storage (8.0.0):
244 | - Firebase/Storage (= 7.3.0)
245 | - firebase_core
246 | - Flutter
247 | - FirebaseAuth (7.3.0):
248 | - FirebaseCore (~> 7.0)
249 | - GoogleUtilities/AppDelegateSwizzler (~> 7.0)
250 | - GoogleUtilities/Environment (~> 7.0)
251 | - GTMSessionFetcher/Core (~> 1.4)
252 | - FirebaseCore (7.3.0):
253 | - FirebaseCoreDiagnostics (~> 7.0)
254 | - GoogleUtilities/Environment (~> 7.0)
255 | - GoogleUtilities/Logger (~> 7.0)
256 | - FirebaseCoreDiagnostics (7.3.0):
257 | - GoogleDataTransport (~> 8.0)
258 | - GoogleUtilities/Environment (~> 7.0)
259 | - GoogleUtilities/Logger (~> 7.0)
260 | - nanopb (~> 2.30906.0)
261 | - FirebaseFirestore (7.3.0):
262 | - abseil/algorithm (= 0.20200225.0)
263 | - abseil/base (= 0.20200225.0)
264 | - abseil/memory (= 0.20200225.0)
265 | - abseil/meta (= 0.20200225.0)
266 | - abseil/strings/strings (= 0.20200225.0)
267 | - abseil/time (= 0.20200225.0)
268 | - abseil/types (= 0.20200225.0)
269 | - FirebaseCore (~> 7.0)
270 | - "gRPC-C++ (~> 1.28.0)"
271 | - leveldb-library (~> 1.22)
272 | - nanopb (~> 2.30906.0)
273 | - FirebaseStorage (7.3.0):
274 | - FirebaseCore (~> 7.0)
275 | - GTMSessionFetcher/Core (~> 1.4)
276 | - Flutter (1.0.0)
277 | - GoogleDataTransport (8.1.0):
278 | - nanopb (~> 2.30906.0)
279 | - GoogleUtilities/AppDelegateSwizzler (7.2.2):
280 | - GoogleUtilities/Environment
281 | - GoogleUtilities/Logger
282 | - GoogleUtilities/Network
283 | - GoogleUtilities/Environment (7.2.2):
284 | - PromisesObjC (~> 1.2)
285 | - GoogleUtilities/Logger (7.2.2):
286 | - GoogleUtilities/Environment
287 | - GoogleUtilities/Network (7.2.2):
288 | - GoogleUtilities/Logger
289 | - "GoogleUtilities/NSData+zlib"
290 | - GoogleUtilities/Reachability
291 | - "GoogleUtilities/NSData+zlib (7.2.2)"
292 | - GoogleUtilities/Reachability (7.2.2):
293 | - GoogleUtilities/Logger
294 | - "gRPC-C++ (1.28.2)":
295 | - "gRPC-C++/Implementation (= 1.28.2)"
296 | - "gRPC-C++/Interface (= 1.28.2)"
297 | - "gRPC-C++/Implementation (1.28.2)":
298 | - abseil/container/inlined_vector (= 0.20200225.0)
299 | - abseil/memory/memory (= 0.20200225.0)
300 | - abseil/strings/str_format (= 0.20200225.0)
301 | - abseil/strings/strings (= 0.20200225.0)
302 | - abseil/types/optional (= 0.20200225.0)
303 | - "gRPC-C++/Interface (= 1.28.2)"
304 | - gRPC-Core (= 1.28.2)
305 | - "gRPC-C++/Interface (1.28.2)"
306 | - gRPC-Core (1.28.2):
307 | - gRPC-Core/Implementation (= 1.28.2)
308 | - gRPC-Core/Interface (= 1.28.2)
309 | - gRPC-Core/Implementation (1.28.2):
310 | - abseil/container/inlined_vector (= 0.20200225.0)
311 | - abseil/memory/memory (= 0.20200225.0)
312 | - abseil/strings/str_format (= 0.20200225.0)
313 | - abseil/strings/strings (= 0.20200225.0)
314 | - abseil/types/optional (= 0.20200225.0)
315 | - BoringSSL-GRPC (= 0.0.7)
316 | - gRPC-Core/Interface (= 1.28.2)
317 | - gRPC-Core/Interface (1.28.2)
318 | - GTMSessionFetcher/Core (1.5.0)
319 | - image_picker (0.0.1):
320 | - Flutter
321 | - leveldb-library (1.22)
322 | - nanopb (2.30906.0):
323 | - nanopb/decode (= 2.30906.0)
324 | - nanopb/encode (= 2.30906.0)
325 | - nanopb/decode (2.30906.0)
326 | - nanopb/encode (2.30906.0)
327 | - PromisesObjC (1.2.12)
328 |
329 | DEPENDENCIES:
330 | - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`)
331 | - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
332 | - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
333 | - firebase_storage (from `.symlinks/plugins/firebase_storage/ios`)
334 | - Flutter (from `Flutter`)
335 | - image_picker (from `.symlinks/plugins/image_picker/ios`)
336 |
337 | SPEC REPOS:
338 | trunk:
339 | - abseil
340 | - BoringSSL-GRPC
341 | - Firebase
342 | - FirebaseAuth
343 | - FirebaseCore
344 | - FirebaseCoreDiagnostics
345 | - FirebaseFirestore
346 | - FirebaseStorage
347 | - GoogleDataTransport
348 | - GoogleUtilities
349 | - "gRPC-C++"
350 | - gRPC-Core
351 | - GTMSessionFetcher
352 | - leveldb-library
353 | - nanopb
354 | - PromisesObjC
355 |
356 | EXTERNAL SOURCES:
357 | cloud_firestore:
358 | :path: ".symlinks/plugins/cloud_firestore/ios"
359 | firebase_auth:
360 | :path: ".symlinks/plugins/firebase_auth/ios"
361 | firebase_core:
362 | :path: ".symlinks/plugins/firebase_core/ios"
363 | firebase_storage:
364 | :path: ".symlinks/plugins/firebase_storage/ios"
365 | Flutter:
366 | :path: Flutter
367 | image_picker:
368 | :path: ".symlinks/plugins/image_picker/ios"
369 |
370 | SPEC CHECKSUMS:
371 | abseil: 6c8eb7892aefa08d929b39f9bb108e5367e3228f
372 | BoringSSL-GRPC: 8edf627ee524575e2f8d19d56f068b448eea3879
373 | cloud_firestore: efd6d2bafe5147690cff2ccfbde13752a56fa281
374 | Firebase: 26223c695fe322633274198cb19dca8cb7e54416
375 | firebase_auth: f07f3ee03813d95157ee08ce763c649c656174b5
376 | firebase_core: f47224dd6a9b928b4cf0128b0d004ec9e47d4bf2
377 | firebase_storage: a4b292db6551a64c3697c7e67cb1df524b494bb1
378 | FirebaseAuth: c224a0cf1afa0949bd5c7bfcf154b4f5ce8ddef2
379 | FirebaseCore: 4d3c72622ce0e2106aaa07bb4b2935ba2c370972
380 | FirebaseCoreDiagnostics: d50e11039e5984d92c8a512be2395f13df747350
381 | FirebaseFirestore: 1906bf163afdb7c432d2e3b5c40ceb9dd2df5820
382 | FirebaseStorage: 5002b1895bfe74a5ce92ad54f966e6162d0da2e5
383 | Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
384 | GoogleDataTransport: 116c84c4bdeb76be2a7a46de51244368f9794eab
385 | GoogleUtilities: 31c5b01f978a70c6cff2afc6272b3f1921614b43
386 | "gRPC-C++": 13d8ccef97d5c3c441b7e3c529ef28ebee86fad2
387 | gRPC-Core: 4afa11bfbedf7cdecd04de535a9e046893404ed5
388 | GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52
389 | image_picker: 50e7c7ff960e5f58faa4d1f4af84a771c671bc4a
390 | leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7
391 | nanopb: 1bf24dd71191072e120b83dd02d08f3da0d65e53
392 | PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97
393 |
394 | PODFILE CHECKSUM: 8e679eca47255a8ca8067c4c67aab20e64cb974d
395 |
396 | COCOAPODS: 1.10.1
397 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
12 | 539E7D3BB856F39CFC5E6F0A /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 91C5579ABB0217FD2FE9BB78 /* libPods-Runner.a */; };
13 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
14 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
15 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
16 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
17 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
18 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXCopyFilesBuildPhase section */
22 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
23 | isa = PBXCopyFilesBuildPhase;
24 | buildActionMask = 2147483647;
25 | dstPath = "";
26 | dstSubfolderSpec = 10;
27 | files = (
28 | );
29 | name = "Embed Frameworks";
30 | runOnlyForDeploymentPostprocessing = 0;
31 | };
32 | /* End PBXCopyFilesBuildPhase section */
33 |
34 | /* Begin PBXFileReference section */
35 | 0470F5D4093E14FE0D9F4E81 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
36 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
37 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
39 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
40 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
41 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
42 | 84F5C0981ED41284C09D3BBE /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
43 | 91C5579ABB0217FD2FE9BB78 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
45 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
46 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
47 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
48 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
49 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
50 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
51 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
52 | B0E62A5AC5A6386CABAC0F0B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
53 | /* End PBXFileReference section */
54 |
55 | /* Begin PBXFrameworksBuildPhase section */
56 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
57 | isa = PBXFrameworksBuildPhase;
58 | buildActionMask = 2147483647;
59 | files = (
60 | 539E7D3BB856F39CFC5E6F0A /* libPods-Runner.a in Frameworks */,
61 | );
62 | runOnlyForDeploymentPostprocessing = 0;
63 | };
64 | /* End PBXFrameworksBuildPhase section */
65 |
66 | /* Begin PBXGroup section */
67 | 477B12A96C18315B257286B0 /* Frameworks */ = {
68 | isa = PBXGroup;
69 | children = (
70 | 91C5579ABB0217FD2FE9BB78 /* libPods-Runner.a */,
71 | );
72 | name = Frameworks;
73 | sourceTree = "";
74 | };
75 | 9740EEB11CF90186004384FC /* Flutter */ = {
76 | isa = PBXGroup;
77 | children = (
78 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
79 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
80 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
81 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
82 | );
83 | name = Flutter;
84 | sourceTree = "";
85 | };
86 | 97C146E51CF9000F007C117D = {
87 | isa = PBXGroup;
88 | children = (
89 | 9740EEB11CF90186004384FC /* Flutter */,
90 | 97C146F01CF9000F007C117D /* Runner */,
91 | 97C146EF1CF9000F007C117D /* Products */,
92 | CF137BA4E5CC878B4465934D /* Pods */,
93 | 477B12A96C18315B257286B0 /* Frameworks */,
94 | );
95 | sourceTree = "";
96 | };
97 | 97C146EF1CF9000F007C117D /* Products */ = {
98 | isa = PBXGroup;
99 | children = (
100 | 97C146EE1CF9000F007C117D /* Runner.app */,
101 | );
102 | name = Products;
103 | sourceTree = "";
104 | };
105 | 97C146F01CF9000F007C117D /* Runner */ = {
106 | isa = PBXGroup;
107 | children = (
108 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
109 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
110 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
111 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
112 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
113 | 97C147021CF9000F007C117D /* Info.plist */,
114 | 97C146F11CF9000F007C117D /* Supporting Files */,
115 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
116 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
117 | );
118 | path = Runner;
119 | sourceTree = "";
120 | };
121 | 97C146F11CF9000F007C117D /* Supporting Files */ = {
122 | isa = PBXGroup;
123 | children = (
124 | 97C146F21CF9000F007C117D /* main.m */,
125 | );
126 | name = "Supporting Files";
127 | sourceTree = "";
128 | };
129 | CF137BA4E5CC878B4465934D /* Pods */ = {
130 | isa = PBXGroup;
131 | children = (
132 | 84F5C0981ED41284C09D3BBE /* Pods-Runner.debug.xcconfig */,
133 | B0E62A5AC5A6386CABAC0F0B /* Pods-Runner.release.xcconfig */,
134 | 0470F5D4093E14FE0D9F4E81 /* Pods-Runner.profile.xcconfig */,
135 | );
136 | name = Pods;
137 | path = Pods;
138 | sourceTree = "";
139 | };
140 | /* End PBXGroup section */
141 |
142 | /* Begin PBXNativeTarget section */
143 | 97C146ED1CF9000F007C117D /* Runner */ = {
144 | isa = PBXNativeTarget;
145 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
146 | buildPhases = (
147 | 85147C71D178058E833A0F76 /* [CP] Check Pods Manifest.lock */,
148 | 9740EEB61CF901F6004384FC /* Run Script */,
149 | 97C146EA1CF9000F007C117D /* Sources */,
150 | 97C146EB1CF9000F007C117D /* Frameworks */,
151 | 97C146EC1CF9000F007C117D /* Resources */,
152 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
153 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
154 | AAB7082484EC94289E6708A9 /* [CP] Copy Pods Resources */,
155 | );
156 | buildRules = (
157 | );
158 | dependencies = (
159 | );
160 | name = Runner;
161 | productName = Runner;
162 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
163 | productType = "com.apple.product-type.application";
164 | };
165 | /* End PBXNativeTarget section */
166 |
167 | /* Begin PBXProject section */
168 | 97C146E61CF9000F007C117D /* Project object */ = {
169 | isa = PBXProject;
170 | attributes = {
171 | LastUpgradeCheck = 1020;
172 | ORGANIZATIONNAME = "The Chromium Authors";
173 | TargetAttributes = {
174 | 97C146ED1CF9000F007C117D = {
175 | CreatedOnToolsVersion = 7.3.1;
176 | };
177 | };
178 | };
179 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
180 | compatibilityVersion = "Xcode 3.2";
181 | developmentRegion = en;
182 | hasScannedForEncodings = 0;
183 | knownRegions = (
184 | en,
185 | Base,
186 | );
187 | mainGroup = 97C146E51CF9000F007C117D;
188 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
189 | projectDirPath = "";
190 | projectRoot = "";
191 | targets = (
192 | 97C146ED1CF9000F007C117D /* Runner */,
193 | );
194 | };
195 | /* End PBXProject section */
196 |
197 | /* Begin PBXResourcesBuildPhase section */
198 | 97C146EC1CF9000F007C117D /* Resources */ = {
199 | isa = PBXResourcesBuildPhase;
200 | buildActionMask = 2147483647;
201 | files = (
202 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
203 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
204 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
205 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
206 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
207 | );
208 | runOnlyForDeploymentPostprocessing = 0;
209 | };
210 | /* End PBXResourcesBuildPhase section */
211 |
212 | /* Begin PBXShellScriptBuildPhase section */
213 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
214 | isa = PBXShellScriptBuildPhase;
215 | buildActionMask = 2147483647;
216 | files = (
217 | );
218 | inputPaths = (
219 | );
220 | name = "Thin Binary";
221 | outputPaths = (
222 | );
223 | runOnlyForDeploymentPostprocessing = 0;
224 | shellPath = /bin/sh;
225 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
226 | };
227 | 85147C71D178058E833A0F76 /* [CP] Check Pods Manifest.lock */ = {
228 | isa = PBXShellScriptBuildPhase;
229 | buildActionMask = 2147483647;
230 | files = (
231 | );
232 | inputFileListPaths = (
233 | );
234 | inputPaths = (
235 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
236 | "${PODS_ROOT}/Manifest.lock",
237 | );
238 | name = "[CP] Check Pods Manifest.lock";
239 | outputFileListPaths = (
240 | );
241 | outputPaths = (
242 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
243 | );
244 | runOnlyForDeploymentPostprocessing = 0;
245 | shellPath = /bin/sh;
246 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
247 | showEnvVarsInLog = 0;
248 | };
249 | 9740EEB61CF901F6004384FC /* Run Script */ = {
250 | isa = PBXShellScriptBuildPhase;
251 | buildActionMask = 2147483647;
252 | files = (
253 | );
254 | inputPaths = (
255 | );
256 | name = "Run Script";
257 | outputPaths = (
258 | );
259 | runOnlyForDeploymentPostprocessing = 0;
260 | shellPath = /bin/sh;
261 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
262 | };
263 | AAB7082484EC94289E6708A9 /* [CP] Copy Pods Resources */ = {
264 | isa = PBXShellScriptBuildPhase;
265 | buildActionMask = 2147483647;
266 | files = (
267 | );
268 | inputPaths = (
269 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
270 | "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-C++/gRPCCertificates-Cpp.bundle",
271 | );
272 | name = "[CP] Copy Pods Resources";
273 | outputPaths = (
274 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates-Cpp.bundle",
275 | );
276 | runOnlyForDeploymentPostprocessing = 0;
277 | shellPath = /bin/sh;
278 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
279 | showEnvVarsInLog = 0;
280 | };
281 | /* End PBXShellScriptBuildPhase section */
282 |
283 | /* Begin PBXSourcesBuildPhase section */
284 | 97C146EA1CF9000F007C117D /* Sources */ = {
285 | isa = PBXSourcesBuildPhase;
286 | buildActionMask = 2147483647;
287 | files = (
288 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
289 | 97C146F31CF9000F007C117D /* main.m in Sources */,
290 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
291 | );
292 | runOnlyForDeploymentPostprocessing = 0;
293 | };
294 | /* End PBXSourcesBuildPhase section */
295 |
296 | /* Begin PBXVariantGroup section */
297 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
298 | isa = PBXVariantGroup;
299 | children = (
300 | 97C146FB1CF9000F007C117D /* Base */,
301 | );
302 | name = Main.storyboard;
303 | sourceTree = "";
304 | };
305 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
306 | isa = PBXVariantGroup;
307 | children = (
308 | 97C147001CF9000F007C117D /* Base */,
309 | );
310 | name = LaunchScreen.storyboard;
311 | sourceTree = "";
312 | };
313 | /* End PBXVariantGroup section */
314 |
315 | /* Begin XCBuildConfiguration section */
316 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
317 | isa = XCBuildConfiguration;
318 | buildSettings = {
319 | ALWAYS_SEARCH_USER_PATHS = NO;
320 | CLANG_ANALYZER_NONNULL = YES;
321 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
322 | CLANG_CXX_LIBRARY = "libc++";
323 | CLANG_ENABLE_MODULES = YES;
324 | CLANG_ENABLE_OBJC_ARC = YES;
325 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
326 | CLANG_WARN_BOOL_CONVERSION = YES;
327 | CLANG_WARN_COMMA = YES;
328 | CLANG_WARN_CONSTANT_CONVERSION = YES;
329 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
330 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
331 | CLANG_WARN_EMPTY_BODY = YES;
332 | CLANG_WARN_ENUM_CONVERSION = YES;
333 | CLANG_WARN_INFINITE_RECURSION = YES;
334 | CLANG_WARN_INT_CONVERSION = YES;
335 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
336 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
337 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
338 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
339 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
340 | CLANG_WARN_STRICT_PROTOTYPES = YES;
341 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
342 | CLANG_WARN_UNREACHABLE_CODE = YES;
343 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
344 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
345 | COPY_PHASE_STRIP = NO;
346 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
347 | ENABLE_NS_ASSERTIONS = NO;
348 | ENABLE_STRICT_OBJC_MSGSEND = YES;
349 | GCC_C_LANGUAGE_STANDARD = gnu99;
350 | GCC_NO_COMMON_BLOCKS = YES;
351 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
352 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
353 | GCC_WARN_UNDECLARED_SELECTOR = YES;
354 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
355 | GCC_WARN_UNUSED_FUNCTION = YES;
356 | GCC_WARN_UNUSED_VARIABLE = YES;
357 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
358 | MTL_ENABLE_DEBUG_INFO = NO;
359 | SDKROOT = iphoneos;
360 | TARGETED_DEVICE_FAMILY = "1,2";
361 | VALIDATE_PRODUCT = YES;
362 | };
363 | name = Profile;
364 | };
365 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
366 | isa = XCBuildConfiguration;
367 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
368 | buildSettings = {
369 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
370 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
371 | DEVELOPMENT_TEAM = S8QB4VV633;
372 | ENABLE_BITCODE = NO;
373 | FRAMEWORK_SEARCH_PATHS = (
374 | "$(inherited)",
375 | "$(PROJECT_DIR)/Flutter",
376 | );
377 | INFOPLIST_FILE = Runner/Info.plist;
378 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
379 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
380 | LIBRARY_SEARCH_PATHS = (
381 | "$(inherited)",
382 | "$(PROJECT_DIR)/Flutter",
383 | );
384 | PRODUCT_BUNDLE_IDENTIFIER = com.example.dash_chat_tutorial;
385 | PRODUCT_NAME = "$(TARGET_NAME)";
386 | VERSIONING_SYSTEM = "apple-generic";
387 | };
388 | name = Profile;
389 | };
390 | 97C147031CF9000F007C117D /* Debug */ = {
391 | isa = XCBuildConfiguration;
392 | buildSettings = {
393 | ALWAYS_SEARCH_USER_PATHS = NO;
394 | CLANG_ANALYZER_NONNULL = YES;
395 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
396 | CLANG_CXX_LIBRARY = "libc++";
397 | CLANG_ENABLE_MODULES = YES;
398 | CLANG_ENABLE_OBJC_ARC = YES;
399 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
400 | CLANG_WARN_BOOL_CONVERSION = YES;
401 | CLANG_WARN_COMMA = YES;
402 | CLANG_WARN_CONSTANT_CONVERSION = YES;
403 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
404 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
405 | CLANG_WARN_EMPTY_BODY = YES;
406 | CLANG_WARN_ENUM_CONVERSION = YES;
407 | CLANG_WARN_INFINITE_RECURSION = YES;
408 | CLANG_WARN_INT_CONVERSION = YES;
409 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
410 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
411 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
412 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
413 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
414 | CLANG_WARN_STRICT_PROTOTYPES = YES;
415 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
416 | CLANG_WARN_UNREACHABLE_CODE = YES;
417 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
418 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
419 | COPY_PHASE_STRIP = NO;
420 | DEBUG_INFORMATION_FORMAT = dwarf;
421 | ENABLE_STRICT_OBJC_MSGSEND = YES;
422 | ENABLE_TESTABILITY = YES;
423 | GCC_C_LANGUAGE_STANDARD = gnu99;
424 | GCC_DYNAMIC_NO_PIC = NO;
425 | GCC_NO_COMMON_BLOCKS = YES;
426 | GCC_OPTIMIZATION_LEVEL = 0;
427 | GCC_PREPROCESSOR_DEFINITIONS = (
428 | "DEBUG=1",
429 | "$(inherited)",
430 | );
431 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
432 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
433 | GCC_WARN_UNDECLARED_SELECTOR = YES;
434 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
435 | GCC_WARN_UNUSED_FUNCTION = YES;
436 | GCC_WARN_UNUSED_VARIABLE = YES;
437 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
438 | MTL_ENABLE_DEBUG_INFO = YES;
439 | ONLY_ACTIVE_ARCH = YES;
440 | SDKROOT = iphoneos;
441 | TARGETED_DEVICE_FAMILY = "1,2";
442 | };
443 | name = Debug;
444 | };
445 | 97C147041CF9000F007C117D /* Release */ = {
446 | isa = XCBuildConfiguration;
447 | buildSettings = {
448 | ALWAYS_SEARCH_USER_PATHS = NO;
449 | CLANG_ANALYZER_NONNULL = YES;
450 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
451 | CLANG_CXX_LIBRARY = "libc++";
452 | CLANG_ENABLE_MODULES = YES;
453 | CLANG_ENABLE_OBJC_ARC = YES;
454 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
455 | CLANG_WARN_BOOL_CONVERSION = YES;
456 | CLANG_WARN_COMMA = YES;
457 | CLANG_WARN_CONSTANT_CONVERSION = YES;
458 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
459 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
460 | CLANG_WARN_EMPTY_BODY = YES;
461 | CLANG_WARN_ENUM_CONVERSION = YES;
462 | CLANG_WARN_INFINITE_RECURSION = YES;
463 | CLANG_WARN_INT_CONVERSION = YES;
464 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
465 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
466 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
467 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
468 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
469 | CLANG_WARN_STRICT_PROTOTYPES = YES;
470 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
471 | CLANG_WARN_UNREACHABLE_CODE = YES;
472 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
473 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
474 | COPY_PHASE_STRIP = NO;
475 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
476 | ENABLE_NS_ASSERTIONS = NO;
477 | ENABLE_STRICT_OBJC_MSGSEND = YES;
478 | GCC_C_LANGUAGE_STANDARD = gnu99;
479 | GCC_NO_COMMON_BLOCKS = YES;
480 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
481 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
482 | GCC_WARN_UNDECLARED_SELECTOR = YES;
483 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
484 | GCC_WARN_UNUSED_FUNCTION = YES;
485 | GCC_WARN_UNUSED_VARIABLE = YES;
486 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
487 | MTL_ENABLE_DEBUG_INFO = NO;
488 | SDKROOT = iphoneos;
489 | TARGETED_DEVICE_FAMILY = "1,2";
490 | VALIDATE_PRODUCT = YES;
491 | };
492 | name = Release;
493 | };
494 | 97C147061CF9000F007C117D /* Debug */ = {
495 | isa = XCBuildConfiguration;
496 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
497 | buildSettings = {
498 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
499 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
500 | ENABLE_BITCODE = NO;
501 | FRAMEWORK_SEARCH_PATHS = (
502 | "$(inherited)",
503 | "$(PROJECT_DIR)/Flutter",
504 | );
505 | INFOPLIST_FILE = Runner/Info.plist;
506 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
507 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
508 | LIBRARY_SEARCH_PATHS = (
509 | "$(inherited)",
510 | "$(PROJECT_DIR)/Flutter",
511 | );
512 | PRODUCT_BUNDLE_IDENTIFIER = com.example.dash_chat_tutorial;
513 | PRODUCT_NAME = "$(TARGET_NAME)";
514 | VERSIONING_SYSTEM = "apple-generic";
515 | };
516 | name = Debug;
517 | };
518 | 97C147071CF9000F007C117D /* Release */ = {
519 | isa = XCBuildConfiguration;
520 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
521 | buildSettings = {
522 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
523 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
524 | ENABLE_BITCODE = NO;
525 | FRAMEWORK_SEARCH_PATHS = (
526 | "$(inherited)",
527 | "$(PROJECT_DIR)/Flutter",
528 | );
529 | INFOPLIST_FILE = Runner/Info.plist;
530 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
531 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
532 | LIBRARY_SEARCH_PATHS = (
533 | "$(inherited)",
534 | "$(PROJECT_DIR)/Flutter",
535 | );
536 | PRODUCT_BUNDLE_IDENTIFIER = com.example.dash_chat_tutorial;
537 | PRODUCT_NAME = "$(TARGET_NAME)";
538 | VERSIONING_SYSTEM = "apple-generic";
539 | };
540 | name = Release;
541 | };
542 | /* End XCBuildConfiguration section */
543 |
544 | /* Begin XCConfigurationList section */
545 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
546 | isa = XCConfigurationList;
547 | buildConfigurations = (
548 | 97C147031CF9000F007C117D /* Debug */,
549 | 97C147041CF9000F007C117D /* Release */,
550 | 249021D3217E4FDB00AE95B9 /* Profile */,
551 | );
552 | defaultConfigurationIsVisible = 0;
553 | defaultConfigurationName = Release;
554 | };
555 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
556 | isa = XCConfigurationList;
557 | buildConfigurations = (
558 | 97C147061CF9000F007C117D /* Debug */,
559 | 97C147071CF9000F007C117D /* Release */,
560 | 249021D4217E4FDB00AE95B9 /* Profile */,
561 | );
562 | defaultConfigurationIsVisible = 0;
563 | defaultConfigurationName = Release;
564 | };
565 | /* End XCConfigurationList section */
566 | };
567 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
568 | }
569 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : FlutterAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #include "AppDelegate.h"
2 | #include "GeneratedPluginRegistrant.h"
3 |
4 | @implementation AppDelegate
5 |
6 | - (BOOL)application:(UIApplication *)application
7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
8 | [GeneratedPluginRegistrant registerWithRegistry:self];
9 | // Override point for customization after application launch.
10 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
11 | }
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fayeed/dash_chat/2ee9632d43e889134d5a995d5fd901da626f0e3e/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | example
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/example/ios/Runner/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char* argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import 'package:firebase_core/firebase_core.dart';
5 | import 'package:cloud_firestore/cloud_firestore.dart';
6 | import 'package:firebase_storage/firebase_storage.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:image_picker/image_picker.dart';
9 | import 'package:intl/intl.dart';
10 | import 'package:dash_chat/dash_chat.dart';
11 |
12 | void main() async {
13 | WidgetsFlutterBinding.ensureInitialized();
14 | await Firebase.initializeApp();
15 | runApp(MyApp());
16 | }
17 |
18 | class MyApp extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return MaterialApp(
22 | title: 'Flutter Demo',
23 | theme: ThemeData(
24 | primarySwatch: Colors.purple,
25 | ),
26 | home: MyHomePage(),
27 | );
28 | }
29 | }
30 |
31 | class MyHomePage extends StatefulWidget {
32 | @override
33 | _MyHomePageState createState() => _MyHomePageState();
34 | }
35 |
36 | class _MyHomePageState extends State {
37 | final GlobalKey _chatViewKey = GlobalKey();
38 |
39 | final ChatUser user = ChatUser(
40 | name: "Fayeed",
41 | uid: "123456789",
42 | avatar: "https://www.wrappixel.com/ampleadmin/assets/images/users/4.jpg",
43 | );
44 |
45 | final ChatUser otherUser = ChatUser(
46 | name: "Mrfatty",
47 | uid: "25649654",
48 | );
49 |
50 | List messages = [];
51 | var m = [];
52 |
53 | var i = 0;
54 |
55 | @override
56 | void initState() {
57 | super.initState();
58 | }
59 |
60 | void systemMessage() {
61 | Timer(Duration(milliseconds: 300), () {
62 | if (i < 6) {
63 | setState(() {
64 | messages = [...messages, m[i]];
65 | });
66 | i++;
67 | }
68 | Timer(Duration(milliseconds: 300), () {
69 | _chatViewKey.currentState!.scrollController
70 | ..animateTo(
71 | _chatViewKey
72 | .currentState!.scrollController.position.maxScrollExtent,
73 | curve: Curves.easeOut,
74 | duration: const Duration(milliseconds: 300),
75 | );
76 | });
77 | });
78 | }
79 |
80 | void onSend(ChatMessage message) {
81 | print(message.toJson());
82 | FirebaseFirestore.instance
83 | .collection('messages')
84 | .doc(DateTime.now().millisecondsSinceEpoch.toString())
85 | .set(message.toJson());
86 | /* setState(() {
87 | messages = [...messages, message];
88 | print(messages.length);
89 | });
90 |
91 | if (i == 0) {
92 | systemMessage();
93 | Timer(Duration(milliseconds: 600), () {
94 | systemMessage();
95 | });
96 | } else {
97 | systemMessage();
98 | } */
99 | }
100 |
101 | @override
102 | Widget build(BuildContext context) {
103 | return Scaffold(
104 | appBar: AppBar(
105 | title: Text("Chat App"),
106 | ),
107 | body: StreamBuilder(
108 | stream: FirebaseFirestore.instance
109 | .collection('messages')
110 | .orderBy("createdAt")
111 | .snapshots(),
112 | builder: (context, snapshot) {
113 | if (!snapshot.hasData) {
114 | return Center(
115 | child: CircularProgressIndicator(
116 | valueColor: AlwaysStoppedAnimation(
117 | Theme.of(context).primaryColor,
118 | ),
119 | ),
120 | );
121 | } else {
122 | List items = snapshot.data!.docs;
123 | var messages =
124 | items.map((i) => ChatMessage.fromJson(i.data()!)).toList();
125 | return DashChat(
126 | key: _chatViewKey,
127 | inverted: false,
128 | onSend: onSend,
129 | sendOnEnter: true,
130 | textInputAction: TextInputAction.send,
131 | user: user,
132 | inputDecoration:
133 | InputDecoration.collapsed(hintText: "Add message here..."),
134 | dateFormat: DateFormat('yyyy-MMM-dd'),
135 | timeFormat: DateFormat('HH:mm'),
136 | messages: messages,
137 | showUserAvatar: false,
138 | showAvatarForEveryMessage: false,
139 | scrollToBottom: false,
140 | onPressAvatar: (ChatUser user) {
141 | print("OnPressAvatar: ${user.name}");
142 | },
143 | onLongPressAvatar: (ChatUser user) {
144 | print("OnLongPressAvatar: ${user.name}");
145 | },
146 | inputMaxLines: 5,
147 | messageContainerPadding: EdgeInsets.only(left: 5.0, right: 5.0),
148 | alwaysShowSend: true,
149 | inputTextStyle: TextStyle(fontSize: 16.0),
150 | inputContainerStyle: BoxDecoration(
151 | border: Border.all(width: 0.0),
152 | color: Colors.white,
153 | ),
154 | onQuickReply: (Reply reply) {
155 | setState(() {
156 | messages.add(ChatMessage(
157 | text: reply.value,
158 | createdAt: DateTime.now(),
159 | user: user));
160 |
161 | messages = [...messages];
162 | });
163 |
164 | Timer(Duration(milliseconds: 300), () {
165 | _chatViewKey.currentState!.scrollController
166 | ..animateTo(
167 | _chatViewKey.currentState!.scrollController.position
168 | .maxScrollExtent,
169 | curve: Curves.easeOut,
170 | duration: const Duration(milliseconds: 300),
171 | );
172 |
173 | if (i == 0) {
174 | systemMessage();
175 | Timer(Duration(milliseconds: 600), () {
176 | systemMessage();
177 | });
178 | } else {
179 | systemMessage();
180 | }
181 | });
182 | },
183 | onLoadEarlier: () {
184 | print("laoding...");
185 | },
186 | shouldShowLoadEarlier: false,
187 | showTraillingBeforeSend: true,
188 | trailing: [
189 | IconButton(
190 | icon: Icon(Icons.photo),
191 | onPressed: () async {
192 | final picker = ImagePicker();
193 | PickedFile? result = await picker.getImage(
194 | source: ImageSource.gallery,
195 | imageQuality: 80,
196 | maxHeight: 400,
197 | maxWidth: 400,
198 | );
199 |
200 | if (result != null) {
201 | final Reference storageRef =
202 | FirebaseStorage.instance.ref().child("chat_images");
203 |
204 | final taskSnapshot = await storageRef.putFile(
205 | File(result.path),
206 | SettableMetadata(
207 | contentType: 'image/jpg',
208 | ),
209 | );
210 |
211 | String url = await taskSnapshot.ref.getDownloadURL();
212 |
213 | ChatMessage message =
214 | ChatMessage(text: "", user: user, image: url);
215 |
216 | FirebaseFirestore.instance
217 | .collection('messages')
218 | .add(message.toJson());
219 | }
220 | },
221 | )
222 | ],
223 | );
224 | }
225 | }),
226 | );
227 | }
228 | }
229 |
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.5.0"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.1.0"
18 | characters:
19 | dependency: transitive
20 | description:
21 | name: characters
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.1.0"
25 | charcode:
26 | dependency: transitive
27 | description:
28 | name: charcode
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.2.0"
32 | clock:
33 | dependency: transitive
34 | description:
35 | name: clock
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.1.0"
39 | cloud_firestore:
40 | dependency: "direct main"
41 | description:
42 | name: cloud_firestore
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.0.0"
46 | cloud_firestore_platform_interface:
47 | dependency: transitive
48 | description:
49 | name: cloud_firestore_platform_interface
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "4.0.0"
53 | cloud_firestore_web:
54 | dependency: transitive
55 | description:
56 | name: cloud_firestore_web
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "1.0.0"
60 | collection:
61 | dependency: transitive
62 | description:
63 | name: collection
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "1.15.0"
67 | crypto:
68 | dependency: transitive
69 | description:
70 | name: crypto
71 | url: "https://pub.dartlang.org"
72 | source: hosted
73 | version: "3.0.0"
74 | dash_chat:
75 | dependency: "direct dev"
76 | description:
77 | path: ".."
78 | relative: true
79 | source: path
80 | version: "1.1.16"
81 | fake_async:
82 | dependency: transitive
83 | description:
84 | name: fake_async
85 | url: "https://pub.dartlang.org"
86 | source: hosted
87 | version: "1.2.0"
88 | firebase_auth:
89 | dependency: "direct main"
90 | description:
91 | name: firebase_auth
92 | url: "https://pub.dartlang.org"
93 | source: hosted
94 | version: "1.0.0"
95 | firebase_auth_platform_interface:
96 | dependency: transitive
97 | description:
98 | name: firebase_auth_platform_interface
99 | url: "https://pub.dartlang.org"
100 | source: hosted
101 | version: "4.0.0"
102 | firebase_auth_web:
103 | dependency: transitive
104 | description:
105 | name: firebase_auth_web
106 | url: "https://pub.dartlang.org"
107 | source: hosted
108 | version: "1.0.0"
109 | firebase_core:
110 | dependency: transitive
111 | description:
112 | name: firebase_core
113 | url: "https://pub.dartlang.org"
114 | source: hosted
115 | version: "1.0.0"
116 | firebase_core_platform_interface:
117 | dependency: transitive
118 | description:
119 | name: firebase_core_platform_interface
120 | url: "https://pub.dartlang.org"
121 | source: hosted
122 | version: "4.0.0"
123 | firebase_core_web:
124 | dependency: transitive
125 | description:
126 | name: firebase_core_web
127 | url: "https://pub.dartlang.org"
128 | source: hosted
129 | version: "1.0.0"
130 | firebase_storage:
131 | dependency: "direct main"
132 | description:
133 | name: firebase_storage
134 | url: "https://pub.dartlang.org"
135 | source: hosted
136 | version: "8.0.0"
137 | firebase_storage_platform_interface:
138 | dependency: transitive
139 | description:
140 | name: firebase_storage_platform_interface
141 | url: "https://pub.dartlang.org"
142 | source: hosted
143 | version: "2.0.0"
144 | firebase_storage_web:
145 | dependency: transitive
146 | description:
147 | name: firebase_storage_web
148 | url: "https://pub.dartlang.org"
149 | source: hosted
150 | version: "1.0.0"
151 | flutter:
152 | dependency: "direct main"
153 | description: flutter
154 | source: sdk
155 | version: "0.0.0"
156 | flutter_parsed_text:
157 | dependency: transitive
158 | description:
159 | name: flutter_parsed_text
160 | url: "https://pub.dartlang.org"
161 | source: hosted
162 | version: "2.1.0"
163 | flutter_plugin_android_lifecycle:
164 | dependency: transitive
165 | description:
166 | name: flutter_plugin_android_lifecycle
167 | url: "https://pub.dartlang.org"
168 | source: hosted
169 | version: "2.0.0"
170 | flutter_test:
171 | dependency: "direct dev"
172 | description: flutter
173 | source: sdk
174 | version: "0.0.0"
175 | flutter_web_plugins:
176 | dependency: transitive
177 | description: flutter
178 | source: sdk
179 | version: "0.0.0"
180 | http:
181 | dependency: transitive
182 | description:
183 | name: http
184 | url: "https://pub.dartlang.org"
185 | source: hosted
186 | version: "0.13.0"
187 | http_parser:
188 | dependency: transitive
189 | description:
190 | name: http_parser
191 | url: "https://pub.dartlang.org"
192 | source: hosted
193 | version: "4.0.0"
194 | image_picker:
195 | dependency: "direct main"
196 | description:
197 | name: image_picker
198 | url: "https://pub.dartlang.org"
199 | source: hosted
200 | version: "0.7.2"
201 | image_picker_platform_interface:
202 | dependency: transitive
203 | description:
204 | name: image_picker_platform_interface
205 | url: "https://pub.dartlang.org"
206 | source: hosted
207 | version: "2.0.1"
208 | intl:
209 | dependency: transitive
210 | description:
211 | name: intl
212 | url: "https://pub.dartlang.org"
213 | source: hosted
214 | version: "0.17.0"
215 | js:
216 | dependency: transitive
217 | description:
218 | name: js
219 | url: "https://pub.dartlang.org"
220 | source: hosted
221 | version: "0.6.3"
222 | matcher:
223 | dependency: transitive
224 | description:
225 | name: matcher
226 | url: "https://pub.dartlang.org"
227 | source: hosted
228 | version: "0.12.10"
229 | meta:
230 | dependency: transitive
231 | description:
232 | name: meta
233 | url: "https://pub.dartlang.org"
234 | source: hosted
235 | version: "1.3.0"
236 | path:
237 | dependency: transitive
238 | description:
239 | name: path
240 | url: "https://pub.dartlang.org"
241 | source: hosted
242 | version: "1.8.0"
243 | pedantic:
244 | dependency: transitive
245 | description:
246 | name: pedantic
247 | url: "https://pub.dartlang.org"
248 | source: hosted
249 | version: "1.11.0"
250 | plugin_platform_interface:
251 | dependency: transitive
252 | description:
253 | name: plugin_platform_interface
254 | url: "https://pub.dartlang.org"
255 | source: hosted
256 | version: "2.0.0"
257 | sky_engine:
258 | dependency: transitive
259 | description: flutter
260 | source: sdk
261 | version: "0.0.99"
262 | source_span:
263 | dependency: transitive
264 | description:
265 | name: source_span
266 | url: "https://pub.dartlang.org"
267 | source: hosted
268 | version: "1.8.0"
269 | stack_trace:
270 | dependency: transitive
271 | description:
272 | name: stack_trace
273 | url: "https://pub.dartlang.org"
274 | source: hosted
275 | version: "1.10.0"
276 | stream_channel:
277 | dependency: transitive
278 | description:
279 | name: stream_channel
280 | url: "https://pub.dartlang.org"
281 | source: hosted
282 | version: "2.1.0"
283 | string_scanner:
284 | dependency: transitive
285 | description:
286 | name: string_scanner
287 | url: "https://pub.dartlang.org"
288 | source: hosted
289 | version: "1.1.0"
290 | term_glyph:
291 | dependency: transitive
292 | description:
293 | name: term_glyph
294 | url: "https://pub.dartlang.org"
295 | source: hosted
296 | version: "1.2.0"
297 | test_api:
298 | dependency: transitive
299 | description:
300 | name: test_api
301 | url: "https://pub.dartlang.org"
302 | source: hosted
303 | version: "0.2.19"
304 | transparent_image:
305 | dependency: transitive
306 | description:
307 | name: transparent_image
308 | url: "https://pub.dartlang.org"
309 | source: hosted
310 | version: "2.0.0"
311 | typed_data:
312 | dependency: transitive
313 | description:
314 | name: typed_data
315 | url: "https://pub.dartlang.org"
316 | source: hosted
317 | version: "1.3.0"
318 | uuid:
319 | dependency: transitive
320 | description:
321 | name: uuid
322 | url: "https://pub.dartlang.org"
323 | source: hosted
324 | version: "3.0.4"
325 | vector_math:
326 | dependency: transitive
327 | description:
328 | name: vector_math
329 | url: "https://pub.dartlang.org"
330 | source: hosted
331 | version: "2.1.0"
332 | sdks:
333 | dart: ">=2.12.0 <3.0.0"
334 | flutter: ">=1.20.0"
335 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: example
2 | description: A new Flutter project.
3 |
4 | # The following defines the version and build number for your application.
5 | # A version number is three numbers separated by dots, like 1.2.43
6 | # followed by an optional build number separated by a +.
7 | # Both the version and the builder number may be overridden in flutter
8 | # build by specifying --build-name and --build-number, respectively.
9 | # In Android, build-name is used as versionName while build-number used as versionCode.
10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
12 | # Read more about iOS versioning at
13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
14 | version: 1.0.0+1
15 |
16 | environment:
17 | sdk: '>=2.12.0 <3.0.0'
18 |
19 | dependencies:
20 | flutter:
21 | sdk: flutter
22 | firebase_auth: ^1.0.0
23 | cloud_firestore: ^1.0.0
24 | firebase_storage: ^8.0.0
25 | image_picker: ^0.7.2
26 |
27 | # The following adds the Cupertino Icons font to your application.
28 | # Use with the CupertinoIcons class for iOS style icons.
29 | # cupertino_icons: ^1.0.2
30 |
31 | dev_dependencies:
32 | flutter_test:
33 | sdk: flutter
34 | dash_chat:
35 | path: ../
36 |
37 | # For information on the generic Dart part of this file, see the
38 | # following page: https://dart.dev/tools/pub/pubspec
39 |
40 | # The following section is specific to Flutter.
41 | flutter:
42 | # The following line ensures that the Material Icons font is
43 | # included with your application, so that you can use the icons in
44 | # the material Icons class.
45 | uses-material-design: true
46 | # To add assets to your application, add an assets section, like this:
47 | # assets:
48 | # - images/a_dot_burr.jpeg
49 | # - images/a_dot_ham.jpeg
50 | # An image asset can refer to one or more resolution-specific "variants", see
51 | # https://flutter.dev/assets-and-images/#resolution-aware.
52 | # For details regarding adding assets from package dependencies, see
53 | # https://flutter.dev/assets-and-images/#from-packages
54 | # To add custom fonts to your application, add a fonts section here,
55 | # in this "flutter" section. Each entry in this list should have a
56 | # "family" key with the font family name, and a "fonts" key with a
57 | # list giving the asset and other descriptors for the font. For
58 | # example:
59 | # fonts:
60 | # - family: Schyler
61 | # fonts:
62 | # - asset: fonts/Schyler-Regular.ttf
63 | # - asset: fonts/Schyler-Italic.ttf
64 | # style: italic
65 | # - family: Trajan Pro
66 | # fonts:
67 | # - asset: fonts/TrajanPro.ttf
68 | # - asset: fonts/TrajanPro_Bold.ttf
69 | # weight: 700
70 | #
71 | # For details regarding fonts from package dependencies,
72 | # see https://flutter.dev/custom-fonts/#from-packages
73 |
--------------------------------------------------------------------------------
/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:example/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/lib/dash_chat.dart:
--------------------------------------------------------------------------------
1 | library dash_chat;
2 |
3 | import 'dart:async';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter/widgets.dart';
6 | import 'package:flutter/services.dart';
7 | import 'package:uuid/uuid.dart';
8 | import 'package:intl/intl.dart' hide TextDirection;
9 | import 'package:flutter_parsed_text/flutter_parsed_text.dart';
10 | import 'package:transparent_image/transparent_image.dart';
11 |
12 | export 'package:intl/intl.dart' hide TextDirection;
13 | export 'package:flutter_parsed_text/flutter_parsed_text.dart';
14 |
15 | part 'src/chat_view.dart';
16 | part 'src/models/reply.dart';
17 | part 'src/models/quick_replies.dart';
18 | part 'src/models/chat_user.dart';
19 | part 'src/models/chat_message.dart';
20 | part 'src/models/scroll_to_bottom_style.dart';
21 | part 'src/widgets/custom_scroll_behaviour.dart';
22 | part 'src/chat_input_toolbar.dart';
23 | part 'src/message_listview.dart';
24 | part 'src/widgets/date_builder.dart';
25 | part 'src/widgets/avatar_container.dart';
26 | part 'src/widgets/message_container.dart';
27 | part 'src/widgets/quick_reply.dart';
28 | part 'src/widgets/scroll_to_bottom.dart';
29 | part 'src/widgets/load_earlier.dart';
30 |
--------------------------------------------------------------------------------
/lib/src/chat_input_toolbar.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | class ChatInputToolbar extends StatelessWidget {
4 | final TextEditingController? controller;
5 | final TextStyle? inputTextStyle;
6 | final InputDecoration? inputDecoration;
7 | final TextCapitalization? textCapitalization;
8 | final BoxDecoration? inputContainerStyle;
9 | final List leading;
10 | final List trailling;
11 | final int inputMaxLines;
12 | final int? maxInputLength;
13 | final bool alwaysShowSend;
14 | final ChatUser user;
15 | final Function(ChatMessage)? onSend;
16 | final String? text;
17 | final Function(String)? onTextChange;
18 | final bool inputDisabled;
19 | final String Function()? messageIdGenerator;
20 | final Widget Function(Function)? sendButtonBuilder;
21 | final Widget Function()? inputFooterBuilder;
22 | final bool showInputCursor;
23 | final double inputCursorWidth;
24 | final Color? inputCursorColor;
25 | final ScrollController? scrollController;
26 | final bool showTraillingBeforeSend;
27 | final FocusNode? focusNode;
28 | final EdgeInsets inputToolbarPadding;
29 | final EdgeInsets inputToolbarMargin;
30 | final TextDirection textDirection;
31 | final bool sendOnEnter;
32 | final bool reverse;
33 | final TextInputAction? textInputAction;
34 |
35 | ChatInputToolbar({
36 | Key? key,
37 | this.textDirection = TextDirection.ltr,
38 | this.focusNode,
39 | this.scrollController,
40 | this.text,
41 | this.textInputAction,
42 | this.sendOnEnter = false,
43 | this.onTextChange,
44 | this.inputDisabled = false,
45 | this.controller,
46 | this.leading = const [],
47 | this.trailling = const [],
48 | this.inputDecoration,
49 | this.textCapitalization,
50 | this.inputTextStyle,
51 | this.inputContainerStyle,
52 | this.inputMaxLines = 1,
53 | this.showInputCursor = true,
54 | this.maxInputLength,
55 | this.inputCursorWidth = 2.0,
56 | this.inputCursorColor,
57 | this.onSend,
58 | this.reverse = false,
59 | required this.user,
60 | this.alwaysShowSend = false,
61 | this.messageIdGenerator,
62 | this.inputFooterBuilder,
63 | this.sendButtonBuilder,
64 | this.showTraillingBeforeSend = true,
65 | this.inputToolbarPadding = const EdgeInsets.all(0.0),
66 | this.inputToolbarMargin = const EdgeInsets.all(0.0),
67 | }) : super(key: key);
68 |
69 | @override
70 | Widget build(BuildContext context) {
71 | ChatMessage message = ChatMessage(
72 | text: text,
73 | user: user,
74 | messageIdGenerator: messageIdGenerator,
75 | createdAt: DateTime.now(),
76 | );
77 |
78 | return Container(
79 | padding: inputToolbarPadding,
80 | margin: inputToolbarMargin,
81 | decoration: inputContainerStyle != null
82 | ? inputContainerStyle
83 | : BoxDecoration(color: Colors.white),
84 | child: Column(
85 | children: [
86 | Row(
87 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
88 | crossAxisAlignment: CrossAxisAlignment.center,
89 | children: [
90 | ...leading,
91 | Expanded(
92 | child: Padding(
93 | padding: const EdgeInsets.symmetric(horizontal: 8.0),
94 | child: Directionality(
95 | textDirection: textDirection,
96 | child: TextField(
97 | focusNode: focusNode,
98 | onChanged: (value) {
99 | onTextChange!(value);
100 | },
101 | onSubmitted: (value) {
102 | if (sendOnEnter) {
103 | _sendMessage(context, message);
104 | }
105 | },
106 | textInputAction: textInputAction,
107 | buildCounter: (
108 | BuildContext context, {
109 | int? currentLength,
110 | int? maxLength,
111 | bool? isFocused,
112 | }) =>
113 | null,
114 | decoration: inputDecoration != null
115 | ? inputDecoration
116 | : InputDecoration.collapsed(
117 | hintText: "",
118 | fillColor: Colors.white,
119 | ),
120 | textCapitalization: textCapitalization!,
121 | controller: controller,
122 | style: inputTextStyle,
123 | maxLength: maxInputLength,
124 | minLines: 1,
125 | maxLines: inputMaxLines,
126 | showCursor: showInputCursor,
127 | cursorColor: inputCursorColor,
128 | cursorWidth: inputCursorWidth,
129 | enabled: !inputDisabled,
130 | ),
131 | ),
132 | ),
133 | ),
134 | if (showTraillingBeforeSend) ...trailling,
135 | if (sendButtonBuilder != null)
136 | sendButtonBuilder!(() async {
137 | if (text!.length != 0) {
138 | await onSend!(message);
139 |
140 | controller!.text = "";
141 |
142 | onTextChange!("");
143 | }
144 | })
145 | else
146 | IconButton(
147 | icon: Icon(Icons.send),
148 | onPressed: alwaysShowSend || text!.length != 0
149 | ? () => _sendMessage(context, message)
150 | : null,
151 | ),
152 | if (!showTraillingBeforeSend) ...trailling,
153 | ],
154 | ),
155 | if (inputFooterBuilder != null) inputFooterBuilder!()
156 | ],
157 | ),
158 | );
159 | }
160 |
161 | void _sendMessage(BuildContext context, ChatMessage message) async {
162 | if (text!.length != 0) {
163 | await onSend!(message);
164 |
165 | controller!.text = "";
166 |
167 | onTextChange!("");
168 |
169 | FocusScope.of(context).requestFocus(focusNode);
170 |
171 | Timer(Duration(milliseconds: 150), () {
172 | scrollController!.animateTo(
173 | reverse ? 0.0 : scrollController!.position.maxScrollExtent + 30.0,
174 | curve: Curves.easeOut,
175 | duration: const Duration(milliseconds: 300),
176 | );
177 | });
178 | }
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/lib/src/chat_view.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | /// A complete chat UI which is inspired by [react-native-gifted-chat]
4 | /// Highly customizable and helps developing chat UI faster
5 | class DashChat extends StatefulWidget {
6 | /// Flex value for the messeage container defaults to 1
7 | /// Made so that the message container takes as much as possible
8 | /// if no height or width is passed explicity
9 | final int messageContainerFlex;
10 |
11 | /// Set the chat into read only mode, meaning no way to send messages
12 | /// default to false
13 | final bool readOnly;
14 |
15 | /// Height for the Dash chat Widget
16 | final double? height;
17 |
18 | // Width for the Dash chat Widget
19 | final double? width;
20 |
21 | /// List of messages to display in the chat container
22 | /// Takes a [List] of [ChatMessage]
23 | final List messages;
24 |
25 | /// If provided, this text editing controller will be used for
26 | /// the text input.
27 | final TextEditingController? textController;
28 |
29 | /// If provided, this focus node will be used for the text input.
30 | final FocusNode? focusNode;
31 |
32 | /// Use to change the direction of the text ltr is used for
33 | /// launguages that start from left like English &
34 | /// rtl is used for languages like Arabic
35 | ///
36 | /// Defaults to `TextDirection.ltr`
37 | final TextDirection inputTextDirection;
38 |
39 | /// If provided will stop using the default controller
40 | /// i.e [TextEditingController] and will use this to update the
41 | /// text input field.
42 | final String? text;
43 |
44 | /// If the text parameter is passed then onTextChange must also
45 | /// be passed.
46 | final Function(String)? onTextChange;
47 |
48 | /// If the input TextField is disabled.
49 | final bool inputDisabled;
50 |
51 | /// Used to provide input decoration to the text as default only
52 | /// to the input placeholder for the chat input
53 | /// "Add Message here...".
54 | final InputDecoration? inputDecoration;
55 |
56 | ///Configures how the platform keyboard will select an uppercase or lowercase keyboard.
57 | ///Only supports text keyboards, other keyboard types will ignore this configuration. Capitalization is locale-aware.
58 | ///Defaults to [TextCapitalization.none]. Must not be null.
59 | final TextCapitalization textCapitalization;
60 |
61 | /// Usually new message added by the user gets [Uuid] String
62 | /// Can be override by proving this parameter
63 | final String Function()? messageIdGenerator;
64 |
65 | /// The current user object [ChatUser].
66 | final ChatUser user;
67 |
68 | /// To function where you can make api calls and play
69 | /// with the [ChatMessage] obeject before make calls.
70 | final Function(ChatMessage) onSend;
71 |
72 | /// Should the send button be always active it defaults to false
73 | /// Usually it will only become active if some text is entered.
74 | final bool alwaysShowSend;
75 |
76 | /// Should the message be sent by hitting enter on web or text input action
77 | /// Can be useful for tablet or web usage
78 | final bool sendOnEnter;
79 |
80 | /// Input action of the keyboard
81 | final TextInputAction? textInputAction;
82 |
83 | /// [DateFormat] object for formatting date to show in [MessageListView]
84 | /// defaults to `yyyy-MM-dd`.
85 | final DateFormat? dateFormat;
86 |
87 | /// [DateFormat] object for formatting time to show in [MessageContainer]
88 | /// defaults to `HH:mm:ss`.
89 | final DateFormat? timeFormat;
90 |
91 | /// Should the user avatar be shown defaults to false and will not
92 | /// show the user avatar.
93 | final bool showUserAvatar;
94 |
95 | /// avatarBuilder will override the the default avatar which uses
96 | /// [CircleAvatar].
97 | final Widget Function(ChatUser)? avatarBuilder;
98 |
99 | /// Should the avatar be shown for every message defaulst to false.
100 | final bool showAvatarForEveryMessage;
101 |
102 | /// [onPressAvatar] function takes a function with this structure
103 | /// [Function(ChatUser)] will trigger when the avatar
104 | /// is tapped on
105 | final Function(ChatUser)? onPressAvatar;
106 |
107 | /// [onLongPressAvatar] function takea a function with this structure
108 | /// [Function(ChatUser)] will trigger when the avatar
109 | /// is long pressed
110 | final Function(ChatUser)? onLongPressAvatar;
111 |
112 | /// [onLongPressMessage] function takea a function with this structure
113 | /// [Function(ChatMessage)] will trigger when the message
114 | /// is long pressed.
115 | final Function(ChatMessage)? onLongPressMessage;
116 |
117 | /// Should the messages be shown in reversed order.
118 | final bool inverted;
119 |
120 | /// messageBuilder will override the the default chat container which uses
121 | /// and you will need to build complete message Widget it will not accept
122 | /// and include any other builder functions.
123 | final Widget Function(ChatMessage)? messageBuilder;
124 |
125 | /// messageTextBuilder will override the the default message text.
126 | final Widget Function(String?, [ChatMessage])? messageTextBuilder;
127 |
128 | /// messageImageBuilder will override the the default Image.
129 | final Widget Function(String? url, [ChatMessage])? messageImageBuilder;
130 |
131 | /// messageTimeBuilder will override the the default text.
132 | final Widget Function(String formattedTime, [ChatMessage])?
133 | messageTimeBuilder;
134 |
135 | /// dateBuilder will override the the default time text.
136 | final Widget Function(String)? dateBuilder;
137 |
138 | /// A Widget that will be shown below the [MessageListView] like you can
139 | /// show a "tying..." at the end.
140 | final Widget Function()? chatFooterBuilder;
141 |
142 | /// Main input length of the input text box defaulst to no limit.
143 | final int? maxInputLength;
144 |
145 | /// Used to parse text to make it linkified text uses
146 | /// [flutter_parsed_text](https://pub.dev/packages/flutter_parsed_text)
147 | /// takes a list of [MatchText] in order to parse Email, phone, links
148 | /// and can also add custom pattersn using regex
149 | final List parsePatterns;
150 |
151 | /// Provides a custom style to the message container
152 | /// takes [BoxDecoration]
153 | final BoxDecoration? messageContainerDecoration;
154 |
155 | /// [List] of [Widget] to show before the [TextField].
156 | final List leading;
157 |
158 | /// [List] of [Widget] to show after the [TextField].will remove the
159 | /// send button and will have to implement that yourself.
160 | final List trailing;
161 |
162 | /// sendButtonBuilder will override the the default [IconButton].
163 | final Widget Function(Function)? sendButtonBuilder;
164 |
165 | /// Style for the [TextField].
166 | final TextStyle? inputTextStyle;
167 |
168 | /// [TextField] container style.
169 | final BoxDecoration? inputContainerStyle;
170 |
171 | /// Max length of the input lines default to 1.
172 | final int inputMaxLines;
173 |
174 | /// Should the input cursor be shown defaults to true.
175 | final bool showInputCursor;
176 |
177 | /// Width of the text input defaults to 2.0.
178 | final double inputCursorWidth;
179 |
180 | /// Color of the input cursor defaults to theme.
181 | final Color? inputCursorColor;
182 |
183 | /// ScrollController for the [MessageListView] will use the default
184 | /// scrollcontroller in the Widget.
185 | final ScrollController? scrollController;
186 |
187 | /// A Widget that will be shown below the [ChatInputToolbar] like you can
188 | /// show a list of buttons like file image just like in Slack app.
189 | final Widget Function()? inputFooterBuilder;
190 |
191 | /// Padding for the [MessageListView].
192 | final EdgeInsetsGeometry messageContainerPadding;
193 |
194 | /// Callback method when the quickReply was tapped on
195 | /// will pass [Reply] as a paramter to function.
196 | final Function(Reply)? onQuickReply;
197 |
198 | /// Padding for the quick reply area
199 | /// by default it padding is set 0.0
200 | final EdgeInsetsGeometry quickReplyPadding;
201 |
202 | /// Container style for the QuickReply Container [BoxDecoration].
203 | final BoxDecoration? quickReplyStyle;
204 |
205 | /// [TextStyle] for QuickReply textstyle.
206 | final TextStyle? quickReplyTextStyle;
207 |
208 | /// quickReplyBuilder will override the the default QuickReply Widget.
209 | final Widget Function(Reply)? quickReplyBuilder;
210 |
211 | /// Should quick reply be horizontally scrollable
212 | final bool quickReplyScroll;
213 |
214 | /// Should the [trailling] Widgets be shown before the send button
215 | /// As default it will be shown before the send button.
216 | final bool showTraillingBeforeSend;
217 |
218 | /// Should the scroll to bottom widget be shown
219 | /// default to true.
220 | final bool scrollToBottom;
221 |
222 | final bool shouldStartMessagesFromTop;
223 |
224 | /// Overrides the default [scrollToBottomWidget] with a custom widget
225 | final Widget Function()? scrollToBottomWidget;
226 |
227 | /// Override the default behaviour of the onScrollToBottom Widget
228 | final Function? onScrollToBottomPress;
229 |
230 | /// Should the LoadEarlier Floating widget be shown or use
231 | /// load as you scroll scheme whcih will call the [onLoadEarlier]
232 | /// function as default it is set to this scheme which is false.
233 | /// false - load as you scroll scheme
234 | /// true - shows a loadEarlier Widget
235 | final bool shouldShowLoadEarlier;
236 |
237 | /// Override the default behaviour of the onScrollToBottom Widget
238 | final Widget Function()? showLoadEarlierWidget;
239 |
240 | /// Override the default behaviour of the onLoadEarleir Widget
241 | /// or used as a callback when the listView reaches the top
242 | final Function? onLoadEarlier;
243 |
244 | /// Padding for the default input toolbar
245 | /// by default it padding is set 0.0
246 | final EdgeInsets inputToolbarPadding;
247 |
248 | /// Margin for the default input toolbar
249 | /// by default it padding is set 0.0
250 | final EdgeInsets inputToolbarMargin;
251 |
252 | /// [messageButtonsBuilder] function takes a function with this
253 | /// structure [List Function()] to render the buttons inside
254 | /// a row.
255 | final List Function(ChatMessage)? messageButtonsBuilder;
256 |
257 | /// Padding of the message
258 | /// Default to EdgeInsets.all(8.0)
259 | final EdgeInsets messagePadding;
260 |
261 | /// Should show the text before the image in the [MessageContainer]
262 | /// or the opposite
263 | /// Default to `true`
264 | final bool textBeforeImage;
265 |
266 | /// sets the default [AvatarContainer] maxSize.
267 | ///
268 | /// Defaults to `30.0`
269 | final double avatarMaxSize;
270 |
271 | /// overrides the boxdecoration of the message
272 | /// can be used to override color, or customise the message container
273 | /// params [ChatMessage] and [isUser]: boolean
274 | /// return BoxDecoration
275 | final BoxDecoration Function(ChatMessage, bool?)? messageDecorationBuilder;
276 |
277 | late ScrollToBottomStyle scrollToBottomStyle;
278 |
279 | DashChat({
280 | Key? key,
281 | ScrollToBottomStyle? scrollToBottomStyle,
282 | this.avatarMaxSize = 30.0,
283 | this.inputTextDirection = TextDirection.ltr,
284 | this.inputToolbarMargin = const EdgeInsets.all(0.0),
285 | this.inputToolbarPadding = const EdgeInsets.all(0.0),
286 | this.shouldShowLoadEarlier = false,
287 | this.showLoadEarlierWidget,
288 | this.onLoadEarlier,
289 | this.sendOnEnter = false,
290 | this.textInputAction,
291 | this.scrollToBottom = true,
292 | this.scrollToBottomWidget,
293 | this.onScrollToBottomPress,
294 | this.onQuickReply,
295 | this.quickReplyPadding = const EdgeInsets.all(0.0),
296 | this.quickReplyStyle,
297 | this.quickReplyTextStyle,
298 | this.quickReplyBuilder,
299 | this.quickReplyScroll = false,
300 | this.messageContainerPadding = const EdgeInsets.only(
301 | left: 2.0,
302 | right: 2.0,
303 | ),
304 | this.scrollController,
305 | this.inputCursorColor,
306 | this.inputCursorWidth = 2.0,
307 | this.showInputCursor = true,
308 | this.inputMaxLines = 1,
309 | this.inputContainerStyle,
310 | this.inputTextStyle,
311 | this.leading = const [],
312 | this.trailing = const [],
313 | this.messageContainerDecoration,
314 | this.messageContainerFlex = 1,
315 | this.height,
316 | this.width,
317 | this.readOnly = false,
318 | required this.messages,
319 | this.onTextChange,
320 | this.text,
321 | this.inputDisabled = false,
322 | this.textController,
323 | this.focusNode,
324 | this.inputDecoration,
325 | this.textCapitalization = TextCapitalization.none,
326 | this.alwaysShowSend = false,
327 | this.messageIdGenerator,
328 | this.dateFormat,
329 | this.timeFormat,
330 | required this.user,
331 | required this.onSend,
332 | this.onLongPressAvatar,
333 | this.onLongPressMessage,
334 | this.onPressAvatar,
335 | this.avatarBuilder,
336 | this.showAvatarForEveryMessage = false,
337 | this.showUserAvatar = false,
338 | this.inverted = false,
339 | this.maxInputLength,
340 | this.parsePatterns = const [],
341 | this.chatFooterBuilder,
342 | this.messageBuilder,
343 | this.inputFooterBuilder,
344 | this.sendButtonBuilder,
345 | this.dateBuilder,
346 | this.messageImageBuilder,
347 | this.messageTextBuilder,
348 | this.messageTimeBuilder,
349 | this.showTraillingBeforeSend = true,
350 | this.shouldStartMessagesFromTop = false,
351 | this.messageButtonsBuilder,
352 | this.messagePadding = const EdgeInsets.all(8.0),
353 | this.textBeforeImage = true,
354 | this.messageDecorationBuilder,
355 | }) : super(key: key) {
356 | this.scrollToBottomStyle = scrollToBottomStyle ?? new ScrollToBottomStyle();
357 | }
358 |
359 | String? getVal() {
360 | return text;
361 | }
362 |
363 | @override
364 | DashChatState createState() => DashChatState();
365 | }
366 |
367 | class DashChatState extends State {
368 | FocusNode? inputFocusNode;
369 | TextEditingController? textController;
370 | late ScrollController scrollController;
371 | String _text = "";
372 | bool visible = false;
373 | GlobalKey inputKey = GlobalKey();
374 | double height = 48.0;
375 | bool showLoadMore = false;
376 | String get messageInput => _text;
377 | bool _initialLoad = true;
378 | Timer? _timer;
379 |
380 | void onTextChange(String text) {
381 | if (visible) {
382 | changeVisible(false);
383 | }
384 | setState(() {
385 | this._text = text;
386 | });
387 | }
388 |
389 | void changeVisible(bool value) {
390 | if (widget.scrollToBottom) {
391 | setState(() {
392 | visible = value;
393 | });
394 | }
395 | }
396 |
397 | void changeDefaultLoadMore(bool value) {
398 | setState(() {
399 | showLoadMore = value;
400 | });
401 | }
402 |
403 | @override
404 | void initState() {
405 | scrollController = widget.scrollController ?? ScrollController();
406 | textController = widget.textController ?? TextEditingController();
407 | inputFocusNode = widget.focusNode ?? FocusNode();
408 | WidgetsBinding.instance!.addPostFrameCallback(widgetBuilt);
409 | super.initState();
410 | }
411 |
412 | @override
413 | void dispose() {
414 | _timer?.cancel();
415 | super.dispose();
416 | }
417 |
418 | void widgetBuilt(Duration d) {
419 | double initPos = widget.inverted
420 | ? 0.0
421 | : scrollController.position.maxScrollExtent + 25.0;
422 |
423 | scrollController
424 | .animateTo(
425 | initPos,
426 | duration: const Duration(milliseconds: 150),
427 | curve: Curves.easeInOut,
428 | )
429 | .whenComplete(() {
430 | _timer = Timer(Duration(milliseconds: 1000), () {
431 | if (this.mounted) {
432 | setState(() {
433 | _initialLoad = false;
434 | });
435 | }
436 | });
437 | });
438 |
439 | scrollController.addListener(() {
440 | bool topReached = widget.inverted
441 | ? scrollController.offset >=
442 | scrollController.position.maxScrollExtent &&
443 | !scrollController.position.outOfRange
444 | : scrollController.offset <=
445 | scrollController.position.minScrollExtent &&
446 | !scrollController.position.outOfRange;
447 |
448 | if (widget.shouldShowLoadEarlier) {
449 | if (topReached) {
450 | setState(() {
451 | showLoadMore = true;
452 | });
453 | } else {
454 | setState(() {
455 | showLoadMore = false;
456 | });
457 | }
458 | } else if (topReached) {
459 | widget.onLoadEarlier!();
460 | }
461 | });
462 | }
463 |
464 | @override
465 | Widget build(BuildContext context) {
466 | return LayoutBuilder(
467 | builder: (context, constraints) {
468 | final maxWidth = constraints.maxWidth == double.infinity
469 | ? MediaQuery.of(context).size.width
470 | : constraints.maxWidth;
471 | final maxHeight = constraints.maxWidth == double.infinity
472 | ? MediaQuery.of(context).size.height
473 | : constraints.maxHeight;
474 | return Container(
475 | height: widget.height != null ? widget.height : maxHeight,
476 | width: widget.width != null ? widget.width : maxWidth,
477 | child: Stack(
478 | children: [
479 | Column(
480 | mainAxisAlignment: widget.shouldStartMessagesFromTop
481 | ? MainAxisAlignment.start
482 | : MainAxisAlignment.end,
483 | children: [
484 | MessageListView(
485 | avatarMaxSize: widget.avatarMaxSize,
486 | messagePadding: widget.messagePadding,
487 | constraints: constraints,
488 | shouldShowLoadEarlier: widget.shouldShowLoadEarlier,
489 | showLoadEarlierWidget: widget.showLoadEarlierWidget,
490 | onLoadEarlier: widget.onLoadEarlier,
491 | defaultLoadCallback: changeDefaultLoadMore,
492 | messageContainerPadding:
493 | widget.messageContainerPadding as EdgeInsets,
494 | scrollController: widget.scrollController != null
495 | ? widget.scrollController
496 | : scrollController,
497 | user: widget.user,
498 | messages: widget.messages,
499 | showuserAvatar: widget.showUserAvatar,
500 | dateFormat: widget.dateFormat,
501 | timeFormat: widget.timeFormat,
502 | inverted: widget.inverted,
503 | showAvatarForEverMessage:
504 | widget.showAvatarForEveryMessage,
505 | onLongPressAvatar: widget.onLongPressAvatar,
506 | onPressAvatar: widget.onPressAvatar,
507 | onLongPressMessage: widget.onLongPressMessage,
508 | avatarBuilder: widget.avatarBuilder,
509 | messageBuilder: widget.messageBuilder,
510 | messageTextBuilder: widget.messageTextBuilder,
511 | messageImageBuilder: widget.messageImageBuilder,
512 | messageTimeBuilder: widget.messageTimeBuilder,
513 | dateBuilder: widget.dateBuilder,
514 | messageContainerDecoration:
515 | widget.messageContainerDecoration,
516 | parsePatterns: widget.parsePatterns,
517 | changeVisible: changeVisible,
518 | visible: visible,
519 | showLoadMore: showLoadMore,
520 | messageButtonsBuilder: widget.messageButtonsBuilder,
521 | messageDecorationBuilder:
522 | widget.messageDecorationBuilder),
523 | if (widget.messages.length != 0 &&
524 | widget.messages.last.user.uid != widget.user.uid &&
525 | widget.messages.last.quickReplies != null)
526 | Container(
527 | padding: widget.quickReplyPadding,
528 | constraints: BoxConstraints(
529 | maxHeight: widget.quickReplyScroll ? 50.0 : 100.0),
530 | width: widget.quickReplyScroll ? null : maxWidth,
531 | child: widget.quickReplyScroll
532 | ? ListView(
533 | scrollDirection: Axis.horizontal,
534 | children: widget
535 | .messages.last.quickReplies!.values!
536 | .map(_mapReply)
537 | .toList(),
538 | )
539 | : Wrap(
540 | children: [
541 | ...widget.messages.last.quickReplies!.values!
542 | .sublist(
543 | 0,
544 | widget.messages.last.quickReplies!
545 | .values!.length <=
546 | 3
547 | ? widget.messages.last.quickReplies!
548 | .values!.length
549 | : 3)
550 | .map(_mapReply)
551 | .toList(),
552 | ],
553 | ),
554 | ),
555 | if (widget.chatFooterBuilder != null)
556 | widget.chatFooterBuilder!(),
557 | if (!widget.readOnly)
558 | SafeArea(
559 | child: ChatInputToolbar(
560 | key: inputKey,
561 | sendOnEnter: widget.sendOnEnter,
562 | textInputAction: widget.textInputAction,
563 | inputToolbarPadding: widget.inputToolbarPadding,
564 | textDirection: widget.inputTextDirection,
565 | inputToolbarMargin: widget.inputToolbarMargin,
566 | showTraillingBeforeSend: widget.showTraillingBeforeSend,
567 | inputMaxLines: widget.inputMaxLines,
568 | controller: textController,
569 | inputDecoration: widget.inputDecoration,
570 | textCapitalization: widget.textCapitalization,
571 | onSend: widget.onSend,
572 | user: widget.user,
573 | messageIdGenerator: widget.messageIdGenerator,
574 | maxInputLength: widget.maxInputLength,
575 | sendButtonBuilder: widget.sendButtonBuilder,
576 | text: widget.text != null ? widget.text : _text,
577 | onTextChange: widget.onTextChange != null
578 | ? widget.onTextChange
579 | : onTextChange,
580 | inputDisabled: widget.inputDisabled,
581 | leading: widget.leading,
582 | trailling: widget.trailing,
583 | inputContainerStyle: widget.inputContainerStyle,
584 | inputTextStyle: widget.inputTextStyle,
585 | inputFooterBuilder: widget.inputFooterBuilder,
586 | inputCursorColor: widget.inputCursorColor,
587 | inputCursorWidth: widget.inputCursorWidth,
588 | showInputCursor: widget.showInputCursor,
589 | alwaysShowSend: widget.alwaysShowSend,
590 | scrollController: widget.scrollController != null
591 | ? widget.scrollController
592 | : scrollController,
593 | focusNode: inputFocusNode,
594 | reverse: widget.inverted,
595 | ),
596 | )
597 | ],
598 | ),
599 | if (visible && !_initialLoad)
600 | Positioned(
601 | right: widget.scrollToBottomStyle.right,
602 | left: widget.scrollToBottomStyle.left,
603 | bottom: widget.scrollToBottomStyle.bottom,
604 | top: widget.scrollToBottomStyle.top,
605 | child: widget.scrollToBottomWidget != null
606 | ? widget.scrollToBottomWidget!()
607 | : ScrollToBottom(
608 | onScrollToBottomPress: widget.onScrollToBottomPress,
609 | scrollToBottomStyle: widget.scrollToBottomStyle,
610 | scrollController: scrollController,
611 | inverted: widget.inverted,
612 | ),
613 | ),
614 | ],
615 | ),
616 | );
617 | },
618 | );
619 | }
620 |
621 | QuickReply _mapReply(Reply reply) => QuickReply(
622 | reply: reply,
623 | onReply: widget.onQuickReply,
624 | quickReplyBuilder: widget.quickReplyBuilder,
625 | quickReplyStyle: widget.quickReplyStyle,
626 | quickReplyTextStyle: widget.quickReplyTextStyle,
627 | );
628 | }
629 |
--------------------------------------------------------------------------------
/lib/src/message_listview.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | class MessageListView extends StatefulWidget {
4 | final List messages;
5 | final ChatUser user;
6 | final bool? showuserAvatar;
7 | final DateFormat? dateFormat;
8 | final DateFormat? timeFormat;
9 | final bool? showAvatarForEverMessage;
10 | final Function(ChatUser)? onPressAvatar;
11 | final Function(ChatUser)? onLongPressAvatar;
12 | final bool? renderAvatarOnTop;
13 | final Function(ChatMessage)? onLongPressMessage;
14 | final bool inverted;
15 | final Widget Function(ChatUser)? avatarBuilder;
16 | final Widget Function(ChatMessage)? messageBuilder;
17 | final Widget Function(String?, [ChatMessage])? messageTextBuilder;
18 | final Widget Function(String?, [ChatMessage])? messageImageBuilder;
19 | final Widget Function(String, [ChatMessage])? messageTimeBuilder;
20 | final Widget Function(String)? dateBuilder;
21 | final Widget Function()? renderMessageFooter;
22 | final BoxDecoration? messageContainerDecoration;
23 | final List parsePatterns;
24 | final ScrollController? scrollController;
25 | final EdgeInsets messageContainerPadding;
26 | final Function? changeVisible;
27 | final bool? visible;
28 | final bool? showLoadMore;
29 | final bool? shouldShowLoadEarlier;
30 | final Widget Function()? showLoadEarlierWidget;
31 | final Function? onLoadEarlier;
32 | final Function(bool) defaultLoadCallback;
33 | final BoxConstraints? constraints;
34 | final List Function(ChatMessage)? messageButtonsBuilder;
35 | final EdgeInsets messagePadding;
36 | final bool textBeforeImage;
37 | final double? avatarMaxSize;
38 | final BoxDecoration Function(ChatMessage, bool?)? messageDecorationBuilder;
39 |
40 | MessageListView({
41 | this.showLoadEarlierWidget,
42 | this.avatarMaxSize,
43 | this.shouldShowLoadEarlier,
44 | this.constraints,
45 | this.onLoadEarlier,
46 | required this.defaultLoadCallback,
47 | this.messageContainerPadding =
48 | const EdgeInsets.only(top: 10.0, right: 10.0, left: 10.0),
49 | this.scrollController,
50 | this.parsePatterns = const [],
51 | this.messageContainerDecoration,
52 | required this.messages,
53 | required this.user,
54 | this.showuserAvatar,
55 | this.dateFormat,
56 | this.timeFormat,
57 | this.showAvatarForEverMessage,
58 | required this.inverted,
59 | this.onLongPressAvatar,
60 | this.onLongPressMessage,
61 | this.onPressAvatar,
62 | this.renderAvatarOnTop,
63 | this.messageBuilder,
64 | this.renderMessageFooter,
65 | this.avatarBuilder,
66 | this.dateBuilder,
67 | this.messageImageBuilder,
68 | this.messageTextBuilder,
69 | this.messageTimeBuilder,
70 | this.changeVisible,
71 | this.visible,
72 | this.showLoadMore,
73 | this.messageButtonsBuilder,
74 | this.messagePadding = const EdgeInsets.all(8.0),
75 | this.textBeforeImage = true,
76 | this.messageDecorationBuilder,
77 | });
78 |
79 | @override
80 | _MessageListViewState createState() => _MessageListViewState();
81 | }
82 |
83 | class _MessageListViewState extends State {
84 | double previousPixelPostion = 0.0;
85 |
86 | bool scrollNotificationFunc(ScrollNotification scrollNotification) {
87 | double bottom =
88 | widget.inverted ? 0.0 : scrollNotification.metrics.maxScrollExtent;
89 |
90 | if (scrollNotification.metrics.pixels == bottom) {
91 | if (widget.visible!) {
92 | widget.changeVisible!(false);
93 | }
94 | } else if ((scrollNotification.metrics.pixels - bottom).abs() > 100) {
95 | if (!widget.visible!) {
96 | widget.changeVisible!(true);
97 | }
98 | }
99 | return true;
100 | }
101 |
102 | bool shouldShowAvatar(int index) {
103 | if (widget.showAvatarForEverMessage!) {
104 | return true;
105 | }
106 | if (!widget.inverted && index + 1 < widget.messages.length) {
107 | return widget.messages[index + 1].user.uid !=
108 | widget.messages[index].user.uid;
109 | } else if (widget.inverted && index - 1 >= 0) {
110 | return widget.messages[index - 1].user.uid !=
111 | widget.messages[index].user.uid;
112 | }
113 | return true;
114 | }
115 |
116 | @override
117 | Widget build(BuildContext context) {
118 | DateTime? currentDate;
119 |
120 | final constraints = widget.constraints ??
121 | BoxConstraints(
122 | maxHeight: MediaQuery.of(context).size.height,
123 | maxWidth: MediaQuery.of(context).size.width);
124 |
125 | return Flexible(
126 | child: GestureDetector(
127 | onTap: () => FocusScope.of(context).requestFocus(FocusNode()),
128 | child: Padding(
129 | padding: widget.messageContainerPadding,
130 | child: NotificationListener(
131 | onNotification: scrollNotificationFunc,
132 | child: Stack(
133 | alignment: AlignmentDirectional.topCenter,
134 | children: [
135 | ListView.builder(
136 | controller: widget.scrollController,
137 | shrinkWrap: true,
138 | reverse: widget.inverted,
139 | itemCount: widget.messages.length,
140 | itemBuilder: (context, i) {
141 | bool showAvatar = shouldShowAvatar(i);
142 | bool first = false;
143 | bool last = false;
144 | bool showDate;
145 |
146 | if (widget.messages.length == 0) {
147 | first = true;
148 | } else if (widget.messages.length - 1 == i) {
149 | last = true;
150 | }
151 |
152 | DateTime messageDate = DateTime(
153 | widget.messages[i].createdAt.year,
154 | widget.messages[i].createdAt.month,
155 | widget.messages[i].createdAt.day,
156 | );
157 |
158 | // Needed for inverted list
159 | DateTime previousDate = currentDate ?? messageDate;
160 |
161 | if (currentDate == null) {
162 | currentDate = messageDate;
163 | showDate =
164 | !widget.inverted || widget.messages.length == 1;
165 | } else if (currentDate!.difference(messageDate).inDays !=
166 | 0) {
167 | showDate = true;
168 | currentDate = messageDate;
169 | } else if (i == widget.messages.length - 1 &&
170 | widget.inverted) {
171 | showDate = true;
172 | } else {
173 | showDate = false;
174 | }
175 |
176 | return Align(
177 | child: Column(
178 | children: [
179 | if (showDate &&
180 | (!widget.inverted ||
181 | widget.messages.length == 1 ||
182 | (last && widget.inverted)))
183 | DateBuilder(
184 | date:
185 | widget.inverted ? previousDate : currentDate!,
186 | customDateBuilder: widget.dateBuilder,
187 | dateFormat: widget.dateFormat,
188 | ),
189 | Padding(
190 | padding: EdgeInsets.only(
191 | top: first ? 10.0 : 0.0,
192 | bottom: last ? 10.0 : 0.0,
193 | ),
194 | child: Row(
195 | mainAxisAlignment:
196 | widget.messages[i].user.uid == widget.user.uid
197 | ? MainAxisAlignment.end
198 | : MainAxisAlignment.start,
199 | crossAxisAlignment: CrossAxisAlignment.end,
200 | children: [
201 | Padding(
202 | padding: EdgeInsets.symmetric(
203 | horizontal: constraints.maxWidth * 0.02,
204 | ),
205 | child: Opacity(
206 | opacity:
207 | (widget.showAvatarForEverMessage! ||
208 | showAvatar) &&
209 | widget.messages[i].user.uid !=
210 | widget.user.uid
211 | ? 1
212 | : 0,
213 | child: AvatarContainer(
214 | user: widget.messages[i].user,
215 | onPress: widget.onPressAvatar,
216 | onLongPress: widget.onLongPressAvatar,
217 | avatarBuilder: widget.avatarBuilder,
218 | avatarMaxSize: widget.avatarMaxSize,
219 | ),
220 | ),
221 | ),
222 | Expanded(
223 | child: GestureDetector(
224 | onLongPress: () {
225 | if (widget.onLongPressMessage != null) {
226 | widget.onLongPressMessage!(
227 | widget.messages[i]);
228 | } else {
229 | showBottomSheet(
230 | context: context,
231 | builder: (context) => Container(
232 | child: Column(
233 | mainAxisSize:
234 | MainAxisSize.min,
235 | children: [
236 | ListTile(
237 | leading: Icon(
238 | Icons.content_copy),
239 | title: Text(
240 | "Copy to clipboard"),
241 | onTap: () {
242 | Clipboard.setData(
243 | ClipboardData(
244 | text: widget
245 | .messages[
246 | i]
247 | .text));
248 | Navigator.pop(
249 | context);
250 | },
251 | )
252 | ],
253 | ),
254 | ));
255 | }
256 | },
257 | child: widget.messageBuilder != null
258 | ? widget
259 | .messageBuilder!(widget.messages[i])
260 | : Align(
261 | alignment: widget
262 | .messages[i].user.uid ==
263 | widget.user.uid
264 | ? AlignmentDirectional.centerEnd
265 | : AlignmentDirectional
266 | .centerStart,
267 | child: MessageContainer(
268 | messagePadding:
269 | widget.messagePadding,
270 | constraints: constraints,
271 | isUser:
272 | widget.messages[i].user.uid ==
273 | widget.user.uid,
274 | message: widget.messages[i],
275 | timeFormat: widget.timeFormat,
276 | messageImageBuilder:
277 | widget.messageImageBuilder,
278 | messageTextBuilder:
279 | widget.messageTextBuilder,
280 | messageTimeBuilder:
281 | widget.messageTimeBuilder,
282 | messageContainerDecoration: widget
283 | .messageContainerDecoration,
284 | parsePatterns:
285 | widget.parsePatterns,
286 | buttons:
287 | widget.messages[i].buttons,
288 | messageButtonsBuilder:
289 | widget.messageButtonsBuilder,
290 | textBeforeImage:
291 | widget.textBeforeImage,
292 | messageDecorationBuilder: widget
293 | .messageDecorationBuilder,
294 | ),
295 | ),
296 | ),
297 | ),
298 | if (widget.showuserAvatar!)
299 | Padding(
300 | padding: EdgeInsets.symmetric(
301 | horizontal: constraints.maxWidth * 0.02,
302 | ),
303 | child: Opacity(
304 | opacity:
305 | (widget.showAvatarForEverMessage! ||
306 | showAvatar) &&
307 | widget.messages[i].user.uid ==
308 | widget.user.uid
309 | ? 1
310 | : 0,
311 | child: AvatarContainer(
312 | user: widget.messages[i].user,
313 | onPress: widget.onPressAvatar,
314 | onLongPress: widget.onLongPressAvatar,
315 | avatarBuilder: widget.avatarBuilder,
316 | avatarMaxSize: widget.avatarMaxSize,
317 | ),
318 | ),
319 | )
320 | else
321 | SizedBox(
322 | width: 10.0,
323 | ),
324 | ],
325 | ),
326 | ),
327 | if (showDate &&
328 | widget.inverted &&
329 | widget.messages.length > 1 &&
330 | !last)
331 | DateBuilder(
332 | date:
333 | widget.inverted ? previousDate : currentDate!,
334 | customDateBuilder: widget.dateBuilder,
335 | dateFormat: widget.dateFormat,
336 | ),
337 | ],
338 | ),
339 | );
340 | },
341 | ),
342 | Container(
343 | height: 100.0,
344 | ),
345 | AnimatedPositioned(
346 | top: widget.showLoadMore! ? 8.0 : -50.0,
347 | duration: Duration(milliseconds: 200),
348 | child: widget.showLoadEarlierWidget != null
349 | ? widget.showLoadEarlierWidget!()
350 | : LoadEarlierWidget(
351 | onLoadEarlier: widget.onLoadEarlier,
352 | defaultLoadCallback: widget.defaultLoadCallback,
353 | ),
354 | ),
355 | ],
356 | ),
357 | ),
358 | ),
359 | ),
360 | );
361 | }
362 | }
363 |
--------------------------------------------------------------------------------
/lib/src/models/chat_message.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | /// A message data structure used by dash chat to handle messages
4 | /// and also to handle quick replies
5 | class ChatMessage {
6 | /// Id of the message if no id is supplied a new id is assigned
7 | /// using a [UUID v4] this behaviour could be overriden by provind
8 | /// and [optional] paramter called [messageIdGenerator].
9 | /// [messageIdGenerator] take a function with this
10 | /// signature [String Function()]
11 | String? id;
12 |
13 | /// Actual text message.
14 | String? text;
15 |
16 | /// It's a [non-optional] pararmter which specifies the time the
17 | /// message was delivered takes a [DateTime] object.
18 | late DateTime createdAt;
19 |
20 | /// Takes a [ChatUser] object which is used to distinguish between
21 | /// users and also provide avaatar URLs and name.
22 | late ChatUser user;
23 |
24 | /// A [non-optional] parameter which is used to display images
25 | /// takes a [Sring] as a url
26 | String? image;
27 |
28 | /// A [non-optional] parameter which is used to display vedio
29 | /// takes a [Sring] as a url
30 | String? video;
31 |
32 | /// A [non-optional] parameter which is used to show quick replies
33 | /// to the user
34 | QuickReplies? quickReplies;
35 |
36 | /// Allows to set custom-properties that could help with implementing custom
37 | /// functionality to dashchat.
38 | Map? customProperties;
39 |
40 | /// Allows to set buttons that could help with implementing custom
41 | /// actions in message container.
42 | List? buttons;
43 |
44 | ChatMessage(
45 | {String? id,
46 | required this.text,
47 | required this.user,
48 | this.image,
49 | this.video,
50 | this.quickReplies,
51 | String Function()? messageIdGenerator,
52 | DateTime? createdAt,
53 | this.customProperties,
54 | this.buttons}) {
55 | this.createdAt = createdAt != null ? createdAt : DateTime.now();
56 | this.id = id ?? messageIdGenerator?.call() ?? Uuid().v4().toString();
57 | }
58 |
59 | ChatMessage.fromJson(Map json) {
60 | id = json['id'];
61 | text = json['text'];
62 | image = json['image'];
63 | video = json['video'] ?? json['vedio'];
64 | createdAt = DateTime.fromMillisecondsSinceEpoch(json['createdAt']);
65 | user = ChatUser.fromJson(json['user']);
66 | quickReplies = json['quickReplies'] != null
67 | ? QuickReplies.fromJson(json['quickReplies'])
68 | : null;
69 | customProperties = json['customProperties'] as Map?;
70 | }
71 |
72 | Map toJson() {
73 | final Map data = Map();
74 |
75 | try {
76 | data['id'] = this.id;
77 | data['text'] = this.text;
78 | data['image'] = this.image;
79 | data['video'] = this.video;
80 | data['createdAt'] = this.createdAt.millisecondsSinceEpoch;
81 | data['user'] = user.toJson();
82 | data['quickReplies'] = quickReplies?.toJson();
83 | data['customProperties'] = this.customProperties;
84 | } catch (e, stack) {
85 | print('ERROR caught when trying to convert ChatMessage to JSON:');
86 | print(e);
87 | print(stack);
88 | }
89 | return data;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/lib/src/models/chat_user.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | /// ChatUser used to show distinguish between different people
4 | /// in a chat conversation or a chat group
5 | class ChatUser {
6 | /// Unique id of the user if no unique is provided a [UUID v4]
7 | /// is automatically assigned to the chat user.
8 | String? uid;
9 |
10 | /// An [optional] parameter to set the user name.
11 | String? name;
12 |
13 | /// An [optional] parameter to set the user first name, if set will override the name property.
14 | String? firstName;
15 |
16 | /// An [optional] parameter to set the user last name, if set will override the name property.
17 | String? lastName;
18 |
19 | /// An [optional] parameter to set the user avatar.
20 | String? avatar;
21 |
22 | /// An [optional] parameter to set Text Color
23 | Color? color;
24 |
25 | /// An [optional] parameter to set The Message bubble Color
26 | Color? containerColor;
27 |
28 | /// Allows to set custom-properties that could help with implementing custom
29 | /// functionality to dashchat.
30 | Map? customProperties;
31 |
32 | ChatUser({
33 | String? uid,
34 | String? name,
35 | this.avatar,
36 | this.containerColor,
37 | this.color,
38 | this.customProperties,
39 | this.firstName,
40 | this.lastName,
41 | }) {
42 | this.name = name == null ? "$firstName $lastName" : name;
43 | this.uid = uid != null ? uid : Uuid().v4().toString();
44 | }
45 |
46 | ChatUser.fromJson(Map json) {
47 | final pName = json["name"] as String?;
48 |
49 | uid = json['uid'];
50 | name = pName == null ? "$firstName $lastName" : pName;
51 | firstName = json['firstName'];
52 | lastName = json['lastName'];
53 | avatar = json['avatar'];
54 | containerColor =
55 | json['containerColor'] != null ? Color(json['containerColor']) : null;
56 | color = json['color'] != null ? Color(json['color']) : null;
57 | customProperties = json['customProperties'] as Map?;
58 | }
59 |
60 | Map toJson() {
61 | final Map data = Map();
62 |
63 | try {
64 | data['uid'] = uid;
65 | data['name'] = name;
66 | data['firstName'] = firstName;
67 | data['lastName'] = lastName;
68 | data['avatar'] = avatar;
69 | data['containerColor'] =
70 | containerColor != null ? containerColor!.value : null;
71 | data['color'] = color != null ? color!.value : null;
72 | data['customProperties'] = this.customProperties;
73 | } catch (e) {
74 | print(e);
75 | }
76 |
77 | return data;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/lib/src/models/quick_replies.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | /// Quick replies will contain all the replies that should
4 | /// be shown to the user
5 | class QuickReplies {
6 | /// [List] of replies that will be shown to the user
7 | List? values;
8 |
9 | /// Should the quick replies be dismissable or persist
10 | bool? keepIt;
11 |
12 | QuickReplies({
13 | this.keepIt,
14 | this.values = const [],
15 | });
16 |
17 | QuickReplies.fromJson(Map json) {
18 | keepIt = json['keepIt'];
19 |
20 | if (json['values'] != null) {
21 | List replies = [];
22 |
23 | for (var i = 0; i < json['values'].length; i++) {
24 | replies.add(Reply.fromJson(json['values'][i]));
25 | }
26 |
27 | values = replies;
28 | }
29 | }
30 |
31 | Map toJson() {
32 | final Map data = Map();
33 |
34 | data['keepIt'] = keepIt;
35 | data['values'] = values!.map((e) => e.toJson()).toList();
36 |
37 | return data;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/src/models/reply.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | /// Used for providing replies in quick replies
4 | class Reply {
5 | /// Message shown to the user
6 | late String title;
7 |
8 | /// Actual value underneath the message
9 | /// It's an [optioanl] paramter
10 | String? value;
11 |
12 | /// If no messageId is provided it will use [UUID v4] to
13 | /// set a default id for that message
14 | dynamic messageId;
15 |
16 | Reply({
17 | required this.title,
18 | String? messageId,
19 | this.value,
20 | }) {
21 | this.messageId = messageId ?? Uuid().v4().toString();
22 | }
23 |
24 | Reply.fromJson(Map json) {
25 | title = json['title'];
26 | value = json['value'];
27 | messageId = json['messageId'];
28 | }
29 |
30 | Map toJson() {
31 | final Map data = Map();
32 |
33 | data['messageId'] = messageId;
34 | data['title'] = title;
35 | data['value'] = value;
36 |
37 | return data;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/src/models/scroll_to_bottom_style.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | class ScrollToBottomStyle {
4 | /// Background color of the scrollToBottom widget
5 | ///
6 | /// Defaults to theme accent color
7 | Color? backgroundColor;
8 |
9 | /// Text/ Icon color for scrollToBottom widget
10 | ///
11 | /// Defaults to white
12 | Color? textColor;
13 |
14 | /// Height of the scrollToBottom widget
15 | ///
16 | /// Defaults to `45.0`
17 | double height;
18 |
19 | /// Width of the scrollToBottom widget
20 | ///
21 | /// Defaults to `45.0`
22 | double width;
23 |
24 | /// Top absolute position of the widget
25 | ///
26 | /// Defaults to 0.0
27 | double? top;
28 |
29 | /// Left absolute position of the widget
30 | ///
31 | /// Defaults to 0.0
32 | double? left;
33 |
34 | /// Right absolute position of the widget
35 | ///
36 | /// Defaults to 20.0
37 | double right;
38 |
39 | /// Bottom absolute position of the widget
40 | ///
41 | /// Defaults to 60.0
42 | double bottom;
43 |
44 | /// Icon inside the widget
45 | ///
46 | /// Defaults to material icon -> `keyboard_arrow_down`
47 | IconData? icon;
48 |
49 | ScrollToBottomStyle({
50 | this.backgroundColor,
51 | this.textColor,
52 | this.bottom = 70.0,
53 | this.left,
54 | this.right: 20.0,
55 | this.top,
56 | this.height: 45.0,
57 | this.width: 45.0,
58 | this.icon,
59 | });
60 | }
61 |
--------------------------------------------------------------------------------
/lib/src/widgets/avatar_container.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | /// Avatar container for the the chat view uses a [CircleAvatar]
4 | /// widget as default which can be overriden by providing
5 | /// [avatarBuilder] property
6 | class AvatarContainer extends StatelessWidget {
7 | /// A [ChatUser] object use to get the url of the user
8 | /// avatar
9 | final ChatUser user;
10 |
11 | /// [onPress] function takea a function with this structure
12 | /// [Function(ChatUser)] will trigger when the avatar
13 | /// is tapped on
14 | final Function(ChatUser)? onPress;
15 |
16 | /// [onLongPress] function takea a function with this structure
17 | /// [Function(ChatUser)] will trigger when the avatar
18 | /// is long pressed
19 | final Function(ChatUser)? onLongPress;
20 |
21 | /// [avatarBuilder] function takea a function with this structure
22 | /// [Widget Function(ChatUser)] to build the avatar
23 | final Widget Function(ChatUser)? avatarBuilder;
24 |
25 | /// [constraints] to apply to build the layout
26 | /// by default used MediaQuery and take screen size as constaints
27 | final BoxConstraints? constraints;
28 |
29 | final double? avatarMaxSize;
30 |
31 | const AvatarContainer({
32 | required this.user,
33 | this.onPress,
34 | this.onLongPress,
35 | this.avatarBuilder,
36 | this.constraints,
37 | this.avatarMaxSize,
38 | });
39 |
40 | @override
41 | Widget build(BuildContext context) {
42 | final constraints = this.constraints ??
43 | BoxConstraints(
44 | maxHeight: MediaQuery.of(context).size.height,
45 | maxWidth: MediaQuery.of(context).size.width);
46 |
47 | return GestureDetector(
48 | onTap: () => onPress?.call(user),
49 | onLongPress: () => onLongPress?.call(user),
50 | child: avatarBuilder != null
51 | ? avatarBuilder!(user)
52 | : Stack(
53 | alignment: Alignment.center,
54 | children: [
55 | ClipOval(
56 | child: Container(
57 | height: constraints.maxWidth * 0.08,
58 | width: constraints.maxWidth * 0.08,
59 | constraints: BoxConstraints(
60 | maxWidth: avatarMaxSize!,
61 | maxHeight: avatarMaxSize!,
62 | ),
63 | color: Colors.grey,
64 | child: Center(
65 | child: Text(user.name == null || user.name!.isEmpty
66 | ? ''
67 | : user.name![0])),
68 | ),
69 | ),
70 | user.avatar != null && user.avatar!.length != 0
71 | ? Center(
72 | child: ClipOval(
73 | child: FadeInImage.memoryNetwork(
74 | image: user.avatar!,
75 | placeholder: kTransparentImage,
76 | fit: BoxFit.cover,
77 | height: constraints.maxWidth * 0.08,
78 | width: constraints.maxWidth * 0.08,
79 | ),
80 | ),
81 | )
82 | : Container()
83 | ],
84 | ),
85 | );
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/lib/src/widgets/custom_scroll_behaviour.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | /// Custom scroll behaviour for the the [ChatView].
4 | class CustomScrollBehaviour extends ScrollBehavior {
5 | @override
6 | Widget buildViewportChrome(
7 | BuildContext context, Widget child, AxisDirection axisDirection) {
8 | return child;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/lib/src/widgets/date_builder.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | class DateBuilder extends StatelessWidget {
4 | DateBuilder({
5 | required this.date,
6 | this.customDateBuilder,
7 | this.dateFormat,
8 | });
9 |
10 | final DateTime date;
11 | final Widget Function(String)? customDateBuilder;
12 | final DateFormat? dateFormat;
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return customDateBuilder?.call(dateFormat?.format(date) ??
17 | DateFormat('yyyy-MM-dd').format(date)) ??
18 | Container(
19 | decoration: BoxDecoration(
20 | color: Colors.grey,
21 | borderRadius: BorderRadius.circular(10.0),
22 | ),
23 | padding: EdgeInsets.only(
24 | bottom: 5.0,
25 | top: 5.0,
26 | left: 10.0,
27 | right: 10.0,
28 | ),
29 | margin: EdgeInsets.symmetric(vertical: 10.0),
30 | child: Text(
31 | dateFormat != null
32 | ? dateFormat!.format(date)
33 | : DateFormat('yyyy-MMM-dd').format(date),
34 | style: TextStyle(
35 | color: Colors.white,
36 | fontSize: 12.0,
37 | ),
38 | ),
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/src/widgets/load_earlier.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | class LoadEarlierWidget extends StatelessWidget {
4 | const LoadEarlierWidget({
5 | Key? key,
6 | this.onLoadEarlier,
7 | required this.defaultLoadCallback,
8 | }) : super(key: key);
9 |
10 | final Function? onLoadEarlier;
11 | final Function(bool) defaultLoadCallback;
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return GestureDetector(
16 | onTap: () {
17 | onLoadEarlier?.call();
18 | defaultLoadCallback(false);
19 | },
20 | child: Container(
21 | padding: EdgeInsets.symmetric(
22 | horizontal: 12.0,
23 | vertical: 5.0,
24 | ),
25 | decoration: BoxDecoration(
26 | color: Colors.white,
27 | borderRadius: BorderRadius.circular(20.0),
28 | boxShadow: [
29 | BoxShadow(
30 | spreadRadius: 1.0,
31 | blurRadius: 5.0,
32 | color: Color.fromRGBO(0, 0, 0, 0.2),
33 | offset: Offset(0, 10),
34 | )
35 | ]),
36 | child: Text(
37 | "Load Earlier Messages",
38 | style: TextStyle(
39 | color: Theme.of(context).primaryColor,
40 | ),
41 | ),
42 | ),
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/src/widgets/message_container.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | /// MessageContainer is just a wrapper around [Text], [Image]
4 | /// component to present the message
5 | class MessageContainer extends StatelessWidget {
6 | /// Message Object that will be rendered
7 | /// Takes a [ChatMessage] object
8 | final ChatMessage message;
9 |
10 | /// [DateFormat] object to render the date in desired
11 | /// format, if no format is provided it use
12 | /// the default `HH:mm:ss`
13 | final DateFormat? timeFormat;
14 |
15 | /// [messageTextBuilder] function takes a function with this
16 | /// structure [Widget Function(String)] to render the text inside
17 | /// the container.
18 | final Widget Function(String?, [ChatMessage])? messageTextBuilder;
19 |
20 | /// [messageImageBuilder] function takes a function with this
21 | /// structure [Widget Function(String)] to render the image inside
22 | /// the container.
23 | final Widget Function(String?, [ChatMessage])? messageImageBuilder;
24 |
25 | /// [messageTimeBuilder] function takes a function with this
26 | /// structure [Widget Function(String)] to render the time text inside
27 | /// the container.
28 | final Widget Function(String, [ChatMessage])? messageTimeBuilder;
29 |
30 | /// Provides a custom style to the message container
31 | /// takes [BoxDecoration]
32 | final BoxDecoration? messageContainerDecoration;
33 |
34 | /// Used to parse text to make it linkified text uses
35 | /// [flutter_parsed_text](https://pub.dev/packages/flutter_parsed_text)
36 | /// takes a list of [MatchText] in order to parse Email, phone, links
37 | /// and can also add custom pattersn using regex
38 | final List parsePatterns;
39 |
40 | /// A flag which is used for assiging styles
41 | final bool isUser;
42 |
43 | /// Provides a list of buttons to allow the usage of adding buttons to
44 | /// the bottom of the message
45 | final List? buttons;
46 |
47 | /// [messageButtonsBuilder] function takes a function with this
48 | /// structure [List Function()] to render the buttons inside
49 | /// a row.
50 | final List Function(ChatMessage)? messageButtonsBuilder;
51 |
52 | /// Constraint to use to build the message layout
53 | final BoxConstraints? constraints;
54 |
55 | /// Padding of the message
56 | /// Default to EdgeInsets.all(8.0)
57 | final EdgeInsets messagePadding;
58 |
59 | /// Should show the text before the image in the chat bubble
60 | /// or the opposite
61 | /// Default to `true`
62 | final bool textBeforeImage;
63 |
64 | /// overrides the boxdecoration of the message
65 | /// can be used to override color, or customise the message container
66 | /// params [ChatMessage] and [isUser]: boolean
67 | /// return BoxDecoration
68 | final BoxDecoration Function(ChatMessage, bool?)? messageDecorationBuilder;
69 |
70 | const MessageContainer({
71 | required this.message,
72 | required this.timeFormat,
73 | this.constraints,
74 | this.messageImageBuilder,
75 | this.messageTextBuilder,
76 | this.messageTimeBuilder,
77 | this.messageContainerDecoration,
78 | this.parsePatterns = const [],
79 | this.textBeforeImage = true,
80 | required this.isUser,
81 | this.messageButtonsBuilder,
82 | this.buttons,
83 | this.messagePadding = const EdgeInsets.all(8.0),
84 | this.messageDecorationBuilder,
85 | });
86 |
87 | @override
88 | Widget build(BuildContext context) {
89 | final constraints = this.constraints ??
90 | BoxConstraints(
91 | maxHeight: MediaQuery.of(context).size.height,
92 | maxWidth: MediaQuery.of(context).size.width);
93 | return ConstrainedBox(
94 | constraints: BoxConstraints(
95 | maxWidth: constraints.maxWidth * 0.8,
96 | ),
97 | child: Container(
98 | decoration: messageDecorationBuilder?.call(message, isUser) ??
99 | messageContainerDecoration?.copyWith(
100 | color: message.user.containerColor != null
101 | ? message.user.containerColor
102 | : messageContainerDecoration!.color,
103 | ) ??
104 | BoxDecoration(
105 | color: message.user.containerColor ??
106 | (isUser
107 | ? Theme.of(context).accentColor
108 | : Color.fromRGBO(225, 225, 225, 1)),
109 | borderRadius: BorderRadius.circular(5.0),
110 | ),
111 | margin: EdgeInsets.only(
112 | bottom: 5.0,
113 | ),
114 | padding: messagePadding,
115 | child: Column(
116 | mainAxisAlignment: MainAxisAlignment.end,
117 | crossAxisAlignment:
118 | isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
119 | children: [
120 | if (this.textBeforeImage)
121 | _buildMessageText()
122 | else
123 | _buildMessageImage(),
124 | if (this.textBeforeImage)
125 | _buildMessageImage()
126 | else
127 | _buildMessageText(),
128 | if (buttons != null)
129 | Row(
130 | crossAxisAlignment: CrossAxisAlignment.center,
131 | mainAxisAlignment:
132 | isUser ? MainAxisAlignment.end : MainAxisAlignment.start,
133 | mainAxisSize: MainAxisSize.min,
134 | children: buttons!,
135 | )
136 | else if (messageButtonsBuilder != null)
137 | Row(
138 | crossAxisAlignment: CrossAxisAlignment.center,
139 | mainAxisAlignment:
140 | isUser ? MainAxisAlignment.end : MainAxisAlignment.start,
141 | children: messageButtonsBuilder!(message),
142 | mainAxisSize: MainAxisSize.min,
143 | ),
144 | messageTimeBuilder?.call(
145 | timeFormat?.format(message.createdAt) ??
146 | DateFormat('HH:mm:ss').format(message.createdAt),
147 | message,
148 | ) ??
149 | Padding(
150 | padding: EdgeInsets.only(top: 5.0),
151 | child: Text(
152 | timeFormat != null
153 | ? timeFormat!.format(message.createdAt)
154 | : DateFormat('HH:mm:ss').format(message.createdAt),
155 | style: TextStyle(
156 | fontSize: 10.0,
157 | color: message.user.color ??
158 | (isUser ? Colors.white70 : Colors.black87),
159 | ),
160 | ),
161 | )
162 | ],
163 | ),
164 | ),
165 | );
166 | }
167 |
168 | Widget _buildMessageText() {
169 | return messageTextBuilder?.call(message.text, message) ??
170 | ParsedText(
171 | parse: parsePatterns,
172 | text: message.text!,
173 | style: TextStyle(
174 | color: message.user.color ??
175 | (isUser ? Colors.white70 : Colors.black87),
176 | ),
177 | );
178 | }
179 |
180 | Widget _buildMessageImage() {
181 | if (message.image != null) {
182 | return messageImageBuilder?.call(message.image, message) ??
183 | Padding(
184 | padding: EdgeInsets.only(top: 10.0, bottom: 10.0),
185 | child: FadeInImage.memoryNetwork(
186 | height: constraints!.maxHeight * 0.3,
187 | width: constraints!.maxWidth * 0.7,
188 | fit: BoxFit.contain,
189 | placeholder: kTransparentImage,
190 | image: message.image!,
191 | ),
192 | );
193 | }
194 | return SizedBox(width: 0, height: 0);
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/lib/src/widgets/quick_reply.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | class QuickReply extends StatelessWidget {
4 | final Reply reply;
5 |
6 | final Function(Reply)? onReply;
7 |
8 | final BoxDecoration? quickReplyStyle;
9 |
10 | final TextStyle? quickReplyTextStyle;
11 |
12 | final Widget Function(Reply)? quickReplyBuilder;
13 |
14 | final BoxConstraints? constraints;
15 |
16 | const QuickReply({
17 | this.quickReplyBuilder,
18 | this.quickReplyStyle,
19 | this.quickReplyTextStyle,
20 | this.constraints,
21 | this.onReply,
22 | required this.reply,
23 | });
24 |
25 | @override
26 | Widget build(BuildContext context) {
27 | final constraints = this.constraints ??
28 | BoxConstraints(
29 | maxHeight: MediaQuery.of(context).size.height,
30 | maxWidth: MediaQuery.of(context).size.width);
31 | return GestureDetector(
32 | onTap: () {
33 | onReply?.call(reply);
34 | },
35 | child: quickReplyBuilder?.call(reply) ??
36 | Container(
37 | margin:
38 | EdgeInsets.only(left: 5.0, right: 5.0, top: 5.0, bottom: 10.0),
39 | padding: EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
40 | decoration: quickReplyStyle ??
41 | BoxDecoration(
42 | border: Border.all(
43 | width: 1.0, color: Theme.of(context).accentColor),
44 | borderRadius: BorderRadius.circular(5.0),
45 | ),
46 | constraints: BoxConstraints(maxWidth: constraints.maxWidth / 3),
47 | child: Text(
48 | reply.title,
49 | style: quickReplyTextStyle ??
50 | TextStyle(
51 | color: Theme.of(context).accentColor,
52 | fontSize: 12.0,
53 | ),
54 | ),
55 | ),
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/lib/src/widgets/scroll_to_bottom.dart:
--------------------------------------------------------------------------------
1 | part of dash_chat;
2 |
3 | class ScrollToBottom extends StatelessWidget {
4 | final Function? onScrollToBottomPress;
5 | final ScrollController scrollController;
6 | final bool inverted;
7 | final ScrollToBottomStyle scrollToBottomStyle;
8 |
9 | ScrollToBottom({
10 | this.onScrollToBottomPress,
11 | required this.scrollController,
12 | required this.inverted,
13 | required this.scrollToBottomStyle,
14 | });
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return Container(
19 | width: scrollToBottomStyle.width,
20 | height: scrollToBottomStyle.height,
21 | child: RawMaterialButton(
22 | elevation: 5,
23 | fillColor: scrollToBottomStyle.backgroundColor ??
24 | Theme.of(context).primaryColor,
25 | shape: CircleBorder(),
26 | child: Icon(
27 | scrollToBottomStyle.icon ?? Icons.keyboard_arrow_down,
28 | color: scrollToBottomStyle.textColor ?? Colors.white,
29 | ),
30 | onPressed: () {
31 | onScrollToBottomPress?.call() ??
32 | scrollController.animateTo(
33 | inverted
34 | ? 0.0
35 | : scrollController.position.maxScrollExtent + 25.0,
36 | duration: const Duration(milliseconds: 300),
37 | curve: Curves.easeInOut,
38 | );
39 | },
40 | ),
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.5.0"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.1.0"
18 | characters:
19 | dependency: transitive
20 | description:
21 | name: characters
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.1.0"
25 | charcode:
26 | dependency: transitive
27 | description:
28 | name: charcode
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.2.0"
32 | clock:
33 | dependency: transitive
34 | description:
35 | name: clock
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.1.0"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.15.0"
46 | crypto:
47 | dependency: transitive
48 | description:
49 | name: crypto
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "3.0.0"
53 | fake_async:
54 | dependency: transitive
55 | description:
56 | name: fake_async
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "1.2.0"
60 | flutter:
61 | dependency: "direct main"
62 | description: flutter
63 | source: sdk
64 | version: "0.0.0"
65 | flutter_parsed_text:
66 | dependency: "direct main"
67 | description:
68 | name: flutter_parsed_text
69 | url: "https://pub.dartlang.org"
70 | source: hosted
71 | version: "2.1.0"
72 | flutter_test:
73 | dependency: "direct dev"
74 | description: flutter
75 | source: sdk
76 | version: "0.0.0"
77 | intl:
78 | dependency: "direct main"
79 | description:
80 | name: intl
81 | url: "https://pub.dartlang.org"
82 | source: hosted
83 | version: "0.17.0"
84 | matcher:
85 | dependency: transitive
86 | description:
87 | name: matcher
88 | url: "https://pub.dartlang.org"
89 | source: hosted
90 | version: "0.12.10"
91 | meta:
92 | dependency: transitive
93 | description:
94 | name: meta
95 | url: "https://pub.dartlang.org"
96 | source: hosted
97 | version: "1.3.0"
98 | path:
99 | dependency: transitive
100 | description:
101 | name: path
102 | url: "https://pub.dartlang.org"
103 | source: hosted
104 | version: "1.8.0"
105 | sky_engine:
106 | dependency: transitive
107 | description: flutter
108 | source: sdk
109 | version: "0.0.99"
110 | source_span:
111 | dependency: transitive
112 | description:
113 | name: source_span
114 | url: "https://pub.dartlang.org"
115 | source: hosted
116 | version: "1.8.0"
117 | stack_trace:
118 | dependency: transitive
119 | description:
120 | name: stack_trace
121 | url: "https://pub.dartlang.org"
122 | source: hosted
123 | version: "1.10.0"
124 | stream_channel:
125 | dependency: transitive
126 | description:
127 | name: stream_channel
128 | url: "https://pub.dartlang.org"
129 | source: hosted
130 | version: "2.1.0"
131 | string_scanner:
132 | dependency: transitive
133 | description:
134 | name: string_scanner
135 | url: "https://pub.dartlang.org"
136 | source: hosted
137 | version: "1.1.0"
138 | term_glyph:
139 | dependency: transitive
140 | description:
141 | name: term_glyph
142 | url: "https://pub.dartlang.org"
143 | source: hosted
144 | version: "1.2.0"
145 | test_api:
146 | dependency: transitive
147 | description:
148 | name: test_api
149 | url: "https://pub.dartlang.org"
150 | source: hosted
151 | version: "0.2.19"
152 | transparent_image:
153 | dependency: "direct main"
154 | description:
155 | name: transparent_image
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "2.0.0"
159 | typed_data:
160 | dependency: transitive
161 | description:
162 | name: typed_data
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "1.3.0"
166 | uuid:
167 | dependency: "direct main"
168 | description:
169 | name: uuid
170 | url: "https://pub.dartlang.org"
171 | source: hosted
172 | version: "3.0.4"
173 | vector_math:
174 | dependency: transitive
175 | description:
176 | name: vector_math
177 | url: "https://pub.dartlang.org"
178 | source: hosted
179 | version: "2.1.0"
180 | sdks:
181 | dart: ">=2.12.0 <3.0.0"
182 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: dash_chat
2 | description: The most complete Chat UI for flutter inspired by react-native-gifted-chat.
3 | version: 1.1.16
4 | homepage: https://github.com/fayeed/dash_chat
5 |
6 | environment:
7 | sdk: '>=2.12.0 <3.0.0'
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 | flutter_parsed_text: ^2.1.0
13 | uuid: ^3.0.4
14 | intl: ^0.17.0
15 | transparent_image: ^2.0.0
16 |
17 | dev_dependencies:
18 | flutter_test:
19 | sdk: flutter
20 |
21 | # The following section is specific to Flutter.
22 | flutter:
23 | # To add assets to your package, add an assets section, like this:
24 | # assets:
25 | # - images/a_dot_burr.jpeg
26 | # - images/a_dot_ham.jpeg
27 | #
28 | # For details regarding assets in packages, see
29 | # https://flutter.dev/assets-and-images/#from-packages
30 | #
31 | # An image asset can refer to one or more resolution-specific "variants", see
32 | # https://flutter.dev/assets-and-images/#resolution-aware.
33 | # To add custom fonts to your package, add a fonts section here,
34 | # in this "flutter" section. Each entry in this list should have a
35 | # "family" key with the font family name, and a "fonts" key with a
36 | # list giving the asset and other descriptors for the font. For
37 | # example:
38 | # fonts:
39 | # - family: Schyler
40 | # fonts:
41 | # - asset: fonts/Schyler-Regular.ttf
42 | # - asset: fonts/Schyler-Italic.ttf
43 | # style: italic
44 | # - family: Trajan Pro
45 | # fonts:
46 | # - asset: fonts/TrajanPro.ttf
47 | # - asset: fonts/TrajanPro_Bold.ttf
48 | # weight: 700
49 | #
50 | # For details regarding fonts in packages, see
51 | # https://flutter.dev/custom-fonts/#from-packages
52 |
--------------------------------------------------------------------------------
/test/chat_user_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 |
3 | import 'package:dash_chat/dash_chat.dart';
4 |
5 | void main() {
6 | test('can instantiate a ChatUser passing a name', () {
7 | ChatUser chatUser = ChatUser(
8 | name: 'my-name',
9 | );
10 |
11 | expect(chatUser.name, 'my-name');
12 | });
13 |
14 | test('can instantiate a ChatUser passing a first and last name', () {
15 | ChatUser chatUser = ChatUser(
16 | firstName: 'first',
17 | lastName: 'last',
18 | );
19 |
20 | expect(chatUser.name, 'first last');
21 | });
22 | }
23 |
--------------------------------------------------------------------------------
/test/dash_chat_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 |
3 | import 'package:dash_chat/dash_chat.dart';
4 |
5 | void main() {}
6 |
--------------------------------------------------------------------------------