diff --git a/images/imessage_clone_hero_v1.png b/images/imessage_clone_hero_v1.png new file mode 100644 index 0000000..46ce283 Binary files /dev/null and b/images/imessage_clone_hero_v1.png differ diff --git a/imessage/README.md b/imessage/README.md index a480ef0..e6d0f7c 100644 --- a/imessage/README.md +++ b/imessage/README.md @@ -1,16 +1,53 @@ -# imessage +# iMessage Clone -A new Flutter project. +![](https://raw.githubusercontent.com/GetStream/flutter-samples/master/images/imessage_clone_hero_v1.png) + +iMessage Clone is a sample app implemented using Stream Chat and Flutter. It is a fully fledged messaging app built using core packages. ## Getting Started -This project is a starting point for a Flutter application. +Before running this project please ensure Flutter is installed and configured on your machine. If you're new to Flutter, please checkout the [official guide](https://flutter.dev/docs/get-started/install) with installation instructions for your OS. -A few resources to get you started if this is your first Flutter project: + -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) +This project is only configured to support the following platforms: -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +- Android +- iOS + +Web and Desktop are not supported at this time. + +After installing Flutter and the necessary toolchain for your device (Android or iOS), connect your device or open your emulator before running the following: + +**Clone the repo** + +```bash +git clone https://github.com/GetStream/flutter-samples +``` + +**Open the app folder** + +```bash +cd flutter-samples/imessage +``` + +**Install package dependencies:** + +```bash +flutter packages get +``` + +**Open or create an emulator** + +```bash +# To run an emulator, run 'flutter emulators --launch '. +# To create a new emulator, run 'flutter emulators --create [--name xyz]'. +# You can find more information on managing emulators at the links below: +# [https://developer.android.com/studio/run/managing-avds](https://developer.android.com/studio/run/managing-avds)[https://developer.android.com/studio/command-line/avdmanager](https://developer.android.com/studio/command-line/avdmanager) +``` + +**Run the project on your device or emulator:** + +```bash +flutter run +``` diff --git a/imessage/android/app/build.gradle b/imessage/android/app/build.gradle index dbbb4d7..f0bd488 100644 --- a/imessage/android/app/build.gradle +++ b/imessage/android/app/build.gradle @@ -35,7 +35,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.imessage" - minSdkVersion 16 + minSdkVersion 21 targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/imessage/android/gradle.properties b/imessage/android/gradle.properties index 94adc3a..a673820 100644 --- a/imessage/android/gradle.properties +++ b/imessage/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +android.enableR8=true diff --git a/imessage/ios/Flutter/AppFrameworkInfo.plist b/imessage/ios/Flutter/AppFrameworkInfo.plist index 9367d48..beffb28 100644 --- a/imessage/ios/Flutter/AppFrameworkInfo.plist +++ b/imessage/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable App CFBundleIdentifier @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 10.0 diff --git a/imessage/ios/Flutter/Debug.xcconfig b/imessage/ios/Flutter/Debug.xcconfig index 96e9085..e8efba1 100644 --- a/imessage/ios/Flutter/Debug.xcconfig +++ b/imessage/ios/Flutter/Debug.xcconfig @@ -1,3 +1,2 @@ #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/imessage/ios/Flutter/Release.xcconfig b/imessage/ios/Flutter/Release.xcconfig index 388e6ff..399e934 100644 --- a/imessage/ios/Flutter/Release.xcconfig +++ b/imessage/ios/Flutter/Release.xcconfig @@ -1,3 +1,2 @@ #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/imessage/ios/Runner.xcodeproj/project.pbxproj b/imessage/ios/Runner.xcodeproj/project.pbxproj index b1cf8af..fa8ab5a 100644 --- a/imessage/ios/Runner.xcodeproj/project.pbxproj +++ b/imessage/ios/Runner.xcodeproj/project.pbxproj @@ -9,11 +9,11 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 54127ED34F2B67975767881F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F124A271054C4FEB7BA298EA /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + AD80798B75BEF39A28B8324C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0EA12FA64E96796BC730E03 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -30,14 +30,15 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 10AA3A20A8E8F51016843518 /* 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 = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 1DFBF673D502906C56E28E7F /* 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 = ""; }; + 212173E324AE0FED45F2866A /* 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 = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7851D9CF8A0104A48AF22BF4 /* 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 = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 87BC4F0CB1D55BC3C77185AD /* 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 = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -45,8 +46,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F124A271054C4FEB7BA298EA /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F6E360FB0F19A8EE63C4281A /* 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 = ""; }; + D0EA12FA64E96796BC730E03 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,27 +54,27 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 54127ED34F2B67975767881F /* Pods_Runner.framework in Frameworks */, + AD80798B75BEF39A28B8324C /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 053DC9E602F5F5F21DC144A5 /* Frameworks */ = { + 3F98BA59D9BB09C5816FF126 /* Frameworks */ = { isa = PBXGroup; children = ( - F124A271054C4FEB7BA298EA /* Pods_Runner.framework */, + D0EA12FA64E96796BC730E03 /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; }; - 15FB9768D11A361EAB687B81 /* Pods */ = { + 7231B8C27F5ACE1DDE056446 /* Pods */ = { isa = PBXGroup; children = ( - 1DFBF673D502906C56E28E7F /* Pods-Runner.debug.xcconfig */, - 7851D9CF8A0104A48AF22BF4 /* Pods-Runner.release.xcconfig */, - F6E360FB0F19A8EE63C4281A /* Pods-Runner.profile.xcconfig */, + 87BC4F0CB1D55BC3C77185AD /* Pods-Runner.debug.xcconfig */, + 10AA3A20A8E8F51016843518 /* Pods-Runner.release.xcconfig */, + 212173E324AE0FED45F2866A /* Pods-Runner.profile.xcconfig */, ); name = Pods; path = Pods; @@ -97,8 +97,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - 15FB9768D11A361EAB687B81 /* Pods */, - 053DC9E602F5F5F21DC144A5 /* Frameworks */, + 7231B8C27F5ACE1DDE056446 /* Pods */, + 3F98BA59D9BB09C5816FF126 /* Frameworks */, ); sourceTree = ""; }; @@ -132,14 +132,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - F8BE585E4B71465AB994DDE3 /* [CP] Check Pods Manifest.lock */, + CAA82304056351F698F2FE92 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - FF3AE7DF11C51CD94F69B7CD /* [CP] Embed Pods Frameworks */, + 5C9569EFDB9DD32B2BAFBEBB /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -212,6 +212,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 5C9569EFDB9DD32B2BAFBEBB /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -226,7 +243,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - F8BE585E4B71465AB994DDE3 /* [CP] Check Pods Manifest.lock */ = { + CAA82304056351F698F2FE92 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -248,64 +265,6 @@ 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"; showEnvVarsInLog = 0; }; - FF3AE7DF11C51CD94F69B7CD /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework", - "${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework", - "${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework", - "${PODS_ROOT}/../Flutter/Flutter.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework", - "${BUILT_PRODUCTS_DIR}/esys_flutter_share/esys_flutter_share.framework", - "${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework", - "${BUILT_PRODUCTS_DIR}/flutter_app_badger/flutter_app_badger.framework", - "${BUILT_PRODUCTS_DIR}/flutter_keyboard_visibility/flutter_keyboard_visibility.framework", - "${BUILT_PRODUCTS_DIR}/image_gallery_saver/image_gallery_saver.framework", - "${BUILT_PRODUCTS_DIR}/image_picker/image_picker.framework", - "${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework", - "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", - "${BUILT_PRODUCTS_DIR}/photo_manager/photo_manager.framework", - "${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework", - "${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework", - "${BUILT_PRODUCTS_DIR}/video_compress/video_compress.framework", - "${BUILT_PRODUCTS_DIR}/video_player/video_player.framework", - "${BUILT_PRODUCTS_DIR}/video_thumbnail/video_thumbnail.framework", - "${BUILT_PRODUCTS_DIR}/wakelock/wakelock.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/esys_flutter_share.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_app_badger.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_keyboard_visibility.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_gallery_saver.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/photo_manager.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_compress.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_thumbnail.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -398,8 +357,16 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.imessage; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -522,8 +489,16 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.imessage; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -541,8 +516,16 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.imessage; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/imessage/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/imessage/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/imessage/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/imessage/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/imessage/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/imessage/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/imessage/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/imessage/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c..0000000 --- a/imessage/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/imessage/lib/channel_list_view.dart b/imessage/lib/channel_list_view.dart index 1ae04a4..89c03b5 100644 --- a/imessage/lib/channel_list_view.dart +++ b/imessage/lib/channel_list_view.dart @@ -13,35 +13,38 @@ class ChannelListView extends StatelessWidget { channels.removeWhere((channel) => channel.lastMessageAt == null); return SliverList( delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - //TODO: separators - return Column( - children: [ - ChannelPreview( - channel: channels[index], - onTap: () { - Navigator.push( - context, - PageRouteBuilder( - pageBuilder: (_, __, ___) => StreamChannel( - channel: channels[index], - child: MessagePage(), - ), - transitionsBuilder: ( - _, - animation, - secondaryAnimation, - child, - ) => - SharedAxisTransition( - child: child, - animation: animation, - secondaryAnimation: secondaryAnimation, - transitionType: SharedAxisTransitionType.horizontal, - ), - )); - }) - ], + ( + BuildContext context, + int index, + ) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: ChannelPreview( + channel: channels[index], + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (_, __, ___) => StreamChannel( + channel: channels[index], + child: MessagePage(), + ), + transitionsBuilder: ( + _, + animation, + secondaryAnimation, + child, + ) => + SharedAxisTransition( + child: child, + animation: animation, + secondaryAnimation: secondaryAnimation, + transitionType: SharedAxisTransitionType.horizontal, + ), + ), + ); + }, + ), ); }, childCount: channels.length, diff --git a/imessage/lib/channel_name_text.dart b/imessage/lib/channel_name_text.dart index a733f85..e2e8d2b 100644 --- a/imessage/lib/channel_name_text.dart +++ b/imessage/lib/channel_name_text.dart @@ -2,8 +2,11 @@ import 'package:flutter/cupertino.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart' show Channel; class ChannelNameText extends StatelessWidget { - const ChannelNameText({Key key, @required this.channel, this.size = 12}) - : super(key: key); + const ChannelNameText({ + Key key, + @required this.channel, + this.size = 17, + }) : super(key: key); final Channel channel; final double size; @@ -11,11 +14,12 @@ class ChannelNameText extends StatelessWidget { @override Widget build(BuildContext context) { return Text( - channel.extraData['name'] as String ?? channel.config.name, + channel.extraData['name'] as String ?? 'No name', style: TextStyle( - fontSize: size, - fontWeight: FontWeight.bold, - color: CupertinoColors.black), + fontSize: size, + fontWeight: FontWeight.bold, + color: CupertinoColors.black, + ), ); } } diff --git a/imessage/lib/channel_preview.dart b/imessage/lib/channel_preview.dart index bc61db3..8b8c039 100644 --- a/imessage/lib/channel_preview.dart +++ b/imessage/lib/channel_preview.dart @@ -9,6 +9,7 @@ import 'utils.dart'; class ChannelPreview extends StatelessWidget { final VoidCallback onTap; final Channel channel; + const ChannelPreview({ Key key, @required this.onTap, @@ -24,9 +25,9 @@ class ChannelPreview extends StatelessWidget { ? lastMessage?.attachments //TODO: ugly ?.map((e) { if (e.type == 'image') { - return '📷'; + return '📷 '; } else if (e.type == 'video') { - return '🎬'; + return '🎬 '; } return null; }) @@ -35,57 +36,78 @@ class ChannelPreview extends StatelessWidget { : ''; return GestureDetector( onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - color: CupertinoColors.white), + behavior: HitTestBehavior.opaque, + child: Container( + constraints: BoxConstraints.tightFor( + height: 90, + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + ), child: Row( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - ChannelImage(channel: channel, size: 50), + Padding( + padding: + const EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0), + child: ChannelImage( + channel: channel, + size: 50, + ), + ), Expanded( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8.0), - child: ChannelNameText( - channel: channel, - ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: ChannelNameText( + channel: channel, ), - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8.0), - child: Text( - isSameWeek(channel.lastMessageAt) - ? formatDateSameWeek(channel.lastMessageAt) - : formatDate(channel.lastMessageAt), - style: TextStyle( - fontWeight: FontWeight.bold, - color: CupertinoColors.systemGrey), - ), - ) - ], - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - '$prefix ${lastMessage?.text ?? ''}', - style: TextStyle( - fontWeight: FontWeight.normal, - color: CupertinoColors.systemGrey), - overflow: TextOverflow.ellipsis, ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Row( + children: [ + Text( + isSameWeek(channel.lastMessageAt) + ? formatDateSameWeek(channel.lastMessageAt) + : formatDate(channel.lastMessageAt), + style: TextStyle( + fontSize: 15, + color: CupertinoColors.systemGrey, + ), + ), + Icon( + CupertinoIcons.right_chevron, + color: CupertinoColors.systemGrey3, + ), + ], + ), + ) + ], + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + '$prefix${lastMessage?.text ?? ''}', + style: TextStyle( + fontWeight: FontWeight.normal, + color: CupertinoColors.systemGrey, + fontSize: 16, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, ), - ], - ), + ), + Divider(), + ], ), ) ], diff --git a/imessage/lib/main.dart b/imessage/lib/main.dart index b74c149..f235f41 100644 --- a/imessage/lib/main.dart +++ b/imessage/lib/main.dart @@ -43,6 +43,7 @@ class IMessage extends StatelessWidget { initializeDateFormatting('en_US', null); return CupertinoApp( title: 'Flutter Demo', + debugShowCheckedModeBanner: false, theme: CupertinoThemeData(brightness: Brightness.light), home: StreamChatCore(client: client, child: ChatLoader()), ); @@ -50,31 +51,26 @@ class IMessage extends StatelessWidget { } class ChatLoader extends StatelessWidget { - const ChatLoader({ + ChatLoader({ Key key, }) : super(key: key); + final channelListController = ChannelListController(); + @override Widget build(BuildContext context) { final user = StreamChatCore.of(context).user; - var channelListController = ChannelListController(); return CupertinoPageScaffold( child: ChannelsBloc( child: ChannelListCore( channelListController: channelListController, filter: { - r'$and': [ - { - 'members': { - r'$in': [user.id], - } - }, - { - 'type': { - r'$eq': 'messaging', - } - } - ] + 'members': { + r'$in': [user.id], + }, + 'type': { + r'$eq': 'messaging', + }, }, sort: [SortOption('last_message_at')], pagination: PaginationParams( @@ -108,13 +104,18 @@ class ChatLoader extends StatelessWidget { onEndOfPage: () async { channelListController.paginateData(); }, - child: CustomScrollView(slivers: [ - CupertinoSliverRefreshControl(onRefresh: () async { - channelListController.loadData(); - }), - ChannelPageAppBar(), - ChannelListView(channels: channels) - ]), + child: CustomScrollView( + slivers: [ + CupertinoSliverRefreshControl(onRefresh: () async { + channelListController.loadData(); + }), + ChannelPageAppBar(), + SliverPadding( + sliver: ChannelListView(channels: channels), + padding: const EdgeInsets.only(top: 16), + ) + ], + ), )))); } } diff --git a/imessage/lib/message_header.dart b/imessage/lib/message_header.dart index 4ad170f..31ae34b 100644 --- a/imessage/lib/message_header.dart +++ b/imessage/lib/message_header.dart @@ -9,8 +9,10 @@ class MessageHeader extends StatelessWidget { @override Widget build(BuildContext context) { final receivedAt = DateTime.parse(rawTimeStamp); - var textStyle = TextStyle( - fontWeight: FontWeight.bold, color: CupertinoColors.systemGrey); + final textStyle = TextStyle( + color: CupertinoColors.systemGrey, + fontSize: 14, + ); return isSameWeek(receivedAt) ? Text( formatDateSameWeek(receivedAt), diff --git a/imessage/lib/message_list_view.dart b/imessage/lib/message_list_view.dart index 6e73a7a..e12572c 100644 --- a/imessage/lib/message_list_view.dart +++ b/imessage/lib/message_list_view.dart @@ -12,48 +12,52 @@ class MessageListView extends StatelessWidget { @override Widget build(BuildContext context) { - final entries = groupBy(messages.reversed, + final entries = groupBy(messages, (Message message) => message.createdAt.toString().substring(0, 10)) .entries .toList(); - return Stack( + return Column( children: [ - SizedBox( - height: MediaQuery.of(context).size.height * 0.9, - child: Align( - alignment: FractionalOffset.topCenter, - child: ListView.builder( - shrinkWrap: true, - itemCount: entries.length, - itemBuilder: (context, index) { - - return Column( - children: [ - Padding( - padding: - const EdgeInsets.fromLTRB(8.0, 24.0, 8.0, 8.0), - child: MessageHeader( - rawTimeStamp: entries[index].key), //date - ), - ...entries[index].value //messages - .map((message) { - return MessageWidget( - alignment: isReceived(message, context) - ? Alignment.centerLeft - : Alignment.topRight, - color: isReceived(message, context) - ? CupertinoColors.systemGrey5 - : CupertinoColors.systemBlue, - messageColor: isReceived(message, context) - ? CupertinoColors.black - : CupertinoColors.white, - message: message, - ); - }).toList() - ], - ); - }), - )), + Expanded( + child: SizedBox( + height: MediaQuery.of(context).size.height * 0.9, + child: Align( + alignment: FractionalOffset.topCenter, + child: ListView.builder( + reverse: true, + itemCount: entries.length, + itemBuilder: (context, index) { + return Column( + children: [ + Padding( + padding: + const EdgeInsets.fromLTRB(8.0, 24.0, 8.0, 8.0), + child: MessageHeader( + rawTimeStamp: entries[index].key), //date + ), + ...entries[index] + .value //messages + .map((message) { + return MessageWidget( + alignment: isReceived(message, context) + ? Alignment.centerLeft + : Alignment.topRight, + color: isReceived(message, context) + ? CupertinoColors.systemGrey5 + : CupertinoColors.systemBlue, + messageColor: isReceived(message, context) + ? CupertinoColors.black + : CupertinoColors.white, + message: message, + ); + }) + .toList() + .reversed, + ], + ); + }), + )), + ), MessageInput() ], ); diff --git a/imessage/lib/message_page.dart b/imessage/lib/message_page.dart index 800710e..01b555e 100644 --- a/imessage/lib/message_page.dart +++ b/imessage/lib/message_page.dart @@ -21,8 +21,14 @@ class MessagePage extends StatelessWidget { navigationBar: CupertinoNavigationBar( middle: Column( children: [ - ChannelImage(size: 25, channel: streamChannel.channel), - ChannelNameText(channel: streamChannel.channel) + ChannelImage( + size: 25, + channel: streamChannel.channel, + ), + ChannelNameText( + size: 16, + channel: streamChannel.channel, + ), ], ), ), //ChannelHeader diff --git a/imessage/lib/message_widget.dart b/imessage/lib/message_widget.dart index f2c594c..12786f6 100644 --- a/imessage/lib/message_widget.dart +++ b/imessage/lib/message_widget.dart @@ -1,7 +1,8 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/cupertino.dart'; import 'package:imessage/cutom_painter.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart' show Message, AttachmentUploadStateBuilder; +import 'package:stream_chat_flutter/stream_chat_flutter.dart' + show Message, AttachmentUploadStateBuilder; class MessageWidget extends StatelessWidget { final Alignment alignment; @@ -19,7 +20,7 @@ class MessageWidget extends StatelessWidget { @override Widget build(BuildContext context) { - if (message.attachments.isNotEmpty && + if (message.attachments?.isNotEmpty == true && message.attachments.first.type == "image") { return MessageImage( color: color, message: message, messageColor: messageColor); @@ -58,21 +59,17 @@ class MessageImage extends StatelessWidget { color: color, child: Column( children: [ - - FutureBuilder( //hack: //AttachmentUploadStateBuilder once https://github.com/GetStream/stream-chat-flutter/pull/276 is merged - future: Future.delayed( - const Duration(seconds: 5), - () => - message.attachments.first.thumbUrl ?? - message.attachments.first.imageUrl ?? - message.attachments.first.assetUrl), - builder: (context, snapshot) { - return snapshot.hasData - ? CachedNetworkImage( - imageUrl: snapshot.data, - ) - : CupertinoActivityIndicator(); - }), + if (message.attachments.first.file != null) + Image.memory( + message.attachments.first.file.bytes, + fit: BoxFit.cover, + ) + else + CachedNetworkImage( + imageUrl: message.attachments.first.thumbUrl ?? + message.attachments.first.imageUrl ?? + message.attachments.first.assetUrl, + ), if (message.attachments.first?.title != null) Padding( padding: const EdgeInsets.all(8.0), diff --git a/imessage/lib/utils.dart b/imessage/lib/utils.dart index e290597..f78e450 100644 --- a/imessage/lib/utils.dart +++ b/imessage/lib/utils.dart @@ -8,13 +8,17 @@ String formatDate(DateTime date) { } String formatDateSameWeek(DateTime date) { - final dateFormat = DateFormat('EEEE, hh:mm a'); + DateFormat dateFormat; + if (date.day == DateTime.now().day) { + dateFormat = DateFormat('hh:mm a'); + } else { + dateFormat = DateFormat('EEEE, hh:mm a'); + } return dateFormat.format(date); } String formatDateMessage(DateTime date) { - final dateFormat = - DateFormat('EEE. MMM. d ' 'yy' ' hh:mm a'); + final dateFormat = DateFormat('EEE. MMM. d ' 'yy' ' hh:mm a'); return dateFormat.format(date); } @@ -35,7 +39,8 @@ class CupertinoCircleAvatar extends StatelessWidget { height: size, width: size, fit: BoxFit.cover, - errorWidget: (context, url, error) { //TODO: this crash the app when getting 404 and in debug mode, see :https://github.com/Baseflow/flutter_cached_network_image/issues/504 + errorWidget: (context, url, error) { + //TODO: this crash the app when getting 404 and in debug mode, see :https://github.com/Baseflow/flutter_cached_network_image/issues/504 return CachedNetworkImage( imageUrl: "https://4.bp.blogspot.com/-Jx21kNqFSTU/UXemtqPhZCI/AAAAAAAAh74/BMGSzpU6F48/s1600/funny-cat-pictures-047-001.jpg"); @@ -43,3 +48,23 @@ class CupertinoCircleAvatar extends StatelessWidget { ); } } + + +class Divider extends StatelessWidget { + const Divider({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Expanded( + child: Align( + child: Container( + height: 1, + color: CupertinoColors.systemGrey5, + ), + alignment: Alignment.bottomCenter, + ), + ); + } +} diff --git a/imessage/pubspec.yaml b/imessage/pubspec.yaml index fd78b13..c95b2b3 100644 --- a/imessage/pubspec.yaml +++ b/imessage/pubspec.yaml @@ -24,7 +24,12 @@ dependencies: flutter: sdk: flutter intl: ^0.16.1 - stream_chat_flutter: ^1.2.0-beta + #stream_chat_flutter: ^1.2.0-beta + stream_chat_flutter: + git: + url: git://github.com/GetStream/stream-chat-flutter.git + path: packages/stream_chat_flutter + ref: develop animations: ^1.0.0+5 collection: ^1.14.13 cached_network_image: ^2.0.0-rc