├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── json2java.xml ├── libraries │ ├── Flutter_Plugins.xml │ └── Flutter_for_Android.xml ├── runConfigurations │ └── example_lib_main_dart.xml └── vcs.xml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── .idea │ ├── .name │ ├── caches │ │ └── build_file_checksums.ser │ ├── codeStyles │ │ └── Project.xml │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ └── runConfigurations.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── brzhang │ └── flutter │ └── dim │ └── DimPlugin.java ├── dim.iml ├── dim_android.iml ├── example ├── .gitignore ├── .idea │ ├── libraries │ │ ├── Dart_SDK.xml │ │ └── Flutter_for_Android.xml │ ├── modules.xml │ ├── runConfigurations │ │ └── main_dart.xml │ └── workspace.xml ├── .metadata ├── android │ ├── .gitignore │ ├── .idea │ │ └── codeStyles │ │ │ └── Project.xml │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── brzhang │ │ │ │ └── flutter │ │ │ │ └── dimexample │ │ │ │ └── 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 │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── dim_example.iml ├── dim_example_android.iml ├── flutter_01.log ├── flutter_02.log ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── flutter_export_environment.sh │ ├── Frameworks │ │ └── libstdc++.6.0.9.dylib │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── 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.yaml └── test │ └── widget_test.dart ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── DimModel.h │ ├── DimModel.m │ ├── DimPlugin.h │ ├── DimPlugin.m │ └── MJExtension │ │ ├── Info.plist │ │ ├── MJExtension.h │ │ ├── MJExtensionConst.h │ │ ├── MJExtensionConst.m │ │ ├── MJFoundation.h │ │ ├── MJFoundation.m │ │ ├── MJProperty.h │ │ ├── MJProperty.m │ │ ├── MJPropertyKey.h │ │ ├── MJPropertyKey.m │ │ ├── MJPropertyType.h │ │ ├── MJPropertyType.m │ │ ├── NSObject+MJClass.h │ │ ├── NSObject+MJClass.m │ │ ├── NSObject+MJCoding.h │ │ ├── NSObject+MJCoding.m │ │ ├── NSObject+MJKeyValue.h │ │ ├── NSObject+MJKeyValue.m │ │ ├── NSObject+MJProperty.h │ │ ├── NSObject+MJProperty.m │ │ ├── NSString+MJExtension.h │ │ └── NSString+MJExtension.m └── dim.podspec ├── lib ├── dim.dart └── manger.dart └── pubspec.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | pubspec.lock 7 | .idea/ 8 | build/ 9 | ios/*.framework 10 | .idea 11 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/json2java.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/Flutter_for_Android.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations/example_lib_main_dart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.2.7 2 | 消息增加时间戳 3 | 4 | ## 0.2.6 5 | 可以发送语音消息 6 | 7 | ## 0.2.5 8 | 增加新的会话监听 9 | 10 | ## 0.2.4 11 | 简化描述 12 | 13 | ## 0.2.3 14 | 迁移仓库 15 | 16 | ## 0.2.2 17 | 修改一些说明文件。 18 | 19 | ## 0.2.1 20 | 修改不支持的引用头文件的方式。 21 | 22 | ## 0.2.0 23 | 升级imsdk到官方最新版本。 24 | 25 | ## 0.1.19 26 | 获取用户个人资料 27 | 28 | ## 0.1.16 29 | 简化dim的使用,android端不在需要在AndroidManifests.xml文件中配置service等内容,全部交给插件处理。 30 | 31 | ## 0.1.13 32 | 33 | * 归一化错误码 34 | 35 | ## 0.1.11 36 | 37 | * 去掉无用api,解决消息重复的问题,定位到是重复登录注册了多次listener,做了容错处理。 38 | 39 | ## 0.1.8 40 | 41 | * 解决了消息解析不出的问题。 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | TODO: Add your license here. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dim 2 | 3 | 封装的一个腾讯云im,以便于flutter开发者可以方便继承im到自己的应用中 4 | 5 | ## 使用之前注意事项 6 | 7 | 如果你之前没有使用过腾讯云,请仔细阅读这段文字,如果你已经对腾讯云im了如指掌,可以越过,但建议还是熟悉以下。 8 | 9 | 因为这个库是基于腾讯云im的,因此需要去云im申请一个应用,阅读这篇[文章](https://github.com/tencentyun/TIMSDK/tree/master/Android)可以获得以下知识: 10 | 11 | 1、`appid`怎么来的 12 | 13 | 2、`账号`及其对应的`sig`如何来的,已经推荐的sig的生成方式(当然这个是后台同学关注的)。 14 | 15 | 弄清楚这些之后,就可以开始使用`dim`了。 16 | 17 | ## 使用 dim 18 | dim的使用非常简单,只需引入这个库就可以使用了。 19 | 20 | ```dart 21 | dependencies: 22 | dim: ^0.2.6 23 | ``` 24 | 25 | 不需要像我之前实现的版本那样进行一些繁琐的配置,因为云im升级之后,支持`maven`以及`pod`的引用方式啦。那么Android端 26 | 27 | 28 | ### Android端需要注意什么? 29 | 30 | 1、混淆配置,在你的flutter的Android工程中配置混淆。 31 | 32 | ```java 33 | -keep class com.tencent.** { *; } 34 | ``` 35 | 36 | ### IOS端需要注意什么? 37 | 38 | 1、请注意在你的flutter工程的ios项目根目录执行`pod update`[**非必须,如果报错建议执行一次**] 39 | 40 | 2、随后在执行一次`pod install` 41 | 42 | 43 | ### demo截图 44 | 45 | ![截图](https://raw.githubusercontent.com/bravekingzhang/pic_go/master/20190603113634.png) 46 | 47 | 48 | ![构建](https://raw.githubusercontent.com/bravekingzhang/pic_go/master/20190603113619.png) 49 | 50 | 51 | ## 已有的功能 52 | 53 | 1. 初始化 54 | 55 | 建议整个应用生命周期只执行一次。 56 | 2. 登录 57 | 3. 登出 58 | 4. 获取会话列表 59 | 5. 删除一个会话 60 | 6. 获取私信会话消息[群聊消息目前没有封装] 61 | 62 | 注意,私信发送方的资料云im改成了异步的方式,因此,这个版本不在返回! 63 | 建议用户自己查询一次,最好的方式是将用户资料存储在本地db中,并 64 | 7. 发送图片消息 65 | 66 | 注意,图片消息中图片云im需要的是图片的`本地路径`。 67 | 8. 发送文本消息 68 | 9. 发送地理位置消息 69 | 10. 获取用户资料 70 | 11. 设置用户资料 71 | 72 | 目前仅仅提供了设置`nick`,`gender`,`faceUrl`,有需要在补充。 73 | 12. 监听新的消息 74 | 13. 监听有新的会话 75 | 76 | 注意,和新的消息是一个消息通道,只不过收到的内容是`[]`,对一个空的数组,此时需要去主动调用4获取会话列表来查最新会话列表 77 | 14. 发送语音消息,基本和图片消息一致,使用本地路径 78 | 79 | 80 | 81 | ### 注意 82 | 83 | demo中的 initListener 中的逻辑需要调用一下,最好是在initState中,建立消息通道,flutter这边才能收发消息。 84 | 85 | 86 | ## todo 87 | 88 | 根据需要,可以提issue,或者接受pr来实现更多的接口,主要是体力活。 -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/.idea/.name: -------------------------------------------------------------------------------- 1 | dim -------------------------------------------------------------------------------- /android/.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/android/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /android/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /android/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | -------------------------------------------------------------------------------- /android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 84 | -------------------------------------------------------------------------------- /android/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.brzhang.flutter.dim' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:3.1.2' 12 | } 13 | } 14 | 15 | rootProject.allprojects { 16 | repositories { 17 | google() 18 | jcenter() 19 | } 20 | } 21 | 22 | apply plugin: 'com.android.library' 23 | 24 | android { 25 | compileSdkVersion 27 26 | 27 | defaultConfig { 28 | minSdkVersion 16 29 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 30 | } 31 | lintOptions { 32 | disable 'InvalidPackage' 33 | } 34 | dependencies { 35 | implementation fileTree(include: ['*.jar'], dir: 'libs') 36 | implementation 'com.google.code.gson:gson:2.8.5' 37 | implementation 'com.tencent.imsdk:imsdk:4+' 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | systemProp.http.proxyHost=127.0.0.1 3 | systemProp.http.proxyPort=12759 4 | systemProp.https.proxyHost=127.0.0.1 5 | systemProp.https.proxyPort=12759 -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Aug 02 11:29:13 CST 2018 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.4-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'dim' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /dim.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /dim_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | 9 | .flutter-plugins 10 | -------------------------------------------------------------------------------- /example/.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /example/.idea/libraries/Flutter_for_Android.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/.idea/runConfigurations/main_dart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /example/.idea/workspace.xml: -------------------------------------------------------------------------------- 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 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /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: 1cb2677234175e0d7e06742eef6e865a755f07ff 8 | channel: master 9 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.class 3 | .gradle 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | GeneratedPluginRegistrant.java 11 | -------------------------------------------------------------------------------- /example/android/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /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 | throw new GradleException("versionCode not found. Define flutter.versionCode in the local.properties file.") 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | throw new GradleException("versionName not found. Define flutter.versionName in the local.properties file.") 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 27 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.brzhang.flutter.dimexample" 37 | minSdkVersion 16 38 | targetSdkVersion 27 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 60 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 61 | } 62 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 9 | 14 | 17 | 24 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/brzhang/flutter/dimexample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.brzhang.flutter.dimexample; 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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.1.4' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /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.4-all.zip 7 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /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/dim_example.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/dim_example_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /example/flutter_01.log: -------------------------------------------------------------------------------- 1 | Flutter crash report; please file at https://github.com/flutter/flutter/issues. 2 | 3 | ## command 4 | 5 | flutter doctor 6 | 7 | ## exception 8 | 9 | VersionCheckError: VersionCheckError: Command exited with code 1: git log -n 1 --pretty=format:%ad --date=iso 10 | Standard error: xcrun: error: active developer path ("/Applications/Xcode.app/Contents/Developer") does not exist 11 | Use `sudo xcode-select --switch path/to/Xcode.app` to specify the Xcode that you wish to use for command line developer tools, or use `xcode-select --install` to install the standalone command line developer tools. 12 | See `man xcode-select` for more details. 13 | 14 | 15 | ``` 16 | #0 _runSync (package:flutter_tools/src/version.dart:475:5) 17 | #1 FlutterVersion._latestGitCommitDate (package:flutter_tools/src/version.dart:129:12) 18 | #2 FlutterVersion.frameworkCommitDate (package:flutter_tools/src/version.dart:120:37) 19 | #3 FlutterVersion.frameworkDate (package:flutter_tools/src/version.dart:83:31) 20 | #4 _FlutterValidator.validate (package:flutter_tools/src/doctor.dart:286:45) 21 | 22 | #5 Doctor.startValidatorTasks (package:flutter_tools/src/doctor.dart:88:56) 23 | #6 Doctor.diagnose (package:flutter_tools/src/doctor.dart:145:41) 24 | 25 | #7 DoctorCommand.runCommand (package:flutter_tools/src/commands/doctor.dart:29:39) 26 | 27 | #8 FlutterCommand.verifyThenRunCommand (package:flutter_tools/src/runner/flutter_command.dart:347:18) 28 | #9 _asyncThenWrapperHelper. (dart:async/runtime/libasync_patch.dart:77:64) 29 | #10 _rootRunUnary (dart:async/zone.dart:1132:38) 30 | #11 _CustomZone.runUnary (dart:async/zone.dart:1029:19) 31 | #12 _FutureListener.handleValue (dart:async/future_impl.dart:129:18) 32 | #13 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:642:45) 33 | #14 Future._propagateToListeners (dart:async/future_impl.dart:671:32) 34 | #15 Future._complete (dart:async/future_impl.dart:476:7) 35 | #16 _SyncCompleter.complete (dart:async/future_impl.dart:51:12) 36 | #17 _AsyncAwaitCompleter.complete. (dart:async/runtime/libasync_patch.dart:33:20) 37 | #18 _rootRun (dart:async/zone.dart:1124:13) 38 | #19 _CustomZone.run (dart:async/zone.dart:1021:19) 39 | #20 _CustomZone.bindCallback. (dart:async/zone.dart:947:23) 40 | #21 _microtaskLoop (dart:async/schedule_microtask.dart:41:21) 41 | #22 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5) 42 | #23 _runPendingImmediateCallback (dart:isolate/runtime/libisolate_patch.dart:113:13) 43 | #24 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:166:5) 44 | ``` 45 | 46 | ## flutter doctor 47 | 48 | ``` 49 | encountered exception: VersionCheckError: Command exited with code 1: git log -n 1 --pretty=format:%ad --date=iso 50 | Standard error: xcrun: error: active developer path ("/Applications/Xcode.app/Contents/Developer") does not exist 51 | Use `sudo xcode-select --switch path/to/Xcode.app` to specify the Xcode that you wish to use for command line developer tools, or use `xcode-select --install` to install the standalone command line developer tools. 52 | See `man xcode-select` for more details. 53 | 54 | 55 | #0 _runSync (package:flutter_tools/src/version.dart:475:5) 56 | #1 FlutterVersion._latestGitCommitDate (package:flutter_tools/src/version.dart:129:12) 57 | #2 FlutterVersion.frameworkCommitDate (package:flutter_tools/src/version.dart:120:37) 58 | #3 FlutterVersion.frameworkDate (package:flutter_tools/src/version.dart:83:31) 59 | #4 _FlutterValidator.validate (package:flutter_tools/src/doctor.dart:286:45) 60 | 61 | #5 Doctor.startValidatorTasks (package:flutter_tools/src/doctor.dart:88:56) 62 | #6 Doctor.diagnose (package:flutter_tools/src/doctor.dart:145:41) 63 | 64 | #7 _doctorText. (package:flutter_tools/runner.dart:194:26) 65 | #8 AppContext.run. (package:flutter_tools/src/base/context.dart:142:29) 66 | 67 | #9 _rootRun (dart:async/zone.dart:1124:13) 68 | #10 _CustomZone.run (dart:async/zone.dart:1021:19) 69 | #11 _runZoned (dart:async/zone.dart:1516:10) 70 | #12 runZoned (dart:async/zone.dart:1463:12) 71 | #13 AppContext.run (package:flutter_tools/src/base/context.dart:141:18) 72 | 73 | #14 _doctorText (package:flutter_tools/runner.dart:193:19) 74 | 75 | #15 _createLocalCrashReport (package:flutter_tools/runner.dart:171:32) 76 | 77 | #16 _handleToolError (package:flutter_tools/runner.dart:126:33) 78 | 79 | #17 run. (package:flutter_tools/runner.dart:63:20) 80 | 81 | #18 AppContext.run. (package:flutter_tools/src/base/context.dart:142:29) 82 | 83 | #19 _rootRun (dart:async/zone.dart:1124:13) 84 | #20 _CustomZone.run (dart:async/zone.dart:1021:19) 85 | #21 _runZoned (dart:async/zone.dart:1516:10) 86 | #22 runZoned (dart:async/zone.dart:1463:12) 87 | #23 AppContext.run (package:flutter_tools/src/base/context.dart:141:18) 88 | 89 | #24 runInContext (package:flutter_tools/src/context_runner.dart:42:24) 90 | 91 | #25 run (package:flutter_tools/runner.dart:50:10) 92 | #26 main (package:flutter_tools/executable.dart:51:9) 93 | 94 | #27 main (file:///Users/joshuaton/Documents/Code/Flutter/flutter/packages/flutter_tools/bin/flutter_tools.dart:8:3) 95 | #28 _startIsolate. (dart:isolate/runtime/libisolate_patch.dart:277:32) 96 | #29 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:165:12) 97 | ``` 98 | -------------------------------------------------------------------------------- /example/flutter_02.log: -------------------------------------------------------------------------------- 1 | Flutter crash report; please file at https://github.com/flutter/flutter/issues. 2 | 3 | ## command 4 | 5 | flutter build bundle --suppress-analytics --target lib/main.dart --preview-dart-2 --depfile /Users/hoollyzhang/henCode/dim/example/build/app/intermediates/flutter/debug/snapshot_blob.bin.d --asset-dir /Users/hoollyzhang/henCode/dim/example/build/app/intermediates/flutter/debug/flutter_assets --debug 6 | 7 | ## exception 8 | 9 | ArgumentError: Invalid argument(s): Cannot find executable for /Users/hoollyzhang/flutter/flutter/bin/cache/dart-sdk/bin/pub. 10 | 11 | ``` 12 | #0 _getExecutable (package:process/src/interface/local_process_manager.dart:113:5) 13 | #1 LocalProcessManager.start (package:process/src/interface/local_process_manager.dart:41:7) 14 | #2 runCommand (package:flutter_tools/src/base/process.dart:115:25) 15 | #3 runCommandAndStreamOutput (package:flutter_tools/src/base/process.dart:133:33) 16 | 17 | #4 pub (package:flutter_tools/src/dart/pub.dart:155:18) 18 | 19 | #5 pubGet (package:flutter_tools/src/dart/pub.dart:104:13) 20 | 21 | #6 FlutterCommand.verifyThenRunCommand (package:flutter_tools/src/runner/flutter_command.dart:334:13) 22 | 23 | #7 FlutterCommand.run. (package:flutter_tools/src/runner/flutter_command.dart:282:33) 24 | 25 | #8 AppContext.run. (package:flutter_tools/src/base/context.dart:142:29) 26 | 27 | #9 _rootRun (dart:async/zone.dart:1124:13) 28 | #10 _CustomZone.run (dart:async/zone.dart:1021:19) 29 | #11 _runZoned (dart:async/zone.dart:1516:10) 30 | #12 runZoned (dart:async/zone.dart:1463:12) 31 | #13 AppContext.run (package:flutter_tools/src/base/context.dart:141:18) 32 | 33 | #14 FlutterCommand.run (package:flutter_tools/src/runner/flutter_command.dart:273:20) 34 | #15 CommandRunner.runCommand (package:args/command_runner.dart:194:27) 35 | 36 | #16 FlutterCommandRunner.runCommand. (package:flutter_tools/src/runner/flutter_command_runner.dart:346:21) 37 | 38 | #17 AppContext.run. (package:flutter_tools/src/base/context.dart:142:29) 39 | 40 | #18 _rootRun (dart:async/zone.dart:1124:13) 41 | #19 _CustomZone.run (dart:async/zone.dart:1021:19) 42 | #20 _runZoned (dart:async/zone.dart:1516:10) 43 | #21 runZoned (dart:async/zone.dart:1463:12) 44 | #22 AppContext.run (package:flutter_tools/src/base/context.dart:141:18) 45 | 46 | #23 FlutterCommandRunner.runCommand (package:flutter_tools/src/runner/flutter_command_runner.dart:302:19) 47 | 48 | #24 CommandRunner.run. (package:args/command_runner.dart:109:29) 49 | #25 new Future.sync (dart:async/future.dart:224:31) 50 | #26 CommandRunner.run (package:args/command_runner.dart:109:11) 51 | #27 FlutterCommandRunner.run (package:flutter_tools/src/runner/flutter_command_runner.dart:211:18) 52 | #28 run. (package:flutter_tools/runner.dart:59:20) 53 | 54 | #29 AppContext.run. (package:flutter_tools/src/base/context.dart:142:29) 55 | 56 | #30 _rootRun (dart:async/zone.dart:1124:13) 57 | #31 _CustomZone.run (dart:async/zone.dart:1021:19) 58 | #32 _runZoned (dart:async/zone.dart:1516:10) 59 | #33 runZoned (dart:async/zone.dart:1463:12) 60 | #34 AppContext.run (package:flutter_tools/src/base/context.dart:141:18) 61 | 62 | #35 runInContext (package:flutter_tools/src/context_runner.dart:42:24) 63 | 64 | #36 run (package:flutter_tools/runner.dart:50:10) 65 | #37 main (package:flutter_tools/executable.dart:51:9) 66 | 67 | #38 main (file:///Users/hoollyzhang/flutter/flutter/packages/flutter_tools/bin/flutter_tools.dart:8:3) 68 | #39 _startIsolate. (dart:isolate/runtime/libisolate_patch.dart:284:32) 69 | #40 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:169:12) 70 | ``` 71 | 72 | ## flutter doctor 73 | 74 | ``` 75 | [✓] Flutter (Channel master, v0.5.9-pre.10, on Mac OS X 10.13.5 17F77, locale zh-Hans-CN) 76 | • Flutter version 0.5.9-pre.10 at /Users/hoollyzhang/flutter/flutter 77 | • Framework revision 21f22ed3ba (2 weeks ago), 2018-08-26 19:50:02 -0400 78 | • Engine revision af42b6dc95 79 | • Dart version 2.0.0-dev.69.5.flutter-eab492385c 80 | 81 | [!] Android toolchain - develop for Android devices (Android SDK 28.0.0) 82 | • Android SDK at /Users/hoollyzhang/Library/Android/sdk 83 | • Android NDK at /Users/hoollyzhang/Library/Android/sdk/ndk-bundle 84 | • Platform android-28, build-tools 28.0.0 85 | • ANDROID_HOME = /Users/hoollyzhang/Library/Android/sdk 86 | • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java 87 | • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b01) 88 | ✗ Android license status unknown. 89 | 90 | [✓] iOS toolchain - develop for iOS devices (Xcode 9.4.1) 91 | • Xcode at /Applications/Xcode.app/Contents/Developer 92 | • Xcode 9.4.1, Build version 9F2000 93 | • ios-deploy 1.9.2 94 | • CocoaPods version 1.5.3 95 | 96 | [✓] Android Studio (version 3.1) 97 | • Android Studio at /Applications/Android Studio.app/Contents 98 | • Flutter plugin version 26.0.1 99 | • Dart plugin version 173.4700 100 | • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b01) 101 | 102 | [!] IntelliJ IDEA Ultimate Edition (version 2017.3.4) 103 | • IntelliJ at /Applications/IntelliJ IDEA.app 104 | ✗ Flutter plugin not installed; this adds Flutter specific functionality. 105 | ✗ Dart plugin not installed; this adds Dart specific functionality. 106 | • For information about installing plugins, see 107 | https://flutter.io/intellij-setup/#installing-the-plugins 108 | 109 | [!] VS Code (version 1.23.1) 110 | • VS Code at /Applications/Visual Studio Code.app/Contents 111 | • Flutter extension not installed; install from 112 | https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter 113 | 114 | [✓] Connected devices (2 available) 115 | • HUAWEI NXT AL10 • 5LM7N16401002621 • android-arm64 • Android 7.0 (API 24) 116 | • 张勇的 iPhone • c50a3c88e239175b7a126ab35c4ada3a86395abb • ios • iOS 11.4.1 117 | 118 | ! Doctor found issues in 3 categories. 119 | ``` 120 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/app.flx 37 | /Flutter/app.zip 38 | /Flutter/flutter_assets/ 39 | /Flutter/App.framework 40 | /Flutter/Flutter.framework 41 | /Flutter/Generated.xcconfig 42 | /ServiceDefinitions.json 43 | 44 | Pods/ 45 | .symlinks/ 46 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=/Users/hoollyzhang/flutter/flutter" 4 | export "FLUTTER_APPLICATION_PATH=/Users/hoollyzhang/henCode/dim/example" 5 | export "FLUTTER_TARGET=/Users/hoollyzhang/henCode/dim/example/lib/main.dart" 6 | export "FLUTTER_BUILD_DIR=build" 7 | export "SYMROOT=${SOURCE_ROOT}/../build/ios" 8 | export "FLUTTER_FRAMEWORK_DIR=/Users/hoollyzhang/flutter/flutter/bin/cache/artifacts/engine/ios" 9 | export "FLUTTER_BUILD_NAME=1.0.0" 10 | export "FLUTTER_BUILD_NUMBER=1" 11 | export "TRACK_WIDGET_CREATION=true" 12 | -------------------------------------------------------------------------------- /example/ios/Frameworks/libstdc++.6.0.9.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/example/ios/Frameworks/libstdc++.6.0.9.dylib -------------------------------------------------------------------------------- /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 parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | pods_ary = [] 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) { |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | pods_ary.push({:name => podname, :path => podpath}); 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | } 32 | return pods_ary 33 | end 34 | 35 | target 'Runner' do 36 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 37 | # referring to absolute paths on developers' machines. 38 | system('rm -rf .symlinks') 39 | system('mkdir -p .symlinks/plugins') 40 | 41 | # Flutter Pods 42 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 43 | if generated_xcode_build_settings.empty? 44 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." 45 | end 46 | generated_xcode_build_settings.map { |p| 47 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 48 | symlink = File.join('.symlinks', 'flutter') 49 | File.symlink(File.dirname(p[:path]), symlink) 50 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 51 | end 52 | } 53 | 54 | # Plugin Pods 55 | plugin_pods = parse_KV_file('../.flutter-plugins') 56 | plugin_pods.map { |p| 57 | symlink = File.join('.symlinks', 'plugins', p[:name]) 58 | File.symlink(p[:path], symlink) 59 | pod p[:name], :path => File.join(symlink, 'ios') 60 | } 61 | end 62 | 63 | post_install do |installer| 64 | installer.pods_project.targets.each do |target| 65 | target.build_configurations.each do |config| 66 | config.build_settings['ENABLE_BITCODE'] = 'NO' 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - dim (0.2.6): 3 | - Flutter 4 | - TXIMSDK_iOS 5 | - YYModel 6 | - Flutter (1.0.0) 7 | - TXIMSDK_iOS (4.3.135) 8 | - YYModel (1.0.4) 9 | 10 | DEPENDENCIES: 11 | - dim (from `.symlinks/plugins/dim/ios`) 12 | - Flutter (from `.symlinks/flutter/ios`) 13 | 14 | SPEC REPOS: 15 | https://github.com/cocoapods/specs.git: 16 | - TXIMSDK_iOS 17 | - YYModel 18 | 19 | EXTERNAL SOURCES: 20 | dim: 21 | :path: ".symlinks/plugins/dim/ios" 22 | Flutter: 23 | :path: ".symlinks/flutter/ios" 24 | 25 | SPEC CHECKSUMS: 26 | dim: 51ecffa284a3f408c576059f4a15eb23214841b0 27 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 28 | TXIMSDK_iOS: 46fd4c57411f140559d0222423cef27776722684 29 | YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30 30 | 31 | PODFILE CHECKSUM: fbaee5b23138ad4cd0cb935c94fcf473f272085c 32 | 33 | COCOAPODS: 1.7.5 34 | -------------------------------------------------------------------------------- /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.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 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 | 10 | // Override point for customization after application launch. 11 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 12 | } 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/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 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | dim_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 'package:flutter/material.dart'; 2 | import 'dart:async'; 3 | 4 | import 'package:flutter/services.dart'; 5 | import 'package:dim/dim.dart'; 6 | 7 | import 'dart:math'; 8 | 9 | void main() => runApp(new MyApp()); 10 | 11 | class MyApp extends StatefulWidget { 12 | @override 13 | _MyAppState createState() => new _MyAppState(); 14 | } 15 | 16 | class _MyAppState extends State { 17 | Dim _dim = new Dim(); 18 | 19 | String _result = ""; 20 | 21 | List _users = List(); 22 | 23 | //在另外一个手机上测试改变下用户,靠这里了 24 | int _currentUser = 0; 25 | 26 | int appid = 1400215656; 27 | 28 | StreamSubscription _messageStreamSubscription; 29 | 30 | @override 31 | void initState() { 32 | super.initState(); 33 | ////大家 一个消息监听通道要记得在initState中调用一下哦。 34 | initListener(); 35 | _users.add({ 36 | 'username': 'hoolly1', 37 | 'sig': 38 | "eJxlj8FPgzAche-8FYTrjLbQgngbbkEmHIgzZF4Iwg*obLTSssmM-7sRNTbxXb8v7*W9G6ZpWtv44bIoSz72KleTAMu8MS1kXfxBIViVFyp3huofhDfBBsiLWsEwQ0wptRHSHVZBr1jNfoyW8-1*wpogqy6fV74bCEI2pi51dYU1M0zWu9soXWUp37Q0CKYoCGJVPi3ASRkbty*CyCQeC7dZc1igFK5OUbvchMdyyvz7tqulF3qrJT3Is-vYvI4nz92hwc-EHQnpM0k6bVKxA-xe8sk19oij0SMMkvF*FmyEKbYd9BXL*DA*AfzTXb0_" 39 | }); 40 | _users.add({ 41 | 'username': 'hoolly2', 42 | 'sig': 43 | "eJxlj8FOg0AURfd8BWFbIzMDM60mLhqUhNpqacUFGwKdgb4yDATGltb470bUSOLbnpN773s3TNO0Xpbb63S3q9*UTvS5EZZ5a1rIuvqDTQM8SXXitPwfFH0DrUjSXIt2gJhSShAaO8CF0pDDj7GvaynPZCR0vEyGlu8EFyGCKaNsrEAxwNVD5AXhPeHk4D1uTq7wudL9pVB9PJmjCmTZ*pn0Q8*3N9sc8qciKBbsGOBYnEo2E5nM9yxmr5FcVwd7MWEdrHHozLPnVbS0w7tRpYZK-L50487wdDrefBRtB7UaBIIwxcRBX2cZH8YnH3NdyQ__" 44 | }); 45 | _users.add({ 46 | 'username': 'hoolly3', 47 | 'sig': 48 | "eJxlj11PgzAARd-5FU2fjbZA2ccbwW0QYbqMEeSlAdpB2YQGyiYx-ncjakbifT0n9*Z*aAAAGPr7*zTPm75WVA2SQ7AEEMG7G5RSMJoqarTsH*TvUrScpkfF2xFiQoiO0NQRjNdKHMWvUTbN*TwYE6FjJzqu-DSYCOmYWMSaKqIYYbB6dbyd40XeY6i8uHpyS5*blzBKtv1zvtrGa9Zd1yeLWb0hrzxyba*0g83eVpsgr5wC6Vl8KHYvWZUmTvdQJ2o4ZHXH0eD77tywJ5NKvPG-SwtzjmeL2YReeNuJph4FHWGCdQN9B2qf2hc3HF5a" 49 | }); 50 | } 51 | 52 | // Platform messages are asynchronous, so we initialize in an async method. 53 | Future initListener() async { 54 | // message was in flight, we want to discard the reply rather than calling 55 | // setState to update our non-existent appearance. 56 | if (!mounted) return; 57 | 58 | if (_messageStreamSubscription == null) { 59 | _messageStreamSubscription = _dim.onMessage.listen((dynamic onData) { 60 | print( 61 | "我监听到数据了$onData,需要在这里判断是你是消息列表还是需要刷新会话的请求。会话的请求是一个空的列表[],消息列表是有内容的"); 62 | }); 63 | } 64 | } 65 | 66 | @override 67 | void dispose() { 68 | // TODO: implement dispose 69 | super.dispose(); 70 | //flutter 这里应该页面退出栈会调用,但是如果这个是根页面,日志是打不出来的。 71 | canCelListener(); 72 | } 73 | 74 | @override 75 | Widget build(BuildContext context) { 76 | return new MaterialApp( 77 | home: new Scaffold( 78 | appBar: new AppBar( 79 | title: Text('当前账号' + _users[_currentUser]["username"]), 80 | ), 81 | body: new Center( 82 | child: CustomScrollView( 83 | primary: false, 84 | slivers: [ 85 | SliverPersistentHeader( 86 | delegate: _SliverAppBarDelegate( 87 | minHeight: 30, 88 | maxHeight: 200, 89 | child: Container( 90 | margin: EdgeInsets.all(10), 91 | padding: EdgeInsets.all(4), 92 | decoration: BoxDecoration( 93 | border: Border.all(), 94 | borderRadius: BorderRadius.all(Radius.circular(5))), 95 | child: SingleChildScrollView( 96 | child: Text(_result.isEmpty ? "这里显示输出结果" : _result), 97 | ), 98 | )), 99 | pinned: true, 100 | ), 101 | SliverPadding( 102 | padding: const EdgeInsets.all(10.0), 103 | sliver: SliverGrid.count( 104 | crossAxisSpacing: 10.0, 105 | mainAxisSpacing: 10.0, 106 | crossAxisCount: 4, 107 | children: [ 108 | RaisedButton( 109 | onPressed: () { 110 | init(); 111 | }, 112 | child: Text('初始化'), 113 | ), 114 | RaisedButton( 115 | onPressed: () { 116 | login(); 117 | }, 118 | child: Text('登录'), 119 | ), 120 | RaisedButton( 121 | onPressed: () { 122 | logout(); 123 | }, 124 | child: Text('登出'), 125 | ), 126 | RaisedButton( 127 | onPressed: () { 128 | postData(); 129 | }, 130 | child: Text('测试发送数据'), 131 | ), 132 | // RaisedButton( 133 | // onPressed: () { 134 | // canCelListener(); 135 | // }, 136 | // child: Text('取消监听'), 137 | // ), 138 | RaisedButton( 139 | onPressed: () { 140 | sendTextMsg(); 141 | }, 142 | child: Text('发文本'), 143 | ), 144 | RaisedButton( 145 | onPressed: () { 146 | sendImageMsg(); 147 | }, 148 | child: Text('发图片'), 149 | ), 150 | RaisedButton( 151 | onPressed: () { 152 | sendLocationMsg(); 153 | }, 154 | child: Text('发位置'), 155 | ), 156 | RaisedButton( 157 | onPressed: () { 158 | getMessages(); 159 | }, 160 | padding: EdgeInsets.all(0), 161 | child: Text('历史消息'), 162 | ), 163 | RaisedButton( 164 | onPressed: () { 165 | getUserInfo(); 166 | }, 167 | child: Text('拿资料'), 168 | ), 169 | RaisedButton( 170 | padding: EdgeInsets.all(0), 171 | onPressed: () { 172 | setUserInfo(); 173 | }, 174 | child: Text('设置资料'), 175 | ), 176 | RaisedButton( 177 | padding: EdgeInsets.all(0), 178 | onPressed: () { 179 | getConversations(); 180 | }, 181 | child: Text('会话列表'), 182 | ), 183 | ], 184 | ), 185 | ), 186 | ], 187 | ), 188 | ), 189 | ), 190 | ); 191 | } 192 | 193 | Future postData() async { 194 | try { 195 | var result = await _dim.postDataTest(); 196 | setState(() { 197 | this._result = result; 198 | }); 199 | print(result); 200 | } on PlatformException { 201 | print("listen 失败"); 202 | } 203 | } 204 | 205 | Future sendTextMsg() async { 206 | try { 207 | var result = await _dim.sendTextMessages( 208 | _users[_users.length - _currentUser - 1]['username'], "haahah"); 209 | print(result); 210 | setState(() { 211 | this._result = result; 212 | }); 213 | } on PlatformException { 214 | print("发送消息失败"); 215 | setState(() { 216 | this._result = "发送消息失败"; 217 | }); 218 | } 219 | } 220 | 221 | Future sendImageMsg() async { 222 | try { 223 | var result = await _dim.sendImageMessages( 224 | _users[_users.length - _currentUser - 1]['username'], 225 | "tyyhuiijkoi.png"); 226 | print(result); 227 | setState(() { 228 | this._result = result; 229 | }); 230 | } on PlatformException { 231 | print("发送图片消息失败"); 232 | setState(() { 233 | this._result = "发送图片消息失败"; 234 | }); 235 | } 236 | } 237 | 238 | Future sendLocationMsg() async { 239 | try { 240 | var result = await _dim.sendLocationMessages( 241 | _users[_users.length - _currentUser - 1]['username'], 242 | 113.93, 243 | 22.54, 244 | "腾讯大厦"); 245 | print(result); 246 | setState(() { 247 | this._result = result; 248 | }); 249 | } on PlatformException { 250 | print("发送位置消息失败"); 251 | setState(() { 252 | this._result = "发送位置消息失败"; 253 | }); 254 | } 255 | } 256 | 257 | ///测试化测试,这里传自己应用的appid 258 | Future init() async { 259 | try { 260 | var result = await _dim.init(appid); 261 | print(result); 262 | setState(() { 263 | this._result = result; 264 | }); 265 | } on PlatformException { 266 | print("初始化失败"); 267 | } 268 | } 269 | 270 | ///第一个测试账号 271 | Future login() async { 272 | try { 273 | var result = await _dim.imLogin( 274 | _users[_currentUser]['username'], _users[_currentUser]['sig']); 275 | print(result); 276 | setState(() { 277 | this._result = result; 278 | }); 279 | } on PlatformException { 280 | print("登录 失败"); 281 | } 282 | } 283 | 284 | Future logout() async { 285 | try { 286 | var result = await _dim.imLogout(); 287 | print(result); 288 | setState(() { 289 | this._result = result; 290 | }); 291 | } on PlatformException { 292 | print("登出 失败"); 293 | } 294 | } 295 | 296 | Future getMessages() async { 297 | try { 298 | var result = await _dim.getMessages( 299 | _users[_users.length - _currentUser - 1]['username'], 300 | ); 301 | print(result); 302 | setState(() { 303 | this._result = result; 304 | }); 305 | } on PlatformException {} 306 | } 307 | 308 | void canCelListener() { 309 | if (_messageStreamSubscription != null) { 310 | _messageStreamSubscription.cancel(); 311 | } 312 | } 313 | 314 | void getUserInfo() async { 315 | try { 316 | List users = List(); 317 | users.add(_users[_users.length - _currentUser - 1]['username']); 318 | var result = await _dim.getUsersProfile(users); 319 | print(result); 320 | setState(() { 321 | this._result = result; 322 | }); 323 | } on PlatformException { 324 | print("获取个人资料失败"); 325 | } 326 | } 327 | 328 | void setUserInfo() async { 329 | try { 330 | var result = await _dim.setUsersProfile( 331 | 1, "hz", "https://www.brzhang.club/images/hz.png"); 332 | print(result); 333 | setState(() { 334 | this._result = result; 335 | }); 336 | } on PlatformException { 337 | print("获取个人资料失败"); 338 | } 339 | } 340 | 341 | void getConversations() async { 342 | try { 343 | var result = await _dim.getConversations(); 344 | print(result); 345 | setState(() { 346 | this._result = result; 347 | }); 348 | } on PlatformException { 349 | print("获取会话列表失败"); 350 | } 351 | } 352 | } 353 | 354 | class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { 355 | _SliverAppBarDelegate({ 356 | @required this.minHeight, 357 | @required this.maxHeight, 358 | @required this.child, 359 | }); 360 | 361 | final double minHeight; 362 | final double maxHeight; 363 | final Widget child; 364 | 365 | @override 366 | double get minExtent => minHeight; 367 | 368 | @override 369 | double get maxExtent => max(maxHeight, minHeight); 370 | 371 | @override 372 | Widget build( 373 | BuildContext context, double shrinkOffset, bool overlapsContent) { 374 | return new SizedBox.expand(child: child); 375 | } 376 | 377 | @override 378 | bool shouldRebuild(_SliverAppBarDelegate oldDelegate) { 379 | return maxHeight != oldDelegate.maxHeight || 380 | minHeight != oldDelegate.minHeight || 381 | child != oldDelegate.child; 382 | } 383 | } 384 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dim_example 2 | description: Demonstrates how to use the dim plugin. 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 | # Read more about versioning at semver.org. 10 | version: 1.0.0+1 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | 16 | # The following adds the Cupertino Icons font to your application. 17 | # Use with the CupertinoIcons class for iOS style icons. 18 | cupertino_icons: ^0.1.2 19 | 20 | dev_dependencies: 21 | flutter_test: 22 | sdk: flutter 23 | 24 | dim: 25 | path: ../ 26 | 27 | # For information on the generic Dart part of this file, see the 28 | # following page: https://www.dartlang.org/tools/pub/pubspec 29 | 30 | # The following section is specific to Flutter. 31 | flutter: 32 | 33 | # The following line ensures that the Material Icons font is 34 | # included with your application, so that you can use the icons in 35 | # the material Icons class. 36 | uses-material-design: true 37 | 38 | # To add assets to your application, add an assets section, like this: 39 | # assets: 40 | # - images/a_dot_burr.jpeg 41 | # - images/a_dot_ham.jpeg 42 | 43 | # An image asset can refer to one or more resolution-specific "variants", see 44 | # https://flutter.io/assets-and-images/#resolution-aware. 45 | 46 | # For details regarding adding assets from package dependencies, see 47 | # https://flutter.io/assets-and-images/#from-packages 48 | 49 | # To add custom fonts to your application, add a fonts section here, 50 | # in this "flutter" section. Each entry in this list should have a 51 | # "family" key with the font family name, and a "fonts" key with a 52 | # list giving the asset and other descriptors for the font. For 53 | # example: 54 | # fonts: 55 | # - family: Schyler 56 | # fonts: 57 | # - asset: fonts/Schyler-Regular.ttf 58 | # - asset: fonts/Schyler-Italic.ttf 59 | # style: italic 60 | # - family: Trajan Pro 61 | # fonts: 62 | # - asset: fonts/TrajanPro.ttf 63 | # - asset: fonts/TrajanPro_Bold.ttf 64 | # weight: 700 65 | # 66 | # For details regarding fonts from package dependencies, 67 | # see https://flutter.io/custom-fonts/#from-packages 68 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter 3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to 4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties 5 | // are correct. 6 | 7 | //import 'package:flutter/material.dart'; 8 | //import 'package:flutter_test/flutter_test.dart'; 9 | // 10 | //import 'package:dim_example/main.dart'; 11 | // 12 | //void main() { 13 | // testWidgets('Verify Platform version', (WidgetTester tester) async { 14 | // // Build our app and trigger a frame. 15 | // await tester.pumpWidget(new MyApp()); 16 | // 17 | // // Verify that platform version is retrieved. 18 | // expect( 19 | // find.byWidgetPredicate( 20 | // (Widget widget) => 21 | // widget is Text && widget.data.startsWith('Running on:'), 22 | // ), 23 | // findsOneWidget); 24 | // }); 25 | //} 26 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bravekingzhang/dim/decc1a81a8e65aa955ff4013b0ae206405e10e27/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/DimModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // DimModel.h 3 | // dim 4 | // 5 | // Created by 飞鱼 on 2019/2/15. 6 | // 7 | 8 | #import 9 | #import 10 | NS_ASSUME_NONNULL_BEGIN 11 | //@class TIMUserProfile, TIMConversation, TIMMessage, TIMGroupMemberInfo, TIMElem; 12 | 13 | 14 | @interface DimUser : NSObject 15 | @property (nonatomic, copy) NSString *identifier; 16 | @property (nonatomic, copy) NSString *nickName; 17 | @property (nonatomic, copy) NSString *remark; 18 | @property (nonatomic, copy) NSString *faceURL; 19 | @property (nonatomic, copy) NSString *selfSignature; 20 | @property (nonatomic, assign) NSInteger gender; 21 | @property (nonatomic, assign) NSInteger birthday; 22 | @property (nonatomic, copy) NSString *location; 23 | 24 | + (DimUser *)initWithTimUser:(TIMUserProfile *)timUserProfile; 25 | 26 | @end 27 | 28 | @interface DimConversation : NSObject 29 | @property(nonatomic,assign) TIMConversationType type; 30 | @property (nonatomic, copy) NSString *peer; 31 | 32 | + (DimConversation *)initWithTIMConversation:(TIMConversation *)timConversation; 33 | @end 34 | 35 | @interface DimMessage : NSObject 36 | @property (nonatomic, strong) DimUser *senderProfile; 37 | @property (nonatomic, copy) NSString *sender; 38 | @property (nonatomic, strong) DimConversation *timConversation; 39 | @property (nonatomic, strong) TIMGroupMemberInfo *timGroupMemberInfo; 40 | @property (nonatomic, strong) TIMElem *message; 41 | @property (nonatomic, assign) NSTimeInterval timeStamp; 42 | 43 | + (DimMessage *)initWithTIMMessage:(TIMMessage *)timMessage; 44 | @end 45 | 46 | NS_ASSUME_NONNULL_END 47 | -------------------------------------------------------------------------------- /ios/Classes/DimModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // DimModel.m 3 | // dim 4 | // 5 | // Created by 飞鱼 on 2019/2/15. 6 | // 7 | 8 | #import "DimModel.h" 9 | 10 | @implementation DimUser 11 | 12 | + (DimUser *)initWithTimUser:(TIMUserProfile *)timUserProfile { 13 | DimUser *dimUser = [[DimUser alloc]init]; 14 | dimUser.identifier = timUserProfile.identifier; 15 | dimUser.nickName = timUserProfile.nickname; 16 | dimUser.faceURL = timUserProfile.faceURL; 17 | dimUser.selfSignature = [[NSString alloc]initWithData:timUserProfile.selfSignature encoding:NSUTF8StringEncoding]; 18 | dimUser.gender = timUserProfile.gender ? timUserProfile.gender : 1; 19 | dimUser.birthday = timUserProfile.birthday; 20 | dimUser.location = [[NSString alloc]initWithData:timUserProfile.location encoding:NSUTF8StringEncoding]; 21 | return dimUser; 22 | } 23 | 24 | @end 25 | 26 | @implementation DimConversation 27 | 28 | + (DimConversation *)initWithTIMConversation:(TIMConversation *)timConversation { 29 | DimConversation *dimConversation = [[DimConversation alloc]init]; 30 | dimConversation.type = timConversation.getType; 31 | dimConversation.peer = timConversation.getReceiver; 32 | return dimConversation; 33 | } 34 | @end 35 | 36 | @implementation DimMessage 37 | 38 | + (DimMessage *)initWithTIMMessage:(TIMMessage *)timMessage { 39 | DimMessage *dimMessage = [[DimMessage alloc]init]; 40 | dimMessage.sender = timMessage.sender; 41 | dimMessage.timConversation = [DimConversation initWithTIMConversation:timMessage.getConversation]; 42 | dimMessage.timGroupMemberInfo = timMessage.getSenderGroupMemberProfile; 43 | dimMessage.message = [timMessage getElem:0]; 44 | dimMessage.timeStamp = timMessage.timestamp.timeIntervalSince1970; 45 | 46 | return dimMessage; 47 | } 48 | @end 49 | -------------------------------------------------------------------------------- /ios/Classes/DimPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface DimPlugin : NSObject 4 | 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Classes/DimPlugin.m: -------------------------------------------------------------------------------- 1 | #import "DimPlugin.h" 2 | #import 3 | #import "YYModel.h" 4 | #import "DimModel.h" 5 | #import "MJExtension.h" 6 | 7 | @interface DimPlugin() 8 | @property (nonatomic, strong) FlutterEventSink eventSink; 9 | 10 | @end 11 | 12 | @implementation DimPlugin 13 | + (void)registerWithRegistrar:(NSObject*)registrar { 14 | FlutterMethodChannel *channel = [FlutterMethodChannel 15 | methodChannelWithName:@"dim_method" 16 | binaryMessenger:[registrar messenger]]; 17 | DimPlugin* instance = [[DimPlugin alloc] init]; 18 | [registrar addMethodCallDelegate:instance channel:channel]; 19 | 20 | FlutterEventChannel *eventChannel = [FlutterEventChannel eventChannelWithName:@"dim_event" binaryMessenger:[registrar messenger]]; 21 | [eventChannel setStreamHandler:instance]; 22 | } 23 | 24 | - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { 25 | 26 | if ([@"getPlatformVersion" isEqualToString:call.method]) { 27 | result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]); 28 | }else if([ @"init" isEqualToString:call.method] ){ 29 | int appidInt = [call.arguments[@"appid"] intValue]; 30 | //初始化 SDK 基本配置 31 | TIMSdkConfig *config = [[TIMSdkConfig alloc] init]; 32 | config.sdkAppId = appidInt; 33 | config.connListener = self; 34 | 35 | //初始化 SDK 36 | int code = [[TIMManager sharedInstance] initSdk:config]; 37 | NSLog(@"initSdk:result is %d", code); 38 | //将用户配置与通讯管理器进行绑定 39 | TIMUserConfig *userConfig = [[TIMUserConfig alloc] init]; 40 | userConfig.userStatusListener = self; 41 | userConfig.refreshListener = self; 42 | [[TIMManager sharedInstance] setUserConfig:userConfig]; 43 | [[TIMManager sharedInstance] removeMessageListener:self]; 44 | [[TIMManager sharedInstance] addMessageListener:self]; 45 | result(@"init Succ"); 46 | } else if ([ @"im_logout" isEqualToString:call.method] ){ 47 | [[TIMManager sharedInstance] logout:^{ 48 | result(@"Logout Succ"); 49 | } fail:^(int code, NSString *msg) { 50 | result([NSString stringWithFormat:@"Login Failed: %d->%@", code, msg]); 51 | }]; 52 | }else if([@"im_login" isEqualToString:call.method]) { 53 | 54 | NSString *identifier = call.arguments[@"identifier"]; 55 | NSString *userSig = call.arguments[@"userSig"]; 56 | NSLog(@"identifier-->userSig:%@-->%@", identifier,userSig); 57 | TIMLoginParam *param = [[TIMLoginParam alloc ]init]; 58 | 59 | param.identifier = identifier; 60 | param.userSig = userSig; 61 | 62 | [[TIMManager sharedInstance] login: param succ:^(){ 63 | result(@"Login Succ"); 64 | } fail:^(int code, NSString * err) { 65 | NSLog(@"Login Failed: %d->%@", code, err); 66 | result([NSString stringWithFormat:@"Login Failed: %d->%@", code, err]); 67 | }]; 68 | }else if([@"sdkLogout" isEqualToString:call.method]){ 69 | [[TIMManager sharedInstance] logout:^{ 70 | result(@"logout success"); 71 | } fail:^(int code, NSString *msg) { 72 | [NSString stringWithFormat:@"logout failed. code %d desc %@", code, msg]; 73 | }]; 74 | }else if([@"getConversations" isEqualToString:call.method]){ 75 | 76 | NSArray *conversationList = [[TIMManager sharedInstance] getConversationList]; 77 | if (conversationList!=nil && conversationList.count>0) { 78 | NSMutableArray *dictArray = [[NSMutableArray alloc]init]; 79 | for (TIMConversation *conversation in conversationList) { 80 | DimConversation *dimConversation = [DimConversation initWithTIMConversation:conversation ]; 81 | [dictArray addObject:dimConversation]; 82 | } 83 | NSString *jsonString = [dictArray yy_modelToJSONString]; 84 | result(jsonString); 85 | }else{ 86 | result(@"[]"); 87 | } 88 | }else if([@"delConversation" isEqualToString:call.method]){ 89 | NSString *identifier = call.arguments[@"identifier"]; 90 | [[TIMManager sharedInstance] deleteConversation:TIM_C2C receiver:identifier]; 91 | result(@"delConversation success"); 92 | }else if([@"getMessages" isEqualToString:call.method]){ 93 | NSString *identifier = call.arguments[@"identifier"]; 94 | int count = [call.arguments[@"count"] intValue]; 95 | int ctype = [call.arguments[@"ctype"] intValue]; 96 | //TIMMessage *lastMsg = call.arguments[@"lastMsg"]; 97 | TIMConversation *con = [[TIMManager sharedInstance] getConversation: ctype==2 ? TIM_GROUP:TIM_C2C receiver:identifier]; 98 | [con getMessage:count last:NULL succ:^(NSArray *msgs) { 99 | if(msgs != nil && msgs.count > 0){ 100 | NSMutableArray *dictArray = [[NSMutableArray alloc]init]; 101 | for (TIMMessage *message in msgs) { 102 | DimMessage *dimMessage = [DimMessage initWithTIMMessage:message]; 103 | [dictArray addObject:dimMessage]; 104 | } 105 | NSString *jsonString = [dictArray yy_modelToJSONString]; 106 | result(jsonString); 107 | }else{ 108 | result(@"[]"); 109 | } 110 | } fail:^(int code, NSString *msg) { 111 | result([NSString stringWithFormat:@"get message failed. code: %d msg: %@", code, msg]); 112 | }]; 113 | }else if([@"sendTextMessages" isEqualToString:call.method]){ 114 | NSString *identifier = call.arguments[@"identifier"]; 115 | NSString *content = call.arguments[@"content"]; 116 | TIMMessage *msg = [TIMMessage new]; 117 | 118 | //添加文本内容 119 | TIMTextElem *elem = [TIMTextElem new]; 120 | elem.text = content; 121 | 122 | //将elem添加到消息 123 | if([msg addElem:elem] != 0){ 124 | NSLog(@"addElement failed"); 125 | return; 126 | } 127 | TIMConversation *conversation = [[TIMManager sharedInstance] getConversation:TIM_C2C receiver:identifier]; 128 | //发送消息 129 | [conversation sendMessage:msg succ:^{ 130 | result(@"send message ok"); 131 | } fail:^(int code, NSString *msg) { 132 | result([NSString stringWithFormat:@"send message failed. code: %d desc:%@", code, msg]); 133 | }]; 134 | }else if([@"sendImageMessages" isEqualToString:call.method]){ 135 | NSString *identifier = call.arguments[@"identifier"]; 136 | NSString *iamgePath = call.arguments[@"image_path"]; 137 | //构造一条消息 138 | TIMMessage *msg = [TIMMessage new]; 139 | 140 | //添加图片 141 | TIMImageElem *elem = [TIMImageElem new]; 142 | elem.path = iamgePath; 143 | if([msg addElem:elem] != 0){ 144 | NSLog(@"addElement failed"); 145 | } 146 | 147 | TIMConversation *conversation = [[TIMManager sharedInstance] getConversation:TIM_C2C receiver:identifier]; 148 | [conversation sendMessage:msg succ:^{ 149 | result(@"SendMsg ok"); 150 | } fail:^(int code, NSString *msg) { 151 | result([NSString stringWithFormat:@"send message failed. code: %d desc:%@", code, msg]); 152 | }]; 153 | 154 | }else if([@"sendSoundMessages" isEqualToString:call.method]){ 155 | NSString *identifier = call.arguments[@"identifier"]; 156 | NSString *soundpath = call.arguments[@"sound_path"]; 157 | int duration = [call.arguments[@"duration"] intValue]; 158 | //构造一条消息 159 | TIMMessage *msg = [TIMMessage new]; 160 | 161 | //添加声音 162 | TIMSoundElem *elem = [TIMSoundElem new]; 163 | elem.path = soundpath; 164 | elem.second = duration; 165 | if([msg addElem:elem] != 0){ 166 | NSLog(@"addElement failed"); 167 | } 168 | 169 | TIMConversation *conversation = [[TIMManager sharedInstance] getConversation:TIM_C2C receiver:identifier]; 170 | [conversation sendMessage:msg succ:^{ 171 | result(@"SendMsg ok"); 172 | } fail:^(int code, NSString *msg) { 173 | result([NSString stringWithFormat:@"send message failed. code: %d desc:%@", code, msg]); 174 | }]; 175 | 176 | }else if([@"sendLocation" isEqualToString:call.method]){ 177 | NSString *identifier = call.arguments[@"identifier"]; 178 | double lat = [call.arguments[@"lat"] doubleValue]; 179 | double lng = [call.arguments[@"lng"] doubleValue]; 180 | NSString *desc = call.arguments[@"desc"]; 181 | //构造一条消息 182 | TIMMessage *msg = [TIMMessage new]; 183 | 184 | //添加图片 185 | TIMLocationElem *elem = [TIMLocationElem new]; 186 | elem.latitude = lat; 187 | elem.longitude = lng; 188 | elem.desc = desc; 189 | if([msg addElem:elem] != 0){ 190 | NSLog(@"addElement failed"); 191 | } 192 | 193 | TIMConversation *conversation = [[TIMManager sharedInstance] getConversation:TIM_C2C receiver:identifier]; 194 | [conversation sendMessage:msg succ:^{ 195 | result(@"SendMsg ok"); 196 | } fail:^(int code, NSString *msg) { 197 | result([NSString stringWithFormat:@"send message failed. code: %d desc:%@", code, msg]); 198 | }]; 199 | 200 | } 201 | else if([@"post_data_test" isEqualToString:call.method]){ 202 | 203 | NSLog(@"post_data_test invoke"); 204 | self.eventSink(@"hahahahha I am from listener"); 205 | 206 | }else if([@"addFriend" isEqualToString:call.method]){ 207 | 208 | TIMFriendRequest *req = [[TIMFriendRequest alloc] init]; 209 | req.identifier = (NSString *)call.arguments[@"identifier"]; 210 | req.addWording =@"请添加我"; 211 | req.addSource = @"AddSource_Type_iOS"; 212 | [[TIMFriendshipManager sharedInstance] addFriend:req succ:^(TIMFriendResult *addResult) { 213 | if (addResult.result_code == 0) 214 | result(@"添加成功"); 215 | else 216 | result([NSString stringWithFormat:@"异常:%ld, %@", (long)addResult.result_code, addResult.result_info]); 217 | } fail:^(int code, NSString *msg) { 218 | result([NSString stringWithFormat:@"失败:%d, %@", code, msg]); 219 | }]; 220 | 221 | }else if([@"delFriend" isEqualToString:call.method]){ 222 | 223 | 224 | NSMutableArray * del_users = [[NSMutableArray alloc] init]; 225 | // 删除好友 iOS_002 226 | [del_users addObject:@"iOS_002"]; 227 | // TIM_FRIEND_DEL_BOTH 指定删除双向好友 228 | [[TIMFriendshipManager sharedInstance] deleteFriends:del_users delType:TIM_FRIEND_DEL_BOTH succ:^(NSArray *results) { 229 | for (TIMFriendResult * res in results) { 230 | if (res.result_code != TIM_FRIEND_STATUS_SUCC) { 231 | result([NSString stringWithFormat:@"deleteFriends failed: user=%@ status=%ld", res.identifier, (long)res.result_code]); 232 | } 233 | else { 234 | result([NSString stringWithFormat:@"deleteFriends succ: user=%@ status=%ld", res.identifier, (long)res.result_code]); 235 | } 236 | } 237 | } fail:^(int code, NSString * err) { 238 | result([NSString stringWithFormat:@"deleteFriends failed: code=%d err=%@", code, err]); 239 | }]; 240 | 241 | }else if([@"listFriends" isEqualToString:call.method]){ 242 | 243 | [[TIMFriendshipManager sharedInstance] getFriendList:^(NSArray * arr) { 244 | NSString *jsonString = [arr yy_modelToJSONString]; 245 | result(jsonString); 246 | }fail:^(int code, NSString * err) { 247 | result([NSString stringWithFormat:@"GetFriendList fail: code=%d err=%@", code, err]); 248 | }]; 249 | 250 | }else if([@"opFriend" isEqualToString:call.method]){ 251 | 252 | NSString *identifier = call.arguments[@"identifier"]; 253 | NSString *opTypeStr = call.arguments[@"opTypeStr"]; 254 | TIMFriendResponse *response = [[TIMFriendResponse alloc] init]; 255 | response.identifier = identifier; 256 | if([opTypeStr isEqualToString:@"Y"]){ 257 | response.responseType = TIM_FRIEND_RESPONSE_AGREE_AND_ADD; 258 | }else{ 259 | response.responseType = TIM_FRIEND_RESPONSE_REJECT; 260 | } 261 | [[TIMFriendshipManager sharedInstance] doResponse:response succ:^(TIMFriendResult *res) { 262 | if (res.result_code != TIM_FRIEND_STATUS_SUCC) { 263 | result([NSString stringWithFormat:@"deleteFriends failed: user=%@ status=%ld", res.identifier, (long)res.result_code]); 264 | } 265 | else { 266 | result([NSString stringWithFormat:@"deleteFriends succ: user=%@ status=%ld", res.identifier, (long)res.result_code]); 267 | } 268 | } fail:^(int code, NSString *err) { 269 | result([NSString stringWithFormat:@"opFriend fail: code=%d err=%@", code, err]); 270 | }]; 271 | 272 | } 273 | else if([@"getUsersProfile" isEqualToString:call.method]){ 274 | 275 | NSArray *arr1 = call.arguments[@"users"]; 276 | 277 | 278 | [[TIMFriendshipManager sharedInstance] getUsersProfile:arr1 forceUpdate:YES succ:^(NSArray * arr) { 279 | // for (TIMUserProfile * profile in arr) { 280 | // NSLog(@"user=%@", profile); 281 | // } 282 | if (arr !=NULL && arr.count >0) { 283 | NSString *jsonString = [arr yy_modelToJSONString]; 284 | result(jsonString); 285 | }else{ 286 | result(@"[]"); 287 | } 288 | }fail:^(int code, NSString * err) { 289 | 290 | result([NSString stringWithFormat:@"getUsersProfile fail: code=%d err=%@", code, err]); 291 | }]; 292 | } 293 | else if([@"setUsersProfile" isEqualToString:call.method]){ 294 | NSString *nick = call.arguments[@"nick"]; 295 | NSInteger gender = (NSInteger)call.arguments[@"gender"]; 296 | NSString *faceUrl = call.arguments[@"faceUrl"]; 297 | [[TIMFriendshipManager sharedInstance]modifySelfProfile:@{TIMProfileTypeKey_Nick:nick,TIMProfileTypeKey_FaceUrl:faceUrl,TIMProfileTypeKey_Gender:[NSNumber numberWithInt:gender==1?TIM_GENDER_MALE:TIM_GENDER_FEMALE]} succ:^{ 298 | result(@"setUsersProfile succ"); 299 | } fail:^(int code, NSString *err) { 300 | result([NSString stringWithFormat:@"GetFriendList fail: code=%d err=%@", code, err]); 301 | }]; 302 | } 303 | else { 304 | result(FlutterMethodNotImplemented); 305 | } 306 | } 307 | 308 | 309 | #pragma mark - FlutterStreamHandler 310 | - (FlutterError*)onListenWithArguments:(id)arguments 311 | eventSink:(FlutterEventSink)eventSink { 312 | self.eventSink = eventSink; 313 | // [[UIDevice currentDevice] setBatteryMonitoringEnabled:YES]; 314 | // [self sendBatteryStateEvent]; 315 | // [[NSNotificationCenter defaultCenter] 316 | // addObserver:self 317 | // selector:@selector(onBatteryStateDidChange:) 318 | // name:UIDeviceBatteryStateDidChangeNotification 319 | // object:nil]; 320 | return nil; 321 | } 322 | 323 | - (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments{ 324 | return nil; 325 | } 326 | 327 | #pragma mark - TIMMessageListener 328 | /** 329 | * 新消息回调通知 330 | * 331 | * @param msgs 新消息列表,TIMMessage 类型数组 332 | */ 333 | - (void)onNewMessage:(NSArray*)msgs{ 334 | if(msgs != nil && msgs.count > 0){ 335 | NSMutableArray *dictArray = [[NSMutableArray alloc]init]; 336 | for (TIMMessage *message in msgs) { 337 | DimMessage *dimMessage = [DimMessage initWithTIMMessage:message]; 338 | [dictArray addObject:dimMessage]; 339 | } 340 | NSString *jsonString = [dictArray yy_modelToJSONString]; 341 | self.eventSink(jsonString); 342 | } 343 | } 344 | 345 | #pragma mark - TIMRefreshListener 346 | /** 347 | * 会话列表变动 348 | */ 349 | - (void)onRefresh{ 350 | self.eventSink(@"[]"); 351 | } 352 | @end 353 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/MJExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJExtension.h 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 代码地址:https://github.com/CoderMJLee/MJExtension 8 | // 代码地址:http://code4app.com/ios/%E5%AD%97%E5%85%B8-JSON-%E4%B8%8E%E6%A8%A1%E5%9E%8B%E7%9A%84%E8%BD%AC%E6%8D%A2/5339992a933bf062608b4c57 9 | 10 | #import "NSObject+MJCoding.h" 11 | #import "NSObject+MJProperty.h" 12 | #import "NSObject+MJClass.h" 13 | #import "NSObject+MJKeyValue.h" 14 | #import "NSString+MJExtension.h" 15 | #import "MJExtensionConst.h" -------------------------------------------------------------------------------- /ios/Classes/MJExtension/MJExtensionConst.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __MJExtensionConst__H__ 3 | #define __MJExtensionConst__H__ 4 | 5 | #import 6 | 7 | // 信号量 8 | #define MJExtensionSemaphoreCreate \ 9 | static dispatch_semaphore_t signalSemaphore; \ 10 | static dispatch_once_t onceTokenSemaphore; \ 11 | dispatch_once(&onceTokenSemaphore, ^{ \ 12 | signalSemaphore = dispatch_semaphore_create(1); \ 13 | }); 14 | 15 | #define MJExtensionSemaphoreWait \ 16 | dispatch_semaphore_wait(signalSemaphore, DISPATCH_TIME_FOREVER); 17 | 18 | #define MJExtensionSemaphoreSignal \ 19 | dispatch_semaphore_signal(signalSemaphore); 20 | 21 | // 过期 22 | #define MJExtensionDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead) 23 | 24 | // 构建错误 25 | #define MJExtensionBuildError(clazz, msg) \ 26 | NSError *error = [NSError errorWithDomain:msg code:250 userInfo:nil]; \ 27 | [clazz setMj_error:error]; 28 | 29 | // 日志输出 30 | #ifdef DEBUG 31 | #define MJExtensionLog(...) NSLog(__VA_ARGS__) 32 | #else 33 | #define MJExtensionLog(...) 34 | #endif 35 | 36 | /** 37 | * 断言 38 | * @param condition 条件 39 | * @param returnValue 返回值 40 | */ 41 | #define MJExtensionAssertError(condition, returnValue, clazz, msg) \ 42 | [clazz setMj_error:nil]; \ 43 | if ((condition) == NO) { \ 44 | MJExtensionBuildError(clazz, msg); \ 45 | return returnValue;\ 46 | } 47 | 48 | #define MJExtensionAssert2(condition, returnValue) \ 49 | if ((condition) == NO) return returnValue; 50 | 51 | /** 52 | * 断言 53 | * @param condition 条件 54 | */ 55 | #define MJExtensionAssert(condition) MJExtensionAssert2(condition, ) 56 | 57 | /** 58 | * 断言 59 | * @param param 参数 60 | * @param returnValue 返回值 61 | */ 62 | #define MJExtensionAssertParamNotNil2(param, returnValue) \ 63 | MJExtensionAssert2((param) != nil, returnValue) 64 | 65 | /** 66 | * 断言 67 | * @param param 参数 68 | */ 69 | #define MJExtensionAssertParamNotNil(param) MJExtensionAssertParamNotNil2(param, ) 70 | 71 | /** 72 | * 打印所有的属性 73 | */ 74 | #define MJLogAllIvars \ 75 | -(NSString *)description \ 76 | { \ 77 | return [self mj_keyValues].description; \ 78 | } 79 | #define MJExtensionLogAllProperties MJLogAllIvars 80 | 81 | /** 82 | * 类型(属性类型) 83 | */ 84 | extern NSString *const MJPropertyTypeInt; 85 | extern NSString *const MJPropertyTypeShort; 86 | extern NSString *const MJPropertyTypeFloat; 87 | extern NSString *const MJPropertyTypeDouble; 88 | extern NSString *const MJPropertyTypeLong; 89 | extern NSString *const MJPropertyTypeLongLong; 90 | extern NSString *const MJPropertyTypeChar; 91 | extern NSString *const MJPropertyTypeBOOL1; 92 | extern NSString *const MJPropertyTypeBOOL2; 93 | extern NSString *const MJPropertyTypePointer; 94 | 95 | extern NSString *const MJPropertyTypeIvar; 96 | extern NSString *const MJPropertyTypeMethod; 97 | extern NSString *const MJPropertyTypeBlock; 98 | extern NSString *const MJPropertyTypeClass; 99 | extern NSString *const MJPropertyTypeSEL; 100 | extern NSString *const MJPropertyTypeId; 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/MJExtensionConst.m: -------------------------------------------------------------------------------- 1 | #ifndef __MJExtensionConst__M__ 2 | #define __MJExtensionConst__M__ 3 | 4 | #import 5 | 6 | /** 7 | * 成员变量类型(属性类型) 8 | */ 9 | NSString *const MJPropertyTypeInt = @"i"; 10 | NSString *const MJPropertyTypeShort = @"s"; 11 | NSString *const MJPropertyTypeFloat = @"f"; 12 | NSString *const MJPropertyTypeDouble = @"d"; 13 | NSString *const MJPropertyTypeLong = @"l"; 14 | NSString *const MJPropertyTypeLongLong = @"q"; 15 | NSString *const MJPropertyTypeChar = @"c"; 16 | NSString *const MJPropertyTypeBOOL1 = @"c"; 17 | NSString *const MJPropertyTypeBOOL2 = @"b"; 18 | NSString *const MJPropertyTypePointer = @"*"; 19 | 20 | NSString *const MJPropertyTypeIvar = @"^{objc_ivar=}"; 21 | NSString *const MJPropertyTypeMethod = @"^{objc_method=}"; 22 | NSString *const MJPropertyTypeBlock = @"@?"; 23 | NSString *const MJPropertyTypeClass = @"#"; 24 | NSString *const MJPropertyTypeSEL = @":"; 25 | NSString *const MJPropertyTypeId = @"@"; 26 | 27 | #endif -------------------------------------------------------------------------------- /ios/Classes/MJExtension/MJFoundation.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJFoundation.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 14/7/16. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MJFoundation : NSObject 12 | + (BOOL)isClassFromFoundation:(Class)c; 13 | @end 14 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/MJFoundation.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJFoundation.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 14/7/16. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJFoundation.h" 10 | #import "MJExtensionConst.h" 11 | #import 12 | 13 | @implementation MJFoundation 14 | 15 | + (BOOL)isClassFromFoundation:(Class)c 16 | { 17 | if (c == [NSObject class] || c == [NSManagedObject class]) return YES; 18 | 19 | static NSSet *foundationClasses; 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | // 集合中没有NSObject,因为几乎所有的类都是继承自NSObject,具体是不是NSObject需要特殊判断 23 | foundationClasses = [NSSet setWithObjects: 24 | [NSURL class], 25 | [NSDate class], 26 | [NSValue class], 27 | [NSData class], 28 | [NSError class], 29 | [NSArray class], 30 | [NSDictionary class], 31 | [NSString class], 32 | [NSAttributedString class], nil]; 33 | }); 34 | 35 | __block BOOL result = NO; 36 | [foundationClasses enumerateObjectsUsingBlock:^(Class foundationClass, BOOL *stop) { 37 | if ([c isSubclassOfClass:foundationClass]) { 38 | result = YES; 39 | *stop = YES; 40 | } 41 | }]; 42 | return result; 43 | } 44 | @end 45 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/MJProperty.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJProperty.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 包装一个成员属性 8 | 9 | #import 10 | #import 11 | #import "MJPropertyType.h" 12 | #import "MJPropertyKey.h" 13 | 14 | /** 15 | * 包装一个成员 16 | */ 17 | @interface MJProperty : NSObject 18 | /** 成员属性 */ 19 | @property (nonatomic, assign) objc_property_t property; 20 | /** 成员属性的名字 */ 21 | @property (nonatomic, readonly) NSString *name; 22 | 23 | /** 成员属性的类型 */ 24 | @property (nonatomic, readonly) MJPropertyType *type; 25 | /** 成员属性来源于哪个类(可能是父类) */ 26 | @property (nonatomic, assign) Class srcClass; 27 | 28 | /**** 同一个成员属性 - 父类和子类的行为可能不一致(originKey、propertyKeys、objectClassInArray) ****/ 29 | /** 设置最原始的key */ 30 | - (void)setOriginKey:(id)originKey forClass:(Class)c; 31 | /** 对应着字典中的多级key(里面存放的数组,数组里面都是MJPropertyKey对象) */ 32 | - (NSArray *)propertyKeysForClass:(Class)c; 33 | 34 | /** 模型数组中的模型类型 */ 35 | - (void)setObjectClassInArray:(Class)objectClass forClass:(Class)c; 36 | - (Class)objectClassInArrayForClass:(Class)c; 37 | /**** 同一个成员变量 - 父类和子类的行为可能不一致(key、keys、objectClassInArray) ****/ 38 | 39 | /** 40 | * 设置object的成员变量值 41 | */ 42 | - (void)setValue:(id)value forObject:(id)object; 43 | /** 44 | * 得到object的成员属性值 45 | */ 46 | - (id)valueForObject:(id)object; 47 | 48 | /** 49 | * 初始化 50 | */ 51 | + (instancetype)cachedPropertyWithProperty:(objc_property_t)property; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/MJProperty.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJProperty.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJProperty.h" 10 | #import "MJFoundation.h" 11 | #import "MJExtensionConst.h" 12 | #import 13 | 14 | @interface MJProperty() 15 | @property (strong, nonatomic) NSMutableDictionary *propertyKeysDict; 16 | @property (strong, nonatomic) NSMutableDictionary *objectClassInArrayDict; 17 | @end 18 | 19 | @implementation MJProperty 20 | 21 | #pragma mark - 初始化 22 | - (instancetype)init 23 | { 24 | if (self = [super init]) { 25 | _propertyKeysDict = [NSMutableDictionary dictionary]; 26 | _objectClassInArrayDict = [NSMutableDictionary dictionary]; 27 | } 28 | return self; 29 | } 30 | 31 | #pragma mark - 缓存 32 | + (instancetype)cachedPropertyWithProperty:(objc_property_t)property 33 | { 34 | MJExtensionSemaphoreCreate 35 | MJExtensionSemaphoreWait 36 | MJProperty *propertyObj = objc_getAssociatedObject(self, property); 37 | if (propertyObj == nil) { 38 | propertyObj = [[self alloc] init]; 39 | propertyObj.property = property; 40 | objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 41 | } 42 | MJExtensionSemaphoreSignal 43 | return propertyObj; 44 | } 45 | 46 | #pragma mark - 公共方法 47 | - (void)setProperty:(objc_property_t)property 48 | { 49 | _property = property; 50 | 51 | MJExtensionAssertParamNotNil(property); 52 | 53 | // 1.属性名 54 | _name = @(property_getName(property)); 55 | 56 | // 2.成员类型 57 | NSString *attrs = @(property_getAttributes(property)); 58 | NSUInteger dotLoc = [attrs rangeOfString:@","].location; 59 | NSString *code = nil; 60 | NSUInteger loc = 1; 61 | if (dotLoc == NSNotFound) { // 没有, 62 | code = [attrs substringFromIndex:loc]; 63 | } else { 64 | code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc)]; 65 | } 66 | _type = [MJPropertyType cachedTypeWithCode:code]; 67 | } 68 | 69 | /** 70 | * 获得成员变量的值 71 | */ 72 | - (id)valueForObject:(id)object 73 | { 74 | if (self.type.KVCDisabled) return [NSNull null]; 75 | return [object valueForKey:self.name]; 76 | } 77 | 78 | /** 79 | * 设置成员变量的值 80 | */ 81 | - (void)setValue:(id)value forObject:(id)object 82 | { 83 | if (self.type.KVCDisabled || value == nil) return; 84 | [object setValue:value forKey:self.name]; 85 | } 86 | 87 | /** 88 | * 通过字符串key创建对应的keys 89 | */ 90 | - (NSArray *)propertyKeysWithStringKey:(NSString *)stringKey 91 | { 92 | if (stringKey.length == 0) return nil; 93 | 94 | NSMutableArray *propertyKeys = [NSMutableArray array]; 95 | // 如果有多级映射 96 | NSArray *oldKeys = [stringKey componentsSeparatedByString:@"."]; 97 | 98 | for (NSString *oldKey in oldKeys) { 99 | NSUInteger start = [oldKey rangeOfString:@"["].location; 100 | if (start != NSNotFound) { // 有索引的key 101 | NSString *prefixKey = [oldKey substringToIndex:start]; 102 | NSString *indexKey = prefixKey; 103 | if (prefixKey.length) { 104 | MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init]; 105 | propertyKey.name = prefixKey; 106 | [propertyKeys addObject:propertyKey]; 107 | 108 | indexKey = [oldKey stringByReplacingOccurrencesOfString:prefixKey withString:@""]; 109 | } 110 | 111 | /** 解析索引 **/ 112 | // 元素 113 | NSArray *cmps = [[indexKey stringByReplacingOccurrencesOfString:@"[" withString:@""] componentsSeparatedByString:@"]"]; 114 | for (NSInteger i = 0; i 10 | 11 | typedef enum { 12 | MJPropertyKeyTypeDictionary = 0, // 字典的key 13 | MJPropertyKeyTypeArray // 数组的key 14 | } MJPropertyKeyType; 15 | 16 | /** 17 | * 属性的key 18 | */ 19 | @interface MJPropertyKey : NSObject 20 | /** key的名字 */ 21 | @property (copy, nonatomic) NSString *name; 22 | /** key的种类,可能是@"10",可能是@"age" */ 23 | @property (assign, nonatomic) MJPropertyKeyType type; 24 | 25 | /** 26 | * 根据当前的key,也就是name,从object(字典或者数组)中取值 27 | */ 28 | - (id)valueInObject:(id)object; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/MJPropertyKey.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJPropertyKey.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/11. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJPropertyKey.h" 10 | 11 | @implementation MJPropertyKey 12 | 13 | - (id)valueInObject:(id)object 14 | { 15 | if ([object isKindOfClass:[NSDictionary class]] && self.type == MJPropertyKeyTypeDictionary) { 16 | return object[self.name]; 17 | } else if ([object isKindOfClass:[NSArray class]] && self.type == MJPropertyKeyTypeArray) { 18 | NSArray *array = object; 19 | NSUInteger index = self.name.intValue; 20 | if (index < array.count) return array[index]; 21 | return nil; 22 | } 23 | return nil; 24 | } 25 | @end 26 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/MJPropertyType.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJPropertyType.h 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 包装一种类型 8 | 9 | #import 10 | 11 | /** 12 | * 包装一种类型 13 | */ 14 | @interface MJPropertyType : NSObject 15 | /** 类型标识符 */ 16 | @property (nonatomic, copy) NSString *code; 17 | 18 | /** 是否为id类型 */ 19 | @property (nonatomic, readonly, getter=isIdType) BOOL idType; 20 | 21 | /** 是否为基本数字类型:int、float等 */ 22 | @property (nonatomic, readonly, getter=isNumberType) BOOL numberType; 23 | 24 | /** 是否为BOOL类型 */ 25 | @property (nonatomic, readonly, getter=isBoolType) BOOL boolType; 26 | 27 | /** 对象类型(如果是基本数据类型,此值为nil) */ 28 | @property (nonatomic, readonly) Class typeClass; 29 | 30 | /** 类型是否来自于Foundation框架,比如NSString、NSArray */ 31 | @property (nonatomic, readonly, getter = isFromFoundation) BOOL fromFoundation; 32 | /** 类型是否不支持KVC */ 33 | @property (nonatomic, readonly, getter = isKVCDisabled) BOOL KVCDisabled; 34 | 35 | /** 36 | * 获得缓存的类型对象 37 | */ 38 | + (instancetype)cachedTypeWithCode:(NSString *)code; 39 | @end -------------------------------------------------------------------------------- /ios/Classes/MJExtension/MJPropertyType.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJPropertyType.m 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJPropertyType.h" 10 | #import "MJExtension.h" 11 | #import "MJFoundation.h" 12 | #import "MJExtensionConst.h" 13 | 14 | @implementation MJPropertyType 15 | 16 | + (instancetype)cachedTypeWithCode:(NSString *)code 17 | { 18 | MJExtensionAssertParamNotNil2(code, nil); 19 | 20 | static NSMutableDictionary *types; 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | types = [NSMutableDictionary dictionary]; 24 | }); 25 | 26 | MJExtensionSemaphoreCreate 27 | MJExtensionSemaphoreWait 28 | MJPropertyType *type = types[code]; 29 | if (type == nil) { 30 | type = [[self alloc] init]; 31 | type.code = code; 32 | types[code] = type; 33 | } 34 | MJExtensionSemaphoreSignal 35 | return type; 36 | } 37 | 38 | #pragma mark - 公共方法 39 | - (void)setCode:(NSString *)code 40 | { 41 | _code = code; 42 | 43 | MJExtensionAssertParamNotNil(code); 44 | 45 | if ([code isEqualToString:MJPropertyTypeId]) { 46 | _idType = YES; 47 | } else if (code.length == 0) { 48 | _KVCDisabled = YES; 49 | } else if (code.length > 3 && [code hasPrefix:@"@\""]) { 50 | // 去掉@"和",截取中间的类型名称 51 | _code = [code substringWithRange:NSMakeRange(2, code.length - 3)]; 52 | _typeClass = NSClassFromString(_code); 53 | _fromFoundation = [MJFoundation isClassFromFoundation:_typeClass]; 54 | _numberType = [_typeClass isSubclassOfClass:[NSNumber class]]; 55 | 56 | } else if ([code isEqualToString:MJPropertyTypeSEL] || 57 | [code isEqualToString:MJPropertyTypeIvar] || 58 | [code isEqualToString:MJPropertyTypeMethod]) { 59 | _KVCDisabled = YES; 60 | } 61 | 62 | // 是否为数字类型 63 | NSString *lowerCode = _code.lowercaseString; 64 | NSArray *numberTypes = @[MJPropertyTypeInt, MJPropertyTypeShort, MJPropertyTypeBOOL1, MJPropertyTypeBOOL2, MJPropertyTypeFloat, MJPropertyTypeDouble, MJPropertyTypeLong, MJPropertyTypeLongLong, MJPropertyTypeChar]; 65 | if ([numberTypes containsObject:lowerCode]) { 66 | _numberType = YES; 67 | 68 | if ([lowerCode isEqualToString:MJPropertyTypeBOOL1] 69 | || [lowerCode isEqualToString:MJPropertyTypeBOOL2]) { 70 | _boolType = YES; 71 | } 72 | } 73 | } 74 | @end 75 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/NSObject+MJClass.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJClass.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/11. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * 遍历所有类的block(父类) 13 | */ 14 | typedef void (^MJClassesEnumeration)(Class c, BOOL *stop); 15 | 16 | /** 这个数组中的属性名才会进行字典和模型的转换 */ 17 | typedef NSArray * (^MJAllowedPropertyNames)(void); 18 | /** 这个数组中的属性名才会进行归档 */ 19 | typedef NSArray * (^MJAllowedCodingPropertyNames)(void); 20 | 21 | /** 这个数组中的属性名将会被忽略:不进行字典和模型的转换 */ 22 | typedef NSArray * (^MJIgnoredPropertyNames)(void); 23 | /** 这个数组中的属性名将会被忽略:不进行归档 */ 24 | typedef NSArray * (^MJIgnoredCodingPropertyNames)(void); 25 | 26 | /** 27 | * 类相关的扩展 28 | */ 29 | @interface NSObject (MJClass) 30 | /** 31 | * 遍历所有的类 32 | */ 33 | + (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration; 34 | + (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration; 35 | 36 | #pragma mark - 属性白名单配置 37 | /** 38 | * 这个数组中的属性名才会进行字典和模型的转换 39 | * 40 | * @param allowedPropertyNames 这个数组中的属性名才会进行字典和模型的转换 41 | */ 42 | + (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; 43 | 44 | /** 45 | * 这个数组中的属性名才会进行字典和模型的转换 46 | */ 47 | + (NSMutableArray *)mj_totalAllowedPropertyNames; 48 | 49 | #pragma mark - 属性黑名单配置 50 | /** 51 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 52 | * 53 | * @param ignoredPropertyNames 这个数组中的属性名将会被忽略:不进行字典和模型的转换 54 | */ 55 | + (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames; 56 | 57 | /** 58 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 59 | */ 60 | + (NSMutableArray *)mj_totalIgnoredPropertyNames; 61 | 62 | #pragma mark - 归档属性白名单配置 63 | /** 64 | * 这个数组中的属性名才会进行归档 65 | * 66 | * @param allowedCodingPropertyNames 这个数组中的属性名才会进行归档 67 | */ 68 | + (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames; 69 | 70 | /** 71 | * 这个数组中的属性名才会进行字典和模型的转换 72 | */ 73 | + (NSMutableArray *)mj_totalAllowedCodingPropertyNames; 74 | 75 | #pragma mark - 归档属性黑名单配置 76 | /** 77 | * 这个数组中的属性名将会被忽略:不进行归档 78 | * 79 | * @param ignoredCodingPropertyNames 这个数组中的属性名将会被忽略:不进行归档 80 | */ 81 | + (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames; 82 | 83 | /** 84 | * 这个数组中的属性名将会被忽略:不进行归档 85 | */ 86 | + (NSMutableArray *)mj_totalIgnoredCodingPropertyNames; 87 | 88 | #pragma mark - 内部使用 89 | + (void)mj_setupBlockReturnValue:(id (^)(void))block key:(const char *)key; 90 | @end 91 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/NSObject+MJClass.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJClass.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/11. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJClass.h" 10 | #import "NSObject+MJCoding.h" 11 | #import "NSObject+MJKeyValue.h" 12 | #import "MJFoundation.h" 13 | #import 14 | 15 | static const char MJAllowedPropertyNamesKey = '\0'; 16 | static const char MJIgnoredPropertyNamesKey = '\0'; 17 | static const char MJAllowedCodingPropertyNamesKey = '\0'; 18 | static const char MJIgnoredCodingPropertyNamesKey = '\0'; 19 | 20 | @implementation NSObject (MJClass) 21 | 22 | + (NSMutableDictionary *)classDictForKey:(const void *)key 23 | { 24 | static NSMutableDictionary *allowedPropertyNamesDict; 25 | static NSMutableDictionary *ignoredPropertyNamesDict; 26 | static NSMutableDictionary *allowedCodingPropertyNamesDict; 27 | static NSMutableDictionary *ignoredCodingPropertyNamesDict; 28 | 29 | static dispatch_once_t onceToken; 30 | dispatch_once(&onceToken, ^{ 31 | allowedPropertyNamesDict = [NSMutableDictionary dictionary]; 32 | ignoredPropertyNamesDict = [NSMutableDictionary dictionary]; 33 | allowedCodingPropertyNamesDict = [NSMutableDictionary dictionary]; 34 | ignoredCodingPropertyNamesDict = [NSMutableDictionary dictionary]; 35 | }); 36 | 37 | if (key == &MJAllowedPropertyNamesKey) return allowedPropertyNamesDict; 38 | if (key == &MJIgnoredPropertyNamesKey) return ignoredPropertyNamesDict; 39 | if (key == &MJAllowedCodingPropertyNamesKey) return allowedCodingPropertyNamesDict; 40 | if (key == &MJIgnoredCodingPropertyNamesKey) return ignoredCodingPropertyNamesDict; 41 | return nil; 42 | } 43 | 44 | + (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration 45 | { 46 | // 1.没有block就直接返回 47 | if (enumeration == nil) return; 48 | 49 | // 2.停止遍历的标记 50 | BOOL stop = NO; 51 | 52 | // 3.当前正在遍历的类 53 | Class c = self; 54 | 55 | // 4.开始遍历每一个类 56 | while (c && !stop) { 57 | // 4.1.执行操作 58 | enumeration(c, &stop); 59 | 60 | // 4.2.获得父类 61 | c = class_getSuperclass(c); 62 | 63 | if ([MJFoundation isClassFromFoundation:c]) break; 64 | } 65 | } 66 | 67 | + (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration 68 | { 69 | // 1.没有block就直接返回 70 | if (enumeration == nil) return; 71 | 72 | // 2.停止遍历的标记 73 | BOOL stop = NO; 74 | 75 | // 3.当前正在遍历的类 76 | Class c = self; 77 | 78 | // 4.开始遍历每一个类 79 | while (c && !stop) { 80 | // 4.1.执行操作 81 | enumeration(c, &stop); 82 | 83 | // 4.2.获得父类 84 | c = class_getSuperclass(c); 85 | } 86 | } 87 | 88 | #pragma mark - 属性黑名单配置 89 | + (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames 90 | { 91 | [self mj_setupBlockReturnValue:ignoredPropertyNames key:&MJIgnoredPropertyNamesKey]; 92 | } 93 | 94 | + (NSMutableArray *)mj_totalIgnoredPropertyNames 95 | { 96 | return [self mj_totalObjectsWithSelector:@selector(mj_ignoredPropertyNames) key:&MJIgnoredPropertyNamesKey]; 97 | } 98 | 99 | #pragma mark - 归档属性黑名单配置 100 | + (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames 101 | { 102 | [self mj_setupBlockReturnValue:ignoredCodingPropertyNames key:&MJIgnoredCodingPropertyNamesKey]; 103 | } 104 | 105 | + (NSMutableArray *)mj_totalIgnoredCodingPropertyNames 106 | { 107 | return [self mj_totalObjectsWithSelector:@selector(mj_ignoredCodingPropertyNames) key:&MJIgnoredCodingPropertyNamesKey]; 108 | } 109 | 110 | #pragma mark - 属性白名单配置 111 | + (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; 112 | { 113 | [self mj_setupBlockReturnValue:allowedPropertyNames key:&MJAllowedPropertyNamesKey]; 114 | } 115 | 116 | + (NSMutableArray *)mj_totalAllowedPropertyNames 117 | { 118 | return [self mj_totalObjectsWithSelector:@selector(mj_allowedPropertyNames) key:&MJAllowedPropertyNamesKey]; 119 | } 120 | 121 | #pragma mark - 归档属性白名单配置 122 | + (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames 123 | { 124 | [self mj_setupBlockReturnValue:allowedCodingPropertyNames key:&MJAllowedCodingPropertyNamesKey]; 125 | } 126 | 127 | + (NSMutableArray *)mj_totalAllowedCodingPropertyNames 128 | { 129 | return [self mj_totalObjectsWithSelector:@selector(mj_allowedCodingPropertyNames) key:&MJAllowedCodingPropertyNamesKey]; 130 | } 131 | 132 | #pragma mark - block和方法处理:存储block的返回值 133 | + (void)mj_setupBlockReturnValue:(id (^)(void))block key:(const char *)key 134 | { 135 | if (block) { 136 | objc_setAssociatedObject(self, key, block(), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 137 | } else { 138 | objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 139 | } 140 | 141 | // 清空数据 142 | MJExtensionSemaphoreCreate 143 | MJExtensionSemaphoreWait 144 | [[self classDictForKey:key] removeAllObjects]; 145 | MJExtensionSemaphoreSignal 146 | } 147 | 148 | + (NSMutableArray *)mj_totalObjectsWithSelector:(SEL)selector key:(const char *)key 149 | { 150 | MJExtensionSemaphoreCreate 151 | MJExtensionSemaphoreWait 152 | 153 | NSMutableArray *array = [self classDictForKey:key][NSStringFromClass(self)]; 154 | if (array == nil) { 155 | // 创建、存储 156 | [self classDictForKey:key][NSStringFromClass(self)] = array = [NSMutableArray array]; 157 | 158 | if ([self respondsToSelector:selector]) { 159 | #pragma clang diagnostic push 160 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 161 | NSArray *subArray = [self performSelector:selector]; 162 | #pragma clang diagnostic pop 163 | if (subArray) { 164 | [array addObjectsFromArray:subArray]; 165 | } 166 | } 167 | 168 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 169 | NSArray *subArray = objc_getAssociatedObject(c, key); 170 | [array addObjectsFromArray:subArray]; 171 | }]; 172 | } 173 | 174 | MJExtensionSemaphoreSignal 175 | 176 | return array; 177 | } 178 | @end 179 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/NSObject+MJCoding.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJCoding.h 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MJExtensionConst.h" 11 | 12 | /** 13 | * Codeing协议 14 | */ 15 | @protocol MJCoding 16 | @optional 17 | /** 18 | * 这个数组中的属性名才会进行归档 19 | */ 20 | + (NSArray *)mj_allowedCodingPropertyNames; 21 | /** 22 | * 这个数组中的属性名将会被忽略:不进行归档 23 | */ 24 | + (NSArray *)mj_ignoredCodingPropertyNames; 25 | @end 26 | 27 | @interface NSObject (MJCoding) 28 | /** 29 | * 解码(从文件中解析对象) 30 | */ 31 | - (void)mj_decode:(NSCoder *)decoder; 32 | /** 33 | * 编码(将对象写入文件中) 34 | */ 35 | - (void)mj_encode:(NSCoder *)encoder; 36 | @end 37 | 38 | /** 39 | 归档的实现 40 | */ 41 | #define MJCodingImplementation \ 42 | - (id)initWithCoder:(NSCoder *)decoder \ 43 | { \ 44 | if (self = [super init]) { \ 45 | [self mj_decode:decoder]; \ 46 | } \ 47 | return self; \ 48 | } \ 49 | \ 50 | - (void)encodeWithCoder:(NSCoder *)encoder \ 51 | { \ 52 | [self mj_encode:encoder]; \ 53 | } 54 | 55 | #define MJExtensionCodingImplementation MJCodingImplementation -------------------------------------------------------------------------------- /ios/Classes/MJExtension/NSObject+MJCoding.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJCoding.m 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJCoding.h" 10 | #import "NSObject+MJClass.h" 11 | #import "NSObject+MJProperty.h" 12 | #import "MJProperty.h" 13 | 14 | @implementation NSObject (MJCoding) 15 | 16 | - (void)mj_encode:(NSCoder *)encoder 17 | { 18 | Class clazz = [self class]; 19 | 20 | NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; 21 | NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; 22 | 23 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { 24 | // 检测是否被忽略 25 | if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; 26 | if ([ignoredCodingPropertyNames containsObject:property.name]) return; 27 | 28 | id value = [property valueForObject:self]; 29 | if (value == nil) return; 30 | [encoder encodeObject:value forKey:property.name]; 31 | }]; 32 | } 33 | 34 | - (void)mj_decode:(NSCoder *)decoder 35 | { 36 | Class clazz = [self class]; 37 | 38 | NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; 39 | NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; 40 | 41 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { 42 | // 检测是否被忽略 43 | if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; 44 | if ([ignoredCodingPropertyNames containsObject:property.name]) return; 45 | 46 | id value = [decoder decodeObjectForKey:property.name]; 47 | if (value == nil) { // 兼容以前的MJExtension版本 48 | value = [decoder decodeObjectForKey:[@"_" stringByAppendingString:property.name]]; 49 | } 50 | if (value == nil) return; 51 | [property setValue:value forObject:self]; 52 | }]; 53 | } 54 | @end 55 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/NSObject+MJKeyValue.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJKeyValue.h 3 | // MJExtension 4 | // 5 | // Created by mj on 13-8-24. 6 | // Copyright (c) 2013年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MJExtensionConst.h" 11 | #import 12 | #import "MJProperty.h" 13 | 14 | /** 15 | * KeyValue协议 16 | */ 17 | @protocol MJKeyValue 18 | @optional 19 | /** 20 | * 只有这个数组中的属性名才允许进行字典和模型的转换 21 | */ 22 | + (NSArray *)mj_allowedPropertyNames; 23 | 24 | /** 25 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 26 | */ 27 | + (NSArray *)mj_ignoredPropertyNames; 28 | 29 | /** 30 | * 将属性名换为其他key去字典中取值 31 | * 32 | * @return 字典中的key是属性名,value是从字典中取值用的key 33 | */ 34 | + (NSDictionary *)mj_replacedKeyFromPropertyName; 35 | 36 | /** 37 | * 将属性名换为其他key去字典中取值 38 | * 39 | * @return 从字典中取值用的key 40 | */ 41 | + (id)mj_replacedKeyFromPropertyName121:(NSString *)propertyName; 42 | 43 | /** 44 | * 数组中需要转换的模型类 45 | * 46 | * @return 字典中的key是数组属性名,value是数组中存放模型的Class(Class类型或者NSString类型) 47 | */ 48 | + (NSDictionary *)mj_objectClassInArray; 49 | 50 | /** 51 | * 旧值换新值,用于过滤字典中的值 52 | * 53 | * @param oldValue 旧值 54 | * 55 | * @return 新值 56 | */ 57 | - (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property; 58 | 59 | /** 60 | * 当字典转模型完毕时调用 61 | */ 62 | - (void)mj_keyValuesDidFinishConvertingToObject; 63 | - (void)mj_keyValuesDidFinishConvertingToObject:(NSDictionary *)keyValues; 64 | 65 | /** 66 | * 当模型转字典完毕时调用 67 | */ 68 | - (void)mj_objectDidFinishConvertingToKeyValues; 69 | @end 70 | 71 | @interface NSObject (MJKeyValue) 72 | #pragma mark - 类方法 73 | /** 74 | * 字典转模型过程中遇到的错误 75 | */ 76 | + (NSError *)mj_error; 77 | 78 | /** 79 | * 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) 80 | */ 81 | + (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference; 82 | 83 | #pragma mark - 对象方法 84 | /** 85 | * 将字典的键值对转成模型属性 86 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 87 | */ 88 | - (instancetype)mj_setKeyValues:(id)keyValues; 89 | 90 | /** 91 | * 将字典的键值对转成模型属性 92 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 93 | * @param context CoreData上下文 94 | */ 95 | - (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; 96 | 97 | /** 98 | * 将模型转成字典 99 | * @return 字典 100 | */ 101 | - (NSMutableDictionary *)mj_keyValues; 102 | - (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys; 103 | - (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys; 104 | 105 | /** 106 | * 通过模型数组来创建一个字典数组 107 | * @param objectArray 模型数组 108 | * @return 字典数组 109 | */ 110 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray; 111 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys; 112 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys; 113 | 114 | #pragma mark - 字典转模型 115 | /** 116 | * 通过字典来创建一个模型 117 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 118 | * @return 新建的对象 119 | */ 120 | + (instancetype)mj_objectWithKeyValues:(id)keyValues; 121 | 122 | /** 123 | * 通过字典来创建一个CoreData模型 124 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 125 | * @param context CoreData上下文 126 | * @return 新建的对象 127 | */ 128 | + (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; 129 | 130 | /** 131 | * 通过plist来创建一个模型 132 | * @param filename 文件名(仅限于mainBundle中的文件) 133 | * @return 新建的对象 134 | */ 135 | + (instancetype)mj_objectWithFilename:(NSString *)filename; 136 | 137 | /** 138 | * 通过plist来创建一个模型 139 | * @param file 文件全路径 140 | * @return 新建的对象 141 | */ 142 | + (instancetype)mj_objectWithFile:(NSString *)file; 143 | 144 | #pragma mark - 字典数组转模型数组 145 | /** 146 | * 通过字典数组来创建一个模型数组 147 | * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) 148 | * @return 模型数组 149 | */ 150 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray; 151 | 152 | /** 153 | * 通过字典数组来创建一个模型数组 154 | * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) 155 | * @param context CoreData上下文 156 | * @return 模型数组 157 | */ 158 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context; 159 | 160 | /** 161 | * 通过plist来创建一个模型数组 162 | * @param filename 文件名(仅限于mainBundle中的文件) 163 | * @return 模型数组 164 | */ 165 | + (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename; 166 | 167 | /** 168 | * 通过plist来创建一个模型数组 169 | * @param file 文件全路径 170 | * @return 模型数组 171 | */ 172 | + (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file; 173 | 174 | #pragma mark - 转换为JSON 175 | /** 176 | * 转换为JSON Data 177 | */ 178 | - (NSData *)mj_JSONData; 179 | /** 180 | * 转换为字典或者数组 181 | */ 182 | - (id)mj_JSONObject; 183 | /** 184 | * 转换为JSON 字符串 185 | */ 186 | - (NSString *)mj_JSONString; 187 | @end 188 | 189 | @interface NSObject (MJKeyValueDeprecated_v_2_5_16) 190 | - (instancetype)setKeyValues:(id)keyValue MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 191 | - (instancetype)setKeyValues:(id)keyValues error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 192 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 193 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 194 | + (void)referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 195 | - (NSMutableDictionary *)keyValues MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 196 | - (NSMutableDictionary *)keyValuesWithError:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 197 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 198 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 199 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 200 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 201 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 202 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 203 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 204 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 205 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 206 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 207 | + (instancetype)objectWithKeyValues:(id)keyValues MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 208 | + (instancetype)objectWithKeyValues:(id)keyValues error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 209 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 210 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 211 | + (instancetype)objectWithFilename:(NSString *)filename MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 212 | + (instancetype)objectWithFilename:(NSString *)filename error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 213 | + (instancetype)objectWithFile:(NSString *)file MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 214 | + (instancetype)objectWithFile:(NSString *)file error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 215 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 216 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 217 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 218 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 219 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 220 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 221 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 222 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 223 | - (NSData *)JSONData MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 224 | - (id)JSONObject MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 225 | - (NSString *)JSONString MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 226 | @end 227 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/NSObject+MJProperty.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJProperty.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MJExtensionConst.h" 11 | 12 | @class MJProperty; 13 | 14 | /** 15 | * 遍历成员变量用的block 16 | * 17 | * @param property 成员的包装对象 18 | * @param stop YES代表停止遍历,NO代表继续遍历 19 | */ 20 | typedef void (^MJPropertiesEnumeration)(MJProperty *property, BOOL *stop); 21 | 22 | /** 将属性名换为其他key去字典中取值 */ 23 | typedef NSDictionary * (^MJReplacedKeyFromPropertyName)(void); 24 | typedef id (^MJReplacedKeyFromPropertyName121)(NSString *propertyName); 25 | /** 数组中需要转换的模型类 */ 26 | typedef NSDictionary * (^MJObjectClassInArray)(void); 27 | /** 用于过滤字典中的值 */ 28 | typedef id (^MJNewValueFromOldValue)(id object, id oldValue, MJProperty *property); 29 | 30 | /** 31 | * 成员属性相关的扩展 32 | */ 33 | @interface NSObject (MJProperty) 34 | #pragma mark - 遍历 35 | /** 36 | * 遍历所有的成员 37 | */ 38 | + (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration; 39 | 40 | #pragma mark - 新值配置 41 | /** 42 | * 用于过滤字典中的值 43 | * 44 | * @param newValueFormOldValue 用于过滤字典中的值 45 | */ 46 | + (void)mj_setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue; 47 | + (id)mj_getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property; 48 | 49 | #pragma mark - key配置 50 | /** 51 | * 将属性名换为其他key去字典中取值 52 | * 53 | * @param replacedKeyFromPropertyName 将属性名换为其他key去字典中取值 54 | */ 55 | + (void)mj_setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName; 56 | /** 57 | * 将属性名换为其他key去字典中取值 58 | * 59 | * @param replacedKeyFromPropertyName121 将属性名换为其他key去字典中取值 60 | */ 61 | + (void)mj_setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121; 62 | 63 | #pragma mark - array model class配置 64 | /** 65 | * 数组中需要转换的模型类 66 | * 67 | * @param objectClassInArray 数组中需要转换的模型类 68 | */ 69 | + (void)mj_setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray; 70 | @end 71 | 72 | @interface NSObject (MJPropertyDeprecated_v_2_5_16) 73 | + (void)enumerateProperties:(MJPropertiesEnumeration)enumeration MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 74 | + (void)setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 75 | + (id)getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 76 | + (void)setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 77 | + (void)setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121 MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 78 | + (void)setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 79 | @end 80 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/NSObject+MJProperty.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJProperty.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJProperty.h" 10 | #import "NSObject+MJKeyValue.h" 11 | #import "NSObject+MJCoding.h" 12 | #import "NSObject+MJClass.h" 13 | #import "MJProperty.h" 14 | #import "MJFoundation.h" 15 | #import 16 | 17 | #pragma clang diagnostic push 18 | #pragma clang diagnostic ignored "-Wundeclared-selector" 19 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 20 | 21 | static const char MJReplacedKeyFromPropertyNameKey = '\0'; 22 | static const char MJReplacedKeyFromPropertyName121Key = '\0'; 23 | static const char MJNewValueFromOldValueKey = '\0'; 24 | static const char MJObjectClassInArrayKey = '\0'; 25 | 26 | static const char MJCachedPropertiesKey = '\0'; 27 | 28 | @implementation NSObject (Property) 29 | 30 | + (NSMutableDictionary *)propertyDictForKey:(const void *)key 31 | { 32 | static NSMutableDictionary *replacedKeyFromPropertyNameDict; 33 | static NSMutableDictionary *replacedKeyFromPropertyName121Dict; 34 | static NSMutableDictionary *newValueFromOldValueDict; 35 | static NSMutableDictionary *objectClassInArrayDict; 36 | static NSMutableDictionary *cachedPropertiesDict; 37 | 38 | static dispatch_once_t onceToken; 39 | dispatch_once(&onceToken, ^{ 40 | replacedKeyFromPropertyNameDict = [NSMutableDictionary dictionary]; 41 | replacedKeyFromPropertyName121Dict = [NSMutableDictionary dictionary]; 42 | newValueFromOldValueDict = [NSMutableDictionary dictionary]; 43 | objectClassInArrayDict = [NSMutableDictionary dictionary]; 44 | cachedPropertiesDict = [NSMutableDictionary dictionary]; 45 | }); 46 | 47 | if (key == &MJReplacedKeyFromPropertyNameKey) return replacedKeyFromPropertyNameDict; 48 | if (key == &MJReplacedKeyFromPropertyName121Key) return replacedKeyFromPropertyName121Dict; 49 | if (key == &MJNewValueFromOldValueKey) return newValueFromOldValueDict; 50 | if (key == &MJObjectClassInArrayKey) return objectClassInArrayDict; 51 | if (key == &MJCachedPropertiesKey) return cachedPropertiesDict; 52 | return nil; 53 | } 54 | 55 | #pragma mark - --私有方法-- 56 | + (id)propertyKey:(NSString *)propertyName 57 | { 58 | MJExtensionAssertParamNotNil2(propertyName, nil); 59 | 60 | __block id key = nil; 61 | // 查看有没有需要替换的key 62 | if ([self respondsToSelector:@selector(mj_replacedKeyFromPropertyName121:)]) { 63 | key = [self mj_replacedKeyFromPropertyName121:propertyName]; 64 | } 65 | // 兼容旧版本 66 | if ([self respondsToSelector:@selector(replacedKeyFromPropertyName121:)]) { 67 | key = [self performSelector:@selector(replacedKeyFromPropertyName121) withObject:propertyName]; 68 | } 69 | 70 | // 调用block 71 | if (!key) { 72 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 73 | MJReplacedKeyFromPropertyName121 block = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyName121Key); 74 | if (block) { 75 | key = block(propertyName); 76 | } 77 | if (key) *stop = YES; 78 | }]; 79 | } 80 | 81 | // 查看有没有需要替换的key 82 | if ((!key || [key isEqual:propertyName]) && [self respondsToSelector:@selector(mj_replacedKeyFromPropertyName)]) { 83 | key = [self mj_replacedKeyFromPropertyName][propertyName]; 84 | } 85 | // 兼容旧版本 86 | if ((!key || [key isEqual:propertyName]) && [self respondsToSelector:@selector(replacedKeyFromPropertyName)]) { 87 | key = [self performSelector:@selector(replacedKeyFromPropertyName)][propertyName]; 88 | } 89 | 90 | if (!key || [key isEqual:propertyName]) { 91 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 92 | NSDictionary *dict = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyNameKey); 93 | if (dict) { 94 | key = dict[propertyName]; 95 | } 96 | if (key && ![key isEqual:propertyName]) *stop = YES; 97 | }]; 98 | } 99 | 100 | // 2.用属性名作为key 101 | if (!key) key = propertyName; 102 | 103 | return key; 104 | } 105 | 106 | + (Class)propertyObjectClassInArray:(NSString *)propertyName 107 | { 108 | __block id clazz = nil; 109 | if ([self respondsToSelector:@selector(mj_objectClassInArray)]) { 110 | clazz = [self mj_objectClassInArray][propertyName]; 111 | } 112 | // 兼容旧版本 113 | if ([self respondsToSelector:@selector(objectClassInArray)]) { 114 | clazz = [self performSelector:@selector(objectClassInArray)][propertyName]; 115 | } 116 | 117 | if (!clazz) { 118 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 119 | NSDictionary *dict = objc_getAssociatedObject(c, &MJObjectClassInArrayKey); 120 | if (dict) { 121 | clazz = dict[propertyName]; 122 | } 123 | if (clazz) *stop = YES; 124 | }]; 125 | } 126 | 127 | // 如果是NSString类型 128 | if ([clazz isKindOfClass:[NSString class]]) { 129 | clazz = NSClassFromString(clazz); 130 | } 131 | return clazz; 132 | } 133 | 134 | #pragma mark - --公共方法-- 135 | + (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration 136 | { 137 | // 获得成员变量 138 | NSArray *cachedProperties = [self properties]; 139 | 140 | // 遍历成员变量 141 | BOOL stop = NO; 142 | for (MJProperty *property in cachedProperties) { 143 | enumeration(property, &stop); 144 | if (stop) break; 145 | } 146 | } 147 | 148 | #pragma mark - 公共方法 149 | + (NSMutableArray *)properties 150 | { 151 | NSMutableArray *cachedProperties = [self propertyDictForKey:&MJCachedPropertiesKey][NSStringFromClass(self)]; 152 | 153 | if (cachedProperties == nil) { 154 | MJExtensionSemaphoreCreate 155 | MJExtensionSemaphoreWait 156 | 157 | if (cachedProperties == nil) { 158 | cachedProperties = [NSMutableArray array]; 159 | 160 | [self mj_enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) { 161 | // 1.获得所有的成员变量 162 | unsigned int outCount = 0; 163 | objc_property_t *properties = class_copyPropertyList(c, &outCount); 164 | 165 | // 2.遍历每一个成员变量 166 | for (unsigned int i = 0; i 10 | #import "MJExtensionConst.h" 11 | 12 | @interface NSString (MJExtension) 13 | /** 14 | * 驼峰转下划线(loveYou -> love_you) 15 | */ 16 | - (NSString *)mj_underlineFromCamel; 17 | /** 18 | * 下划线转驼峰(love_you -> loveYou) 19 | */ 20 | - (NSString *)mj_camelFromUnderline; 21 | /** 22 | * 首字母变大写 23 | */ 24 | - (NSString *)mj_firstCharUpper; 25 | /** 26 | * 首字母变小写 27 | */ 28 | - (NSString *)mj_firstCharLower; 29 | 30 | - (BOOL)mj_isPureInt; 31 | 32 | - (NSURL *)mj_url; 33 | @end 34 | 35 | @interface NSString (MJExtensionDeprecated_v_2_5_16) 36 | - (NSString *)underlineFromCamel MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 37 | - (NSString *)camelFromUnderline MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 38 | - (NSString *)firstCharUpper MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 39 | - (NSString *)firstCharLower MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 40 | - (BOOL)isPureInt MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 41 | - (NSURL *)url MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 42 | @end 43 | -------------------------------------------------------------------------------- /ios/Classes/MJExtension/NSString+MJExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+MJExtension.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/6/7. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSString+MJExtension.h" 10 | 11 | @implementation NSString (MJExtension) 12 | - (NSString *)mj_underlineFromCamel 13 | { 14 | if (self.length == 0) return self; 15 | NSMutableString *string = [NSMutableString string]; 16 | for (NSUInteger i = 0; i= 2) [string appendString:[cmp substringFromIndex:1]]; 40 | } else { 41 | [string appendString:cmp]; 42 | } 43 | } 44 | return string; 45 | } 46 | 47 | - (NSString *)mj_firstCharLower 48 | { 49 | if (self.length == 0) return self; 50 | NSMutableString *string = [NSMutableString string]; 51 | [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].lowercaseString]; 52 | if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; 53 | return string; 54 | } 55 | 56 | - (NSString *)mj_firstCharUpper 57 | { 58 | if (self.length == 0) return self; 59 | NSMutableString *string = [NSMutableString string]; 60 | [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].uppercaseString]; 61 | if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; 62 | return string; 63 | } 64 | 65 | - (BOOL)mj_isPureInt 66 | { 67 | NSScanner *scan = [NSScanner scannerWithString:self]; 68 | int val; 69 | return [scan scanInt:&val] && [scan isAtEnd]; 70 | } 71 | 72 | - (NSURL *)mj_url 73 | { 74 | // [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!$&'()*+,-./:;=?@_~%#[]"]]; 75 | #pragma clang diagnostic push 76 | #pragma clang diagnostic ignored"-Wdeprecated-declarations" 77 | return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))]; 78 | #pragma clang diagnostic pop 79 | } 80 | @end 81 | 82 | @implementation NSString (MJExtensionDeprecated_v_2_5_16) 83 | - (NSString *)underlineFromCamel 84 | { 85 | return self.mj_underlineFromCamel; 86 | } 87 | 88 | - (NSString *)camelFromUnderline 89 | { 90 | return self.mj_camelFromUnderline; 91 | } 92 | 93 | - (NSString *)firstCharLower 94 | { 95 | return self.mj_firstCharLower; 96 | } 97 | 98 | - (NSString *)firstCharUpper 99 | { 100 | return self.mj_firstCharUpper; 101 | } 102 | 103 | - (BOOL)isPureInt 104 | { 105 | return self.mj_isPureInt; 106 | } 107 | 108 | - (NSURL *)url 109 | { 110 | return self.mj_url; 111 | } 112 | @end 113 | -------------------------------------------------------------------------------- /ios/dim.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 3 | # 4 | Pod::Spec.new do |s| 5 | s.name = 'dim' 6 | s.version = '0.2.7' 7 | s.summary = 'TIMSDK for flutter' 8 | s.description = <<-DESC 9 | A new flutter plugin project. 10 | DESC 11 | s.homepage = 'https://gitee.com/gameOverFlow/dim' 12 | s.license = { :file => '../LICENSE' } 13 | s.author = { 'brzhang' => '1595819400@qq.com' } 14 | s.source = { :path => '.' } 15 | s.source_files = 'Classes/**/*' 16 | s.public_header_files = 'Classes/**/*.h' 17 | s.dependency 'Flutter' 18 | s.dependency 'YYModel' 19 | s.dependency 'TXIMSDK_iOS' 20 | s.ios.deployment_target = '8.0' 21 | end 22 | 23 | -------------------------------------------------------------------------------- /lib/dim.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:flutter/services.dart'; 3 | 4 | class Dim { 5 | factory Dim() { 6 | if (_instance == null) { 7 | final MethodChannel methodChannel = const MethodChannel('dim_method'); 8 | final EventChannel eventChannel = const EventChannel('dim_event'); 9 | _instance = new Dim.private(methodChannel, eventChannel); 10 | } 11 | return _instance; 12 | } 13 | 14 | Dim.private(this._methodChannel, this._eventChannel); 15 | 16 | final MethodChannel _methodChannel; 17 | 18 | final EventChannel _eventChannel; 19 | 20 | Future get platformVersion async { 21 | final String version = 22 | await _methodChannel.invokeMethod('getPlatformVersion'); 23 | return version; 24 | } 25 | 26 | static Dim _instance; 27 | 28 | Stream _listener; 29 | 30 | Stream get onMessage { 31 | if (_listener == null) { 32 | _listener = _eventChannel 33 | .receiveBroadcastStream() 34 | .map((dynamic event) => _parseBatteryState(event)); 35 | } 36 | return _listener; 37 | } 38 | 39 | ///im初始化 40 | Future init(int appid) async { 41 | return await _methodChannel.invokeMethod("init", { 42 | 'appid': appid, 43 | }); 44 | } 45 | 46 | ///im登录 47 | Future imLogin(String identifier, String sig) async { 48 | return await _methodChannel.invokeMethod("im_login", { 49 | 'identifier': identifier, 50 | 'userSig': sig, 51 | }); 52 | } 53 | 54 | ///im登出 55 | Future imLogout() async { 56 | return await _methodChannel.invokeMethod("im_logout"); 57 | } 58 | 59 | ///获取会话列表 60 | Future getConversations() async { 61 | return await _methodChannel.invokeMethod('getConversations'); 62 | } 63 | 64 | ///删除会话 65 | Future delConversation(String identifier) async { 66 | return await _methodChannel.invokeMethod( 67 | 'delConversation', {'identifier': identifier}); 68 | } 69 | 70 | ///获取一个会话的消息,暂不支持流式查询 71 | ///identifier 会话id 72 | ///count 获取消息数量 ,默认50条 73 | ///ctype 1 私信,2群聊 ,默认是私信 74 | Future getMessages(String identifier, 75 | [int count = 50, int ctype = 1]) async { 76 | return await _methodChannel.invokeMethod('getMessages', { 77 | 'identifier': identifier, 78 | 'count': count, 79 | 'ctype': ctype 80 | }); 81 | } 82 | 83 | ///发送文本消息 84 | Future sendTextMessages(String identifier, String content) async { 85 | return await _methodChannel.invokeMethod('sendTextMessages', 86 | {'identifier': identifier, 'content': content}); 87 | } 88 | 89 | ///发送图片消息 90 | ///imagePath eg for android : Environment.getExternalStorageDirectory() + "/DCIM/Camera/1.jpg" 91 | Future sendImageMessages(String identifier, String imagePath) async { 92 | return await _methodChannel.invokeMethod('sendImageMessages', 93 | {'identifier': identifier, 'image_path': imagePath}); 94 | } 95 | 96 | ///发送语音消息 97 | ///soundPath eg for android : Environment.getExternalStorageDirectory() + "/sound.mp3" 98 | Future sendSoundMessages(String identifier, String soundPath, 99 | [int duration = 10]) async { 100 | return await _methodChannel.invokeMethod( 101 | 'sendSoundMessages', { 102 | 'identifier': identifier, 103 | 'sound_path': soundPath, 104 | 'duration': duration 105 | }); 106 | } 107 | 108 | ///发送位置消息 109 | ///eg: 110 | ///lat 113.93 111 | ///lng 22.54 112 | ///desc 腾讯大厦 113 | Future sendLocationMessages( 114 | String identifier, double lat, double lng, String desc) async { 115 | return await _methodChannel.invokeMethod('sendLocation', { 116 | 'identifier': identifier, 117 | 'lat': lat, 118 | 'lng': lng, 119 | 'desc': desc, 120 | }); 121 | } 122 | 123 | ///添加好友 124 | /// 125 | Future addFriend(String identifier) async { 126 | return await _methodChannel 127 | .invokeMethod("addFriend", {'identifier': identifier}); 128 | } 129 | 130 | ///删除好友 131 | /// 132 | Future delFriend(String identifier) async { 133 | return await _methodChannel 134 | .invokeMethod("delFriend", {'identifier': identifier}); 135 | } 136 | 137 | ///获取好友列表 138 | /// 139 | Future listFriends(String identifier) async { 140 | return await _methodChannel.invokeMethod( 141 | "listFriends", {'identifier': identifier}); 142 | } 143 | 144 | ///处理好友的请求,接受/拒绝 145 | ///opTypeStr 接受传 Y 146 | ///opTypeStr 拒绝传 N 147 | Future opFriend(String identifier, String opTypeStr) async { 148 | return await _methodChannel.invokeMethod("opFriend", 149 | {'identifier': identifier, 'opTypeStr': opTypeStr}); 150 | } 151 | 152 | ///获取用户资料 153 | ///param user is a list ["usersf1","jiofoea2"] 154 | Future getUsersProfile(List users) async { 155 | return await _methodChannel 156 | .invokeMethod("getUsersProfile", {'users': users}); 157 | } 158 | 159 | ///设置个人资料 160 | Future setUsersProfile( 161 | int gender, String nick, String faceUrl) async { 162 | return await _methodChannel.invokeMethod("setUsersProfile", 163 | {'gender': gender, 'nick': nick, 'faceUrl': faceUrl}); 164 | } 165 | 166 | ///测试使用eventChannel推送数据过来 167 | Future postDataTest() async { 168 | return await _methodChannel.invokeMethod("post_data_test"); 169 | } 170 | 171 | dynamic _parseBatteryState(event) { 172 | return event; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /lib/manger.dart: -------------------------------------------------------------------------------- 1 | class Manger { 2 | static final Manger _singleton = new Manger._internal(); 3 | 4 | factory Manger() { 5 | return _singleton; 6 | } 7 | 8 | Manger._internal() { 9 | // initialization logic here 10 | } 11 | // rest of the class 12 | } 13 | 14 | abstract class MessageListener { 15 | void makePeopleLaugh(); 16 | } 17 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dim 2 | description: Convenient integration tencent imsdk, developers can easily use imsdk on flutter.reading READEME, then you can enjoy imsdk on flutter。 3 | version: 0.2.7 4 | author: brzhang <1595819400@qq.com> 5 | homepage: https://github.com/bravekingzhang/dim 6 | 7 | dependencies: 8 | flutter: 9 | sdk: flutter 10 | 11 | # For information on the generic Dart part of this file, see the 12 | # following page: https://www.dartlang.org/tools/pub/pubspec 13 | 14 | # The following section is specific to Flutter. 15 | flutter: 16 | plugin: 17 | androidPackage: com.brzhang.flutter.dim 18 | pluginClass: DimPlugin 19 | 20 | # To add assets to your plugin package, add an assets section, like this: 21 | # assets: 22 | # - images/a_dot_burr.jpeg 23 | # - images/a_dot_ham.jpeg 24 | # 25 | # For details regarding assets in packages, see 26 | # https://flutter.io/assets-and-images/#from-packages 27 | # 28 | # An image asset can refer to one or more resolution-specific "variants", see 29 | # https://flutter.io/assets-and-images/#resolution-aware. 30 | 31 | # To add custom fonts to your plugin package, add a fonts section here, 32 | # in this "flutter" section. Each entry in this list should have a 33 | # "family" key with the font family name, and a "fonts" key with a 34 | # list giving the asset and other descriptors for the font. For 35 | # example: 36 | # fonts: 37 | # - family: Schyler 38 | # fonts: 39 | # - asset: fonts/Schyler-Regular.ttf 40 | # - asset: fonts/Schyler-Italic.ttf 41 | # style: italic 42 | # - family: Trajan Pro 43 | # fonts: 44 | # - asset: fonts/TrajanPro.ttf 45 | # - asset: fonts/TrajanPro_Bold.ttf 46 | # weight: 700 47 | # 48 | # For details regarding fonts in packages, see 49 | # https://flutter.io/custom-fonts/#from-packages 50 | environment: 51 | sdk: ">=2.1.0 <3.0.0" 52 | --------------------------------------------------------------------------------