基础页面
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB |
@@ -1,5 +1,5 @@
|
|||||||
# Uncomment this line to define a global platform for your project
|
# Uncomment this line to define a global platform for your project
|
||||||
# platform :ios, '9.0'
|
platform :ios, '9.0'
|
||||||
|
|
||||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|||||||
108
ios/Podfile.lock
Normal file
108
ios/Podfile.lock
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
PODS:
|
||||||
|
- Flutter (1.0.0)
|
||||||
|
- fluttertoast (0.0.2):
|
||||||
|
- Flutter
|
||||||
|
- Toast
|
||||||
|
- FMDB (2.7.5):
|
||||||
|
- FMDB/standard (= 2.7.5)
|
||||||
|
- FMDB/standard (2.7.5)
|
||||||
|
- HydraAsync (2.0.6)
|
||||||
|
- image_cropper (0.0.4):
|
||||||
|
- Flutter
|
||||||
|
- TOCropViewController (~> 2.6.1)
|
||||||
|
- open_file (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- path_provider_ios (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- photo_manager (2.0.0):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
|
- scan (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- smart_auth (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- sqflite (0.0.2):
|
||||||
|
- Flutter
|
||||||
|
- FMDB (>= 2.7.5)
|
||||||
|
- tencent_im_sdk_plugin (4.0.3):
|
||||||
|
- Flutter
|
||||||
|
- HydraAsync
|
||||||
|
- TXIMSDK_Plus_iOS (= 6.7.3184)
|
||||||
|
- Toast (4.0.0)
|
||||||
|
- TOCropViewController (2.6.1)
|
||||||
|
- TXIMSDK_Plus_iOS (6.7.3184)
|
||||||
|
- vibration (1.7.5):
|
||||||
|
- Flutter
|
||||||
|
- video_player_avfoundation (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
|
||||||
|
DEPENDENCIES:
|
||||||
|
- Flutter (from `Flutter`)
|
||||||
|
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||||
|
- image_cropper (from `.symlinks/plugins/image_cropper/ios`)
|
||||||
|
- open_file (from `.symlinks/plugins/open_file/ios`)
|
||||||
|
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
|
||||||
|
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
||||||
|
- scan (from `.symlinks/plugins/scan/ios`)
|
||||||
|
- smart_auth (from `.symlinks/plugins/smart_auth/ios`)
|
||||||
|
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||||
|
- tencent_im_sdk_plugin (from `.symlinks/plugins/tencent_im_sdk_plugin/ios`)
|
||||||
|
- vibration (from `.symlinks/plugins/vibration/ios`)
|
||||||
|
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`)
|
||||||
|
|
||||||
|
SPEC REPOS:
|
||||||
|
trunk:
|
||||||
|
- FMDB
|
||||||
|
- HydraAsync
|
||||||
|
- Toast
|
||||||
|
- TOCropViewController
|
||||||
|
- TXIMSDK_Plus_iOS
|
||||||
|
|
||||||
|
EXTERNAL SOURCES:
|
||||||
|
Flutter:
|
||||||
|
:path: Flutter
|
||||||
|
fluttertoast:
|
||||||
|
:path: ".symlinks/plugins/fluttertoast/ios"
|
||||||
|
image_cropper:
|
||||||
|
:path: ".symlinks/plugins/image_cropper/ios"
|
||||||
|
open_file:
|
||||||
|
:path: ".symlinks/plugins/open_file/ios"
|
||||||
|
path_provider_ios:
|
||||||
|
:path: ".symlinks/plugins/path_provider_ios/ios"
|
||||||
|
photo_manager:
|
||||||
|
:path: ".symlinks/plugins/photo_manager/ios"
|
||||||
|
scan:
|
||||||
|
:path: ".symlinks/plugins/scan/ios"
|
||||||
|
smart_auth:
|
||||||
|
:path: ".symlinks/plugins/smart_auth/ios"
|
||||||
|
sqflite:
|
||||||
|
:path: ".symlinks/plugins/sqflite/ios"
|
||||||
|
tencent_im_sdk_plugin:
|
||||||
|
:path: ".symlinks/plugins/tencent_im_sdk_plugin/ios"
|
||||||
|
vibration:
|
||||||
|
:path: ".symlinks/plugins/vibration/ios"
|
||||||
|
video_player_avfoundation:
|
||||||
|
:path: ".symlinks/plugins/video_player_avfoundation/ios"
|
||||||
|
|
||||||
|
SPEC CHECKSUMS:
|
||||||
|
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
|
||||||
|
fluttertoast: 74526702fea2c060ea55dde75895b7e1bde1c86b
|
||||||
|
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||||
|
HydraAsync: 8d589bd725b0224f899afafc9a396327405f8063
|
||||||
|
image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98
|
||||||
|
open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d
|
||||||
|
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
|
||||||
|
photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604
|
||||||
|
scan: aea35bb4aa59ccc8839c576a18cd57c7d492cc86
|
||||||
|
smart_auth: 4bedbc118723912d0e45a07e8ab34039c19e04f2
|
||||||
|
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||||
|
tencent_im_sdk_plugin: 26c668a5d2f456a5541e2c820dcfcb3d15fdba9a
|
||||||
|
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||||
|
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
|
||||||
|
TXIMSDK_Plus_iOS: 5412f55a77f058b2b5a8575900334daccbae3b08
|
||||||
|
vibration: 7d883d141656a1c1a6d8d238616b2042a51a1241
|
||||||
|
video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff
|
||||||
|
|
||||||
|
PODFILE CHECKSUM: a75497545d4391e2d394c3668e20cfb1c2bbd4aa
|
||||||
|
|
||||||
|
COCOAPODS: 1.11.3
|
||||||
@@ -3,12 +3,13 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 50;
|
objectVersion = 51;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
|
70F84DE20E16DC49868EF51A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7CDE8CA2C88702AA59A75259 /* Pods_Runner.framework */; };
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
@@ -32,9 +33,12 @@
|
|||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
3F9F50CF9C873D1460CCEE80 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
5F1992123E9973AEE97006AF /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
7CDE8CA2C88702AA59A75259 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@@ -42,6 +46,7 @@
|
|||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
E29514A41E605422DF139C6D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -49,6 +54,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
70F84DE20E16DC49868EF51A /* Pods_Runner.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -72,6 +78,8 @@
|
|||||||
9740EEB11CF90186004384FC /* Flutter */,
|
9740EEB11CF90186004384FC /* Flutter */,
|
||||||
97C146F01CF9000F007C117D /* Runner */,
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
97C146EF1CF9000F007C117D /* Products */,
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
|
C394D966EF1617064336C312 /* Pods */,
|
||||||
|
C37FF88E875204B875862A7F /* Frameworks */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -98,6 +106,24 @@
|
|||||||
path = Runner;
|
path = Runner;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C37FF88E875204B875862A7F /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
7CDE8CA2C88702AA59A75259 /* Pods_Runner.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C394D966EF1617064336C312 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5F1992123E9973AEE97006AF /* Pods-Runner.debug.xcconfig */,
|
||||||
|
3F9F50CF9C873D1460CCEE80 /* Pods-Runner.release.xcconfig */,
|
||||||
|
E29514A41E605422DF139C6D /* Pods-Runner.profile.xcconfig */,
|
||||||
|
);
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@@ -105,12 +131,14 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
70B6D40A25B40589C6B223C0 /* [CP] Check Pods Manifest.lock */,
|
||||||
9740EEB61CF901F6004384FC /* Run Script */,
|
9740EEB61CF901F6004384FC /* Run Script */,
|
||||||
97C146EA1CF9000F007C117D /* Sources */,
|
97C146EA1CF9000F007C117D /* Sources */,
|
||||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
97C146EC1CF9000F007C117D /* Resources */,
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
2EC1B54F88AAEC33384CE737 /* [CP] Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -169,6 +197,23 @@
|
|||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
2EC1B54F88AAEC33384CE737 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -183,6 +228,28 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||||
};
|
};
|
||||||
|
70B6D40A25B40589C6B223C0 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -287,13 +354,14 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
MARKETING_VERSION = 1.0.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = site.zhchain.chat;
|
PRODUCT_BUNDLE_IDENTIFIER = site.zhchain.chat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@@ -415,13 +483,14 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
MARKETING_VERSION = 1.0.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = site.zhchain.chat;
|
PRODUCT_BUNDLE_IDENTIFIER = site.zhchain.chat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@@ -437,13 +506,14 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
MARKETING_VERSION = 1.0.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = site.zhchain.chat;
|
PRODUCT_BUNDLE_IDENTIFIER = site.zhchain.chat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
|||||||
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
@@ -4,4 +4,7 @@
|
|||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "group:Runner.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>Chat</string>
|
<string>ZH-Chat</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
@@ -13,15 +13,15 @@
|
|||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>chat</string>
|
<string>zhchat</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
<string>1.0.0</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
<string>1</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class Themes {
|
|||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
),
|
),
|
||||||
unselectedLabelStyle: TextStyle(
|
unselectedLabelStyle: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 11,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
228
lib/controllers/group_controller.dart
Normal file
228
lib/controllers/group_controller.dart
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
|
import 'package:chat/models/im/group_conversation_model.dart';
|
||||||
|
import 'package:chat/services/auth_service.dart';
|
||||||
|
import 'package:chat/services/tim/conversation_service.dart';
|
||||||
|
import 'package:chat/services/tim/group_service.dart';
|
||||||
|
import 'package:chat/services/tim_service.dart';
|
||||||
|
import 'package:chat/utils/ui_tools.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/group_member_filter_enum.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/group_member_role.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/group_member_role_enum.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_member_full_info.dart';
|
||||||
|
|
||||||
|
class GroupController extends GetxController {
|
||||||
|
static GroupController get to => Get.find<GroupController>();
|
||||||
|
|
||||||
|
Rx<GroupConversationModel> currentGroup =
|
||||||
|
GroupConversationModel(groupID: '').obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
currentGroup.value = GroupConversationModel(groupID: '');
|
||||||
|
TimService.to.currentConversationId.value = '';
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置当前操作的群组
|
||||||
|
Future<void> setCurrentGroup(String groupId) async {
|
||||||
|
var group = await TimGroupService.to.info(groupId);
|
||||||
|
|
||||||
|
if (group != null) {
|
||||||
|
TimService.to.currentConversationId.value = 'group_' + groupId;
|
||||||
|
|
||||||
|
currentGroup.value.group = group;
|
||||||
|
|
||||||
|
currentGroup.value.groupID = groupId;
|
||||||
|
|
||||||
|
var selfInfo = await TimGroupService.to.getMemberInfo(
|
||||||
|
group,
|
||||||
|
AuthService.to.userId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selfInfo?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN) {
|
||||||
|
currentGroup.value.isAdmin = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selfInfo?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER) {
|
||||||
|
currentGroup.value.isOwner = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentGroup.value.selfInfo = await TimGroupService.to.getMemberInfo(
|
||||||
|
group,
|
||||||
|
AuthService.to.userId,
|
||||||
|
);
|
||||||
|
|
||||||
|
currentGroup.value.conversation = await TimConversationService.to.getById(
|
||||||
|
'group_' + groupId,
|
||||||
|
);
|
||||||
|
|
||||||
|
currentGroup.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取群成员列表
|
||||||
|
Future<void> fetchGroupMemberList() async {
|
||||||
|
var members = await TimGroupService.to.members(
|
||||||
|
currentGroup.value.groupID,
|
||||||
|
count: 13,
|
||||||
|
);
|
||||||
|
|
||||||
|
currentGroup.value.memberList = members;
|
||||||
|
|
||||||
|
var admins = await TimGroupService.to.members(
|
||||||
|
currentGroup.value.groupID,
|
||||||
|
count: 100,
|
||||||
|
filter: GroupMemberFilterTypeEnum.V2TIM_GROUP_MEMBER_FILTER_ADMIN,
|
||||||
|
);
|
||||||
|
|
||||||
|
currentGroup.value.adminList = admins;
|
||||||
|
currentGroup.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 更新群名称
|
||||||
|
Future<void> updateGroupName(String name) async {
|
||||||
|
var result = await TimGroupService.to.updateName(
|
||||||
|
currentGroup.value.group!,
|
||||||
|
name,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
currentGroup.value.group!.groupName = name;
|
||||||
|
currentGroup.value.conversation!.showName = name;
|
||||||
|
currentGroup.refresh();
|
||||||
|
UiTools.toast('群名称修改成功');
|
||||||
|
Get.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 更新我的群名片
|
||||||
|
Future<void> updateGroupNameCard(String nameCard) async {
|
||||||
|
var res = await TimGroupService.to.setMemberInfo(
|
||||||
|
currentGroup.value.group!,
|
||||||
|
AuthService.to.userId,
|
||||||
|
nameCard,
|
||||||
|
);
|
||||||
|
if (res) {
|
||||||
|
currentGroup.value.selfInfo!.nameCard = nameCard;
|
||||||
|
currentGroup.refresh();
|
||||||
|
|
||||||
|
UiTools.toast('群名片修改成功');
|
||||||
|
Get.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateGroupNotification(String notification) async {
|
||||||
|
var res = await TimGroupService.to.updateNotification(
|
||||||
|
currentGroup.value.group!,
|
||||||
|
notification,
|
||||||
|
);
|
||||||
|
if (res) {
|
||||||
|
currentGroup.value.group!.notification = notification;
|
||||||
|
currentGroup.refresh();
|
||||||
|
|
||||||
|
UiTools.toast('群公告更新成功');
|
||||||
|
Get.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> togglePinned() async {
|
||||||
|
var res = await TimConversationService.to.setOnTop(
|
||||||
|
currentGroup.value.conversation!,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
currentGroup.value.conversation = await TimConversationService.to.getById(
|
||||||
|
'group_' + currentGroup.value.groupID,
|
||||||
|
);
|
||||||
|
|
||||||
|
currentGroup.refresh();
|
||||||
|
UiTools.toast('修改成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> toggleReceiveOpt() async {
|
||||||
|
var res = await TimConversationService.to.setReceiveOpt(
|
||||||
|
currentGroup.value.conversation!,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
currentGroup.value.conversation = await TimConversationService.to.getById(
|
||||||
|
'group_' + currentGroup.value.groupID,
|
||||||
|
);
|
||||||
|
currentGroup.refresh();
|
||||||
|
|
||||||
|
UiTools.toast('修改成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 移除群成员
|
||||||
|
Future<bool> kick(List<String> ids) async {
|
||||||
|
var result = await TimGroupService.to.kickMember(
|
||||||
|
GroupController.to.currentGroup.value.group!,
|
||||||
|
ids,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
setCurrentGroup(currentGroup.value.groupID);
|
||||||
|
fetchGroupMemberList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> transfer(V2TimGroupMemberFullInfo member) async {
|
||||||
|
OkCancelResult result = await showOkCancelAlertDialog(
|
||||||
|
style: AdaptiveStyle.iOS,
|
||||||
|
context: Get.context!,
|
||||||
|
title: '操作提示',
|
||||||
|
message: '确定选择 ${member.nickName} 为新群主,您将自动放弃群主身份。',
|
||||||
|
okLabel: '确定',
|
||||||
|
cancelLabel: '取消',
|
||||||
|
defaultType: OkCancelAlertDefaultType.ok,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == OkCancelResult.ok) {
|
||||||
|
var res = await TimGroupService.to.transfer(
|
||||||
|
currentGroup.value.group!,
|
||||||
|
member.userID,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
/// 直接修改当前用户的身份,为普通用户
|
||||||
|
currentGroup.value.isAdmin = false;
|
||||||
|
currentGroup.value.isOwner = false;
|
||||||
|
await fetchGroupMemberList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> setAdmin(String userID) async {
|
||||||
|
var result = await TimGroupService.to.setMemberRole(
|
||||||
|
currentGroup.value.group!,
|
||||||
|
userID,
|
||||||
|
GroupMemberRoleTypeEnum.V2TIM_GROUP_MEMBER_ROLE_ADMIN,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
UiTools.toast('设置群管理成功');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> cancelAdmin(String userID) async {
|
||||||
|
var result = await TimGroupService.to.setMemberRole(
|
||||||
|
currentGroup.value.group!,
|
||||||
|
userID,
|
||||||
|
GroupMemberRoleTypeEnum.V2TIM_GROUP_MEMBER_ROLE_MEMBER,
|
||||||
|
);
|
||||||
|
if (result) {
|
||||||
|
UiTools.toast('取消群管理成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
111
lib/controllers/private_controller.dart
Normal file
111
lib/controllers/private_controller.dart
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import 'package:chat/models/im/private_conversation_model.dart';
|
||||||
|
import 'package:chat/services/tim/conversation_service.dart';
|
||||||
|
import 'package:chat/services/tim/friend_service.dart';
|
||||||
|
import 'package:chat/services/tim_service.dart';
|
||||||
|
import 'package:chat/utils/ui_tools.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_info_result.dart';
|
||||||
|
|
||||||
|
class PrivateController extends GetxController {
|
||||||
|
static PrivateController get to => Get.find<PrivateController>();
|
||||||
|
|
||||||
|
Rx<PrivateConversationModel> currentFriend =
|
||||||
|
PrivateConversationModel(userID: '').obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
currentFriend.value = PrivateConversationModel(userID: '');
|
||||||
|
TimService.to.currentConversationId.value = '';
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置当前好友
|
||||||
|
Future<void> setCurrentFriend(String userID) async {
|
||||||
|
V2TimFriendInfoResult? info = await TimFriendService.to.friendInfo(userID);
|
||||||
|
|
||||||
|
if (info != null) {
|
||||||
|
TimService.to.currentConversationId.value = 'c2c_' + userID;
|
||||||
|
|
||||||
|
currentFriend.value.userID = userID;
|
||||||
|
currentFriend.value.isFriend =
|
||||||
|
info.relation == UserRelationEnum.V2TIM_FRIEND_RELATION_TYPE_BOTH_WAY;
|
||||||
|
|
||||||
|
currentFriend.value.friendRemark = info.friendInfo!.friendRemark!;
|
||||||
|
currentFriend.value.userProfile = info.friendInfo!.userProfile;
|
||||||
|
|
||||||
|
/// 通过自定义Staffer字段,判断是否是客服,属于哪个店铺
|
||||||
|
if (info.friendInfo!.userProfile?.customInfo!['Staffer']?.isNotEmpty ==
|
||||||
|
true) {
|
||||||
|
currentFriend.value.shopId =
|
||||||
|
info.friendInfo!.userProfile?.customInfo!['Staffer'];
|
||||||
|
currentFriend.value.isStaffer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 通过用户自定义字段,判断是否允许陌生人消息
|
||||||
|
if (info.friendInfo!.userProfile?.customInfo!['Stranger']?.isNotEmpty ==
|
||||||
|
true) {
|
||||||
|
currentFriend.value.allowStranger = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置会话
|
||||||
|
currentFriend.value.conversation =
|
||||||
|
await TimConversationService.to.getById('c2c_' + userID);
|
||||||
|
|
||||||
|
currentFriend.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> togglePinned() async {
|
||||||
|
var res = await TimConversationService.to.setOnTop(
|
||||||
|
currentFriend.value.conversation!,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
currentFriend.value.conversation =
|
||||||
|
await TimConversationService.to.getById(
|
||||||
|
'c2c_' + currentFriend.value.userID,
|
||||||
|
);
|
||||||
|
|
||||||
|
currentFriend.refresh();
|
||||||
|
UiTools.toast('修改成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> changeReceiveOpt() async {
|
||||||
|
var res = await TimConversationService.to.setReceiveOpt(
|
||||||
|
currentFriend.value.conversation!,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
currentFriend.value.conversation =
|
||||||
|
await TimConversationService.to.getById(
|
||||||
|
'c2c_' + currentFriend.value.userID,
|
||||||
|
);
|
||||||
|
currentFriend.refresh();
|
||||||
|
|
||||||
|
UiTools.toast('修改成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setRemark(String remark) async {
|
||||||
|
var result = await TimFriendService.to.setFriendRemark(
|
||||||
|
currentFriend.value.userID,
|
||||||
|
remark,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
currentFriend.value.friendRemark = remark;
|
||||||
|
|
||||||
|
currentFriend.value.conversation =
|
||||||
|
await TimConversationService.to.getById(
|
||||||
|
'c2c_' + currentFriend.value.userID,
|
||||||
|
);
|
||||||
|
|
||||||
|
currentFriend.refresh();
|
||||||
|
|
||||||
|
TimConversationService.to.fetchList();
|
||||||
|
UiTools.toast('备注修改成功');
|
||||||
|
Get.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
121
lib/models/im/calling_model.dart
Normal file
121
lib/models/im/calling_model.dart
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:chat/models/im/custom_message_model.dart';
|
||||||
|
|
||||||
|
class CallingModel extends CustomMessageModel {
|
||||||
|
CallingModel({
|
||||||
|
this.businessID = CustomMessageType.CALL,
|
||||||
|
required this.callType,
|
||||||
|
required this.inviter,
|
||||||
|
required this.inviteeList,
|
||||||
|
required this.data,
|
||||||
|
required this.timeout,
|
||||||
|
required this.actionType,
|
||||||
|
required this.onlineUserOnly,
|
||||||
|
required this.isGroup,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String businessID;
|
||||||
|
|
||||||
|
/// 通话类型 videoCall audioCall
|
||||||
|
String callType;
|
||||||
|
|
||||||
|
/// 邀请人
|
||||||
|
String inviter;
|
||||||
|
|
||||||
|
/// 被邀请人
|
||||||
|
List<String> inviteeList;
|
||||||
|
|
||||||
|
/// 通话时长
|
||||||
|
int timeout;
|
||||||
|
// 1: 邀请方发起邀请
|
||||||
|
// 2: 邀请方取消邀请
|
||||||
|
// 3: 被邀请方接受邀请
|
||||||
|
// 4: 被邀请方拒绝邀请
|
||||||
|
// 5: 邀请超时
|
||||||
|
int actionType;
|
||||||
|
|
||||||
|
bool onlineUserOnly;
|
||||||
|
|
||||||
|
/// 是否是群语音
|
||||||
|
bool isGroup;
|
||||||
|
CallingModelData data;
|
||||||
|
|
||||||
|
String get actionTypeText {
|
||||||
|
final actionMessage = {
|
||||||
|
1: "发起通话",
|
||||||
|
2: "取消通话",
|
||||||
|
3: "接受通话",
|
||||||
|
4: "拒绝通话",
|
||||||
|
5: "超时未接听",
|
||||||
|
};
|
||||||
|
return actionMessage[actionType] ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
factory CallingModel.fromJson(Map<String, dynamic> json) => CallingModel(
|
||||||
|
callType: jsonDecode(json['data'])['data']['cmd'],
|
||||||
|
inviter: json['inviter'],
|
||||||
|
inviteeList: List<String>.from(
|
||||||
|
json['inviteeList'].map(
|
||||||
|
(x) => x.toString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
data: CallingModelData.fromJson(jsonDecode(json['data'])),
|
||||||
|
timeout: json['timeout'],
|
||||||
|
actionType: json['actionType'],
|
||||||
|
onlineUserOnly: json['onlineUserOnly'],
|
||||||
|
isGroup: jsonDecode(json['data'])['is_group'],
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toJson() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallingModelData {
|
||||||
|
CallingModelData({
|
||||||
|
required this.version,
|
||||||
|
required this.callType,
|
||||||
|
required this.data,
|
||||||
|
required this.roomId,
|
||||||
|
required this.isGroup,
|
||||||
|
});
|
||||||
|
|
||||||
|
int version;
|
||||||
|
int callType;
|
||||||
|
DataData data;
|
||||||
|
int roomId;
|
||||||
|
bool isGroup;
|
||||||
|
|
||||||
|
factory CallingModelData.fromJson(Map<String, dynamic> json) =>
|
||||||
|
CallingModelData(
|
||||||
|
version: json['version'],
|
||||||
|
callType: json['call_type'],
|
||||||
|
data: DataData.fromJson(json['data']),
|
||||||
|
roomId: json['room_id'],
|
||||||
|
isGroup: json['is_group'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataData {
|
||||||
|
DataData({
|
||||||
|
required this.cmd,
|
||||||
|
required this.roomId,
|
||||||
|
required this.message,
|
||||||
|
required this.cmdInfo,
|
||||||
|
});
|
||||||
|
|
||||||
|
String cmd; // videoCall audioCall
|
||||||
|
int roomId;
|
||||||
|
String message;
|
||||||
|
String cmdInfo;
|
||||||
|
|
||||||
|
factory DataData.fromJson(Map<String, dynamic> json) => DataData(
|
||||||
|
cmd: json['cmd'],
|
||||||
|
roomId: json['room_id'],
|
||||||
|
message: json['message'],
|
||||||
|
cmdInfo: json['cmd_info'],
|
||||||
|
);
|
||||||
|
}
|
||||||
21
lib/models/im/contact_info_model.dart
Normal file
21
lib/models/im/contact_info_model.dart
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import 'package:azlistview/azlistview.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_info.dart';
|
||||||
|
|
||||||
|
class ContactInfoModel extends ISuspensionBean {
|
||||||
|
String name;
|
||||||
|
String userID;
|
||||||
|
String? tagIndex;
|
||||||
|
String? namePinyin;
|
||||||
|
V2TimFriendInfo? friendInfo;
|
||||||
|
|
||||||
|
ContactInfoModel({
|
||||||
|
required this.name,
|
||||||
|
required this.userID,
|
||||||
|
this.tagIndex,
|
||||||
|
this.namePinyin,
|
||||||
|
this.friendInfo,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getSuspensionTag() => tagIndex!;
|
||||||
|
}
|
||||||
25
lib/models/im/custom_message_model.dart
Normal file
25
lib/models/im/custom_message_model.dart
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
abstract class CustomMessageModel {
|
||||||
|
abstract String businessID;
|
||||||
|
|
||||||
|
String toJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomMessageType {
|
||||||
|
// ignore: constant_identifier_names
|
||||||
|
static const String NAME_CARD = 'name_card';
|
||||||
|
// ignore: constant_identifier_names
|
||||||
|
static const String GROUP_CARD = 'group_card';
|
||||||
|
// ignore: constant_identifier_names
|
||||||
|
static const String DT_TRANSFER = 'dt_transfer';
|
||||||
|
// ignore: constant_identifier_names
|
||||||
|
static const String TYPING_STATUS = 'user_typing_status';
|
||||||
|
// ignore: constant_identifier_names
|
||||||
|
static const String EVALUATION = 'evaluation';
|
||||||
|
// ignore: constant_identifier_names
|
||||||
|
static const String CALL = '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallingType {
|
||||||
|
static const String audioCall = 'audioCall';
|
||||||
|
static const String videoCall = 'videoCall';
|
||||||
|
}
|
||||||
14
lib/models/im/emoji_model.dart
Normal file
14
lib/models/im/emoji_model.dart
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
class EmojiModel {
|
||||||
|
EmojiModel({
|
||||||
|
required this.name,
|
||||||
|
required this.unicode,
|
||||||
|
});
|
||||||
|
|
||||||
|
String name;
|
||||||
|
int unicode;
|
||||||
|
|
||||||
|
factory EmojiModel.fromJson(Map<String, dynamic> json) => EmojiModel(
|
||||||
|
name: json['name'],
|
||||||
|
unicode: json['unicode'],
|
||||||
|
);
|
||||||
|
}
|
||||||
29
lib/models/im/evaluation_model.dart
Normal file
29
lib/models/im/evaluation_model.dart
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import 'package:chat/models/im/custom_message_model.dart';
|
||||||
|
|
||||||
|
class EvaluationModel extends CustomMessageModel {
|
||||||
|
@override
|
||||||
|
String businessID;
|
||||||
|
int version;
|
||||||
|
double score;
|
||||||
|
String comment;
|
||||||
|
|
||||||
|
EvaluationModel({
|
||||||
|
this.businessID = 'evaluation',
|
||||||
|
required this.version,
|
||||||
|
required this.score,
|
||||||
|
required this.comment,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory EvaluationModel.fromJson(Map<String, dynamic> json) =>
|
||||||
|
EvaluationModel(
|
||||||
|
businessID: json['businessID'],
|
||||||
|
version: json['version'],
|
||||||
|
score: double.parse(json['score']),
|
||||||
|
comment: json['comment'],
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toJson() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
33
lib/models/im/group_card_model.dart
Normal file
33
lib/models/im/group_card_model.dart
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:chat/models/im/custom_message_model.dart';
|
||||||
|
|
||||||
|
class GroupCardModel extends CustomMessageModel {
|
||||||
|
@override
|
||||||
|
String businessID;
|
||||||
|
String groupID;
|
||||||
|
String groupName;
|
||||||
|
String inviterID;
|
||||||
|
|
||||||
|
GroupCardModel({
|
||||||
|
this.businessID = CustomMessageType.GROUP_CARD,
|
||||||
|
required this.groupID,
|
||||||
|
required this.groupName,
|
||||||
|
required this.inviterID,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory GroupCardModel.fromJson(Map<String, dynamic> json) => GroupCardModel(
|
||||||
|
businessID: json['businessID'],
|
||||||
|
groupID: json['groupID'],
|
||||||
|
groupName: json['groupName'],
|
||||||
|
inviterID: json['inviterID'],
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toJson() => json.encode({
|
||||||
|
'businessID': businessID,
|
||||||
|
'groupID': groupID,
|
||||||
|
'groupName': groupName,
|
||||||
|
'inviterID': inviterID,
|
||||||
|
});
|
||||||
|
}
|
||||||
25
lib/models/im/group_conversation_model.dart
Normal file
25
lib/models/im/group_conversation_model.dart
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_conversation.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_info.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_member_full_info.dart';
|
||||||
|
|
||||||
|
class GroupConversationModel {
|
||||||
|
String groupID;
|
||||||
|
V2TimGroupInfo? group;
|
||||||
|
List<V2TimGroupMemberFullInfo?>? memberList;
|
||||||
|
List<V2TimGroupMemberFullInfo?>? adminList;
|
||||||
|
V2TimGroupMemberFullInfo? selfInfo;
|
||||||
|
V2TimConversation? conversation;
|
||||||
|
bool isAdmin;
|
||||||
|
bool isOwner;
|
||||||
|
|
||||||
|
GroupConversationModel({
|
||||||
|
required this.groupID,
|
||||||
|
this.group,
|
||||||
|
this.memberList,
|
||||||
|
this.adminList,
|
||||||
|
this.selfInfo,
|
||||||
|
this.conversation,
|
||||||
|
this.isAdmin = false,
|
||||||
|
this.isOwner = false,
|
||||||
|
});
|
||||||
|
}
|
||||||
23
lib/models/im/location_model.dart
Normal file
23
lib/models/im/location_model.dart
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
class LocationModel {
|
||||||
|
LocationModel({
|
||||||
|
required this.name,
|
||||||
|
required this.address,
|
||||||
|
required this.list,
|
||||||
|
required this.latitude,
|
||||||
|
required this.longitude,
|
||||||
|
});
|
||||||
|
|
||||||
|
String name;
|
||||||
|
String address;
|
||||||
|
List<dynamic> list;
|
||||||
|
double latitude;
|
||||||
|
double longitude;
|
||||||
|
|
||||||
|
factory LocationModel.fromJson(Map<String, dynamic> json) => LocationModel(
|
||||||
|
name: json['name'],
|
||||||
|
address: json['address'],
|
||||||
|
list: List<dynamic>.from(json['list'].map((x) => x)),
|
||||||
|
latitude: json['latitude'].toDouble(),
|
||||||
|
longitude: json['longitude'].toDouble(),
|
||||||
|
);
|
||||||
|
}
|
||||||
33
lib/models/im/name_card_model.dart
Normal file
33
lib/models/im/name_card_model.dart
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:chat/models/im/custom_message_model.dart';
|
||||||
|
|
||||||
|
class NameCardModel extends CustomMessageModel {
|
||||||
|
@override
|
||||||
|
String businessID;
|
||||||
|
String avatar;
|
||||||
|
String userID;
|
||||||
|
String userName;
|
||||||
|
|
||||||
|
NameCardModel({
|
||||||
|
this.businessID = CustomMessageType.NAME_CARD,
|
||||||
|
required this.avatar,
|
||||||
|
required this.userID,
|
||||||
|
required this.userName,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory NameCardModel.fromJson(Map<String, dynamic> json) => NameCardModel(
|
||||||
|
businessID: json['businessID'],
|
||||||
|
avatar: json['avatar'],
|
||||||
|
userID: json['userID'],
|
||||||
|
userName: json['userName'],
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toJson() => json.encode({
|
||||||
|
'businessID': businessID,
|
||||||
|
'avatar': avatar,
|
||||||
|
'userID': userID,
|
||||||
|
'userName': userName,
|
||||||
|
});
|
||||||
|
}
|
||||||
40
lib/models/im/private_conversation_model.dart
Normal file
40
lib/models/im/private_conversation_model.dart
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_conversation.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_user_full_info.dart';
|
||||||
|
|
||||||
|
class PrivateConversationModel {
|
||||||
|
String userID;
|
||||||
|
bool isFriend;
|
||||||
|
String friendRemark;
|
||||||
|
V2TimConversation? conversation;
|
||||||
|
V2TimUserFullInfo? userProfile;
|
||||||
|
bool isStaffer;
|
||||||
|
bool allowStranger; // 允许陌生人消息
|
||||||
|
String? shopId; // 他是哪个店铺的客服
|
||||||
|
|
||||||
|
PrivateConversationModel({
|
||||||
|
required this.userID,
|
||||||
|
this.friendRemark = '',
|
||||||
|
this.isFriend = false,
|
||||||
|
this.conversation,
|
||||||
|
this.userProfile,
|
||||||
|
this.isStaffer = false,
|
||||||
|
this.allowStranger = true,
|
||||||
|
this.shopId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserRelationEnum {
|
||||||
|
/// 不是好友
|
||||||
|
static const int V2TIM_FRIEND_RELATION_TYPE_NONE = 0;
|
||||||
|
|
||||||
|
/// 对方在我的好友列表
|
||||||
|
static const int V2TIM_FRIEND_RELATION_TYPE_IN_MY_FRIEND_LIST = 1;
|
||||||
|
|
||||||
|
/// 我在对方的好友列表
|
||||||
|
static const int V2TIM_FRIEND_RELATION_TYPE_IN_OTHER_FRIEND_LIST = 2;
|
||||||
|
|
||||||
|
/// 表示对方在我的好友列表中
|
||||||
|
static const int V2TIM_FRIEND_RELATION_TYPE_BOTH_WAY = 3;
|
||||||
|
}
|
||||||
18
lib/models/im/search_user_model.dart
Normal file
18
lib/models/im/search_user_model.dart
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
class SearchUserModel {
|
||||||
|
SearchUserModel({
|
||||||
|
required this.userID,
|
||||||
|
required this.nickname,
|
||||||
|
required this.avatar,
|
||||||
|
});
|
||||||
|
|
||||||
|
String userID;
|
||||||
|
String nickname;
|
||||||
|
String avatar;
|
||||||
|
|
||||||
|
factory SearchUserModel.fromJson(Map<String, dynamic> json) =>
|
||||||
|
SearchUserModel(
|
||||||
|
userID: json['user_id'].toString(),
|
||||||
|
nickname: json['nickname'],
|
||||||
|
avatar: json['avatar'],
|
||||||
|
);
|
||||||
|
}
|
||||||
32
lib/models/im/transfer_model.dart
Normal file
32
lib/models/im/transfer_model.dart
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:chat/models/im/custom_message_model.dart';
|
||||||
|
|
||||||
|
class TransferModel extends CustomMessageModel {
|
||||||
|
@override
|
||||||
|
String businessID;
|
||||||
|
String amount;
|
||||||
|
int orderId;
|
||||||
|
bool isReceived;
|
||||||
|
|
||||||
|
TransferModel({
|
||||||
|
this.businessID = CustomMessageType.DT_TRANSFER,
|
||||||
|
required this.amount,
|
||||||
|
required this.orderId,
|
||||||
|
this.isReceived = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory TransferModel.fromJson(Map<String, dynamic> json) => TransferModel(
|
||||||
|
businessID: json['businessID'],
|
||||||
|
amount: json['amount'],
|
||||||
|
orderId: json['orderId'] ?? 33,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toJson() => json.encode({
|
||||||
|
'businessID': businessID,
|
||||||
|
'amount': amount,
|
||||||
|
'orderId': orderId,
|
||||||
|
'isReceived': isReceived,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
import 'package:chat/routes/app_routes.dart';
|
import 'package:chat/routes/app_routes.dart';
|
||||||
import 'package:chat/routes/auth_routes.dart';
|
import 'package:chat/routes/auth_routes.dart';
|
||||||
|
import 'package:chat/routes/contact_routes.dart';
|
||||||
|
import 'package:chat/routes/conversation_routes.dart';
|
||||||
|
import 'package:chat/routes/user_routes.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
class AppRouter {
|
class AppRouter {
|
||||||
// 路由页面
|
// 路由页面
|
||||||
static final List<GetPage<dynamic>> getPages = [
|
static final List<GetPage<dynamic>> getPages = [
|
||||||
AppRoutes.router,
|
|
||||||
AuthRoutes.router,
|
AuthRoutes.router,
|
||||||
|
AppRoutes.router,
|
||||||
|
ConversationRoutes.router,
|
||||||
|
ContactRoutes.router,
|
||||||
|
UserRoutes.router,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:chat/views/conversation/index_page.dart';
|
||||||
import 'package:chat/views/home/index_page.dart';
|
import 'package:chat/views/home/index_page.dart';
|
||||||
import 'package:chat/views/public/app_page.dart';
|
import 'package:chat/views/public/app_page.dart';
|
||||||
import 'package:chat/views/public/scan_page.dart';
|
import 'package:chat/views/public/scan_page.dart';
|
||||||
@@ -10,9 +11,11 @@ abstract class AppRoutes {
|
|||||||
static const String app = '/';
|
static const String app = '/';
|
||||||
static const String transit = '/transit';
|
static const String transit = '/transit';
|
||||||
static const String notfound = '/notfound';
|
static const String notfound = '/notfound';
|
||||||
static const String home = '/home';
|
|
||||||
static const String scan = '/scan';
|
static const String scan = '/scan';
|
||||||
|
|
||||||
|
static const String home = '/home';
|
||||||
|
static const String search = '/search';
|
||||||
|
|
||||||
static GetPage router = GetPage(
|
static GetPage router = GetPage(
|
||||||
name: '/',
|
name: '/',
|
||||||
page: () => AppPage(),
|
page: () => AppPage(),
|
||||||
@@ -21,13 +24,17 @@ abstract class AppRoutes {
|
|||||||
name: AppRoutes.transit,
|
name: AppRoutes.transit,
|
||||||
page: () => const TransitPage(),
|
page: () => const TransitPage(),
|
||||||
),
|
),
|
||||||
|
GetPage(
|
||||||
|
name: AppRoutes.scan,
|
||||||
|
page: () => const ScanPage(),
|
||||||
|
),
|
||||||
GetPage(
|
GetPage(
|
||||||
name: AppRoutes.home,
|
name: AppRoutes.home,
|
||||||
page: () => const HomePage(),
|
page: () => const HomePage(),
|
||||||
),
|
),
|
||||||
GetPage(
|
GetPage(
|
||||||
name: AppRoutes.scan,
|
name: AppRoutes.search,
|
||||||
page: () => const ScanPage(),
|
page: () => const ConversationPage(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,27 @@
|
|||||||
import 'package:chat/middleware/auth_middleware.dart';
|
import 'package:chat/middleware/auth_middleware.dart';
|
||||||
|
import 'package:chat/views/contact/group/create/index_page.dart';
|
||||||
import 'package:chat/views/contact/group/index_page.dart';
|
import 'package:chat/views/contact/group/index_page.dart';
|
||||||
|
import 'package:chat/views/contact/group/manage/index_page.dart';
|
||||||
|
import 'package:chat/views/contact/group/notification/index_page.dart';
|
||||||
import 'package:chat/views/contact/index/index_page.dart';
|
import 'package:chat/views/contact/index/index_page.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
abstract class ContactRoutes {
|
abstract class ContactRoutes {
|
||||||
/// 身份验证页面
|
/// 身份验证页面
|
||||||
static const String index = '/contact';
|
static const String index = '/contact';
|
||||||
|
|
||||||
|
static const String friend = '/contact/friend';
|
||||||
|
static const String friendSearch = '/contact/friend/search';
|
||||||
|
static const String friendProfile = '/contact/friend/profile';
|
||||||
|
|
||||||
static const String group = '/contact/group';
|
static const String group = '/contact/group';
|
||||||
|
static const String groupQrCode = '/contact/group/qrCode';
|
||||||
|
static const String groupCreate = '/contact/group/create';
|
||||||
|
static const String groupNotification = '/contact/group/notification';
|
||||||
|
static const String groupManage = '/contact/group/manage';
|
||||||
|
static const String groupApprove = '/contact/group/approve';
|
||||||
|
static const String groupNickname = '/contact/group/nickname';
|
||||||
|
static const String groupKick = '/contact/group/kick';
|
||||||
|
|
||||||
static GetPage router = GetPage(
|
static GetPage router = GetPage(
|
||||||
name: ContactRoutes.index,
|
name: ContactRoutes.index,
|
||||||
@@ -15,9 +30,41 @@ abstract class ContactRoutes {
|
|||||||
],
|
],
|
||||||
page: () => const ContactPage(),
|
page: () => const ContactPage(),
|
||||||
children: [
|
children: [
|
||||||
|
GetPage(
|
||||||
|
name: '/friend',
|
||||||
|
page: () => const ContactGroupPage(),
|
||||||
|
children: [
|
||||||
|
GetPage(
|
||||||
|
name: '/search',
|
||||||
|
page: () => const ContactGroupCreatePage(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
GetPage(
|
GetPage(
|
||||||
name: '/group',
|
name: '/group',
|
||||||
page: () => const ContactGroupPage(),
|
page: () => const ContactGroupPage(),
|
||||||
|
children: [
|
||||||
|
GetPage(
|
||||||
|
name: '/create',
|
||||||
|
page: () => const ContactGroupCreatePage(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: '/qrCode',
|
||||||
|
page: () => const ContactGroupCreatePage(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: '/notification',
|
||||||
|
page: () => const ContactGroupNotificationPage(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: '/manage',
|
||||||
|
page: () => const ContactGroupManagePage(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: '/approve',
|
||||||
|
page: () => const ContactGroupManagePage(),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
36
lib/routes/conversation_routes.dart
Normal file
36
lib/routes/conversation_routes.dart
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import 'package:chat/middleware/auth_middleware.dart';
|
||||||
|
import 'package:chat/views/conversation/index_page.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
abstract class ConversationRoutes {
|
||||||
|
/// 身份验证页面
|
||||||
|
static const String index = '/conversation';
|
||||||
|
|
||||||
|
static const String infoGroup = '/conversation/info/group';
|
||||||
|
static const String infoPrivate = '/conversation/info/private';
|
||||||
|
|
||||||
|
static GetPage router = GetPage(
|
||||||
|
name: ConversationRoutes.index,
|
||||||
|
middlewares: [
|
||||||
|
EnsureAuthMiddleware(),
|
||||||
|
],
|
||||||
|
page: () => const ConversationPage(),
|
||||||
|
children: [
|
||||||
|
GetPage(
|
||||||
|
name: '/info',
|
||||||
|
page: () => Container(),
|
||||||
|
children: [
|
||||||
|
GetPage(
|
||||||
|
name: '/private',
|
||||||
|
page: () => Container(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: '/group',
|
||||||
|
page: () => Container(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
24
lib/routes/user_routes.dart
Normal file
24
lib/routes/user_routes.dart
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import 'package:chat/middleware/auth_middleware.dart';
|
||||||
|
import 'package:chat/views/contact/index/index_page.dart';
|
||||||
|
import 'package:chat/views/user/qr_code/index_page.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
abstract class UserRoutes {
|
||||||
|
/// 身份验证页面
|
||||||
|
static const String index = '/user';
|
||||||
|
static const String qrCode = '/user/qrCode';
|
||||||
|
|
||||||
|
static GetPage router = GetPage(
|
||||||
|
name: UserRoutes.index,
|
||||||
|
middlewares: [
|
||||||
|
EnsureAuthMiddleware(),
|
||||||
|
],
|
||||||
|
page: () => const ContactPage(),
|
||||||
|
children: [
|
||||||
|
GetPage(
|
||||||
|
name: '/qrCode',
|
||||||
|
page: () => const UserQrCodePage(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import 'package:chat/routes/auth_routes.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:get_storage/get_storage.dart';
|
import 'package:get_storage/get_storage.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
|
||||||
|
|
||||||
class AuthService extends GetxService {
|
class AuthService extends GetxService {
|
||||||
static AuthService get to => Get.find<AuthService>();
|
static AuthService get to => Get.find<AuthService>();
|
||||||
@@ -12,19 +14,24 @@ class AuthService extends GetxService {
|
|||||||
/// 登录状态记录,可监听的,这样ever才能监听到
|
/// 登录状态记录,可监听的,这样ever才能监听到
|
||||||
final RxBool isLogin = false.obs;
|
final RxBool isLogin = false.obs;
|
||||||
|
|
||||||
/// 登录的token,供请求时调用,载入内存,是为了每次使用的时候,不需要从磁盘获取
|
/// 供请求时调用,载入内存,是为了每次使用的时候,不需要从磁盘获取
|
||||||
late String userToken = '';
|
late String userId = '';
|
||||||
|
late String userSig = '';
|
||||||
|
|
||||||
/// 获取存储的token,这个可以做到持久化存储
|
/// 获取存储的token,这个可以做到持久化存储
|
||||||
String get _userToken => _box.read('userToken') ?? '';
|
String get _userSig =>
|
||||||
|
_box.read('userSig') ??
|
||||||
|
'eJwtzEELgjAYxvHvsnPIu7VNJ3ToIIKsIAp2ljbrRYylJs3ou2fq8fk98P*Qiz5Hg2tJSlgEZDNvtO7RY4UzixU7W5feoyUp5QAxVVzR5XFvj62bXAjBAGDRHpu-SSnZlseKrxW8TU1p9sV4v3YJoyFrDrV*QYu5yrQ*2cqUzyHkPpixOMbJjnx-EqUv9A__';
|
||||||
|
String get _userId => _box.read('userId') ?? '5';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
|
||||||
if (_userToken.isNotEmpty) {
|
if (_userSig.isNotEmpty) {
|
||||||
isLogin.value = true;
|
isLogin.value = true;
|
||||||
userToken = _userToken;
|
userSig = _userSig;
|
||||||
|
userId = _userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ever(_isLogin, (_) {
|
// ever(_isLogin, (_) {
|
||||||
@@ -37,10 +44,21 @@ class AuthService extends GetxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> login(String address) async {
|
Future<bool> login(String address) async {
|
||||||
_box.write('userToken', address);
|
_box.write('userId', '5');
|
||||||
userToken = address;
|
userId = '5';
|
||||||
isLogin.value = true;
|
isLogin.value = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 退出登录
|
||||||
|
void logout() async {
|
||||||
|
await TencentImSDKPlugin.v2TIMManager.logout();
|
||||||
|
_box.remove('userSig');
|
||||||
|
_box.remove('userId');
|
||||||
|
userSig = '';
|
||||||
|
userId = '';
|
||||||
|
isLogin.value = false;
|
||||||
|
Get.offAllNamed(AuthRoutes.index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
50
lib/services/tim/apply_service.dart
Normal file
50
lib/services/tim/apply_service.dart
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import 'package:chat/services/tim_service.dart';
|
||||||
|
import 'package:chat/utils/ui_tools.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/friend_application_type_enum.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/friend_response_type_enum.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/manager/v2_tim_friendship_manager.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_application.dart';
|
||||||
|
|
||||||
|
class TimApplyService extends GetxService {
|
||||||
|
static TimApplyService get to => Get.find<TimApplyService>();
|
||||||
|
|
||||||
|
/// 好友申请
|
||||||
|
RxList<V2TimFriendApplication?> applies =
|
||||||
|
List<V2TimFriendApplication?>.empty(growable: true).obs;
|
||||||
|
|
||||||
|
/// 好友关系
|
||||||
|
V2TIMFriendshipManager get friendshipManager =>
|
||||||
|
TimService.to.instance.v2TIMFriendshipManager;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() async {
|
||||||
|
super.onInit();
|
||||||
|
await fetchList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取申请列表
|
||||||
|
Future<void> fetchList() async {
|
||||||
|
var applyList = await friendshipManager.getFriendApplicationList();
|
||||||
|
if (applyList.code == 0) {
|
||||||
|
applies.value = applyList.data!.friendApplicationList!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 接受好友请求
|
||||||
|
Future<bool> accept(String userID) async {
|
||||||
|
var result = await friendshipManager.acceptFriendApplication(
|
||||||
|
responseType: FriendResponseTypeEnum.V2TIM_FRIEND_ACCEPT_AGREE_AND_ADD,
|
||||||
|
type: FriendApplicationTypeEnum.V2TIM_FRIEND_APPLICATION_COME_IN,
|
||||||
|
userID: userID,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
await fetchList();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
lib/services/tim/block_service.dart
Normal file
56
lib/services/tim/block_service.dart
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import 'package:chat/services/tim_service.dart';
|
||||||
|
import 'package:chat/utils/ui_tools.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/manager/v2_tim_friendship_manager.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_info.dart';
|
||||||
|
|
||||||
|
class TimBlockService extends GetxService {
|
||||||
|
static TimBlockService get to => Get.find<TimBlockService>();
|
||||||
|
|
||||||
|
/// 好友关系
|
||||||
|
V2TIMFriendshipManager get friendshipManager =>
|
||||||
|
TimService.to.instance.v2TIMFriendshipManager;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() async {
|
||||||
|
super.onInit();
|
||||||
|
await fetchList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 黑名单列表
|
||||||
|
RxList<V2TimFriendInfo> blocks =
|
||||||
|
List<V2TimFriendInfo>.empty(growable: true).obs;
|
||||||
|
|
||||||
|
/// 拉取黑名单列表
|
||||||
|
Future<void> fetchList() async {
|
||||||
|
var blacklist = await friendshipManager.getBlackList();
|
||||||
|
if (blacklist.code == 0) {
|
||||||
|
blocks.value = blacklist.data!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 拉黑某人
|
||||||
|
Future<bool> add(String userID) async {
|
||||||
|
var result = await friendshipManager.addToBlackList(userIDList: [userID]);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return result.data!.first.resultCode == 0;
|
||||||
|
} else {
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解除拉黑
|
||||||
|
Future<bool> remove(String userID) async {
|
||||||
|
var result =
|
||||||
|
await friendshipManager.deleteFromBlackList(userIDList: [userID]);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return result.data!.first.resultCode == 0;
|
||||||
|
} else {
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
379
lib/services/tim/conversation_service.dart
Normal file
379
lib/services/tim/conversation_service.dart
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
import 'package:chat/models/im/custom_message_model.dart';
|
||||||
|
import 'package:chat/models/im/location_model.dart';
|
||||||
|
import 'package:chat/services/tim_service.dart';
|
||||||
|
import 'package:chat/utils/ui_tools.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/V2TimConversationListener.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/conversation_type.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/receive_message_opt_enum.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/manager/v2_tim_conversation_manager.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/manager/v2_tim_message_manager.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_callback.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_conversation.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_msg_create_info_result.dart';
|
||||||
|
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||||
|
|
||||||
|
class TimConversationService extends GetxService {
|
||||||
|
static TimConversationService get to => Get.find<TimConversationService>();
|
||||||
|
|
||||||
|
/// 消息管理实例
|
||||||
|
V2TIMMessageManager get messageManager =>
|
||||||
|
TimService.to.instance.v2TIMMessageManager;
|
||||||
|
|
||||||
|
/// 会话管理
|
||||||
|
V2TIMConversationManager get conversationManager =>
|
||||||
|
TimService.to.instance.v2ConversationManager;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() async {
|
||||||
|
super.onInit();
|
||||||
|
await fetchList();
|
||||||
|
_addListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
_addListener() {
|
||||||
|
conversationManager.addConversationListener(
|
||||||
|
listener: V2TimConversationListener(
|
||||||
|
|
||||||
|
/// 未读消息总数监听
|
||||||
|
onTotalUnreadMessageCountChanged: (_) {
|
||||||
|
unreadCount.value = _;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 会话列表
|
||||||
|
RxList<V2TimConversation?> conversationList =
|
||||||
|
List<V2TimConversation?>.empty(growable: true).obs;
|
||||||
|
|
||||||
|
/// 未读消息总数
|
||||||
|
var unreadCount = 0.obs;
|
||||||
|
|
||||||
|
Future<void> fetchList() async {
|
||||||
|
var data = await conversationManager.getConversationList(
|
||||||
|
count: 100,
|
||||||
|
nextSeq: '0',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data.code == 0) {
|
||||||
|
conversationList.value = data.data!.conversationList!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取未读消息数
|
||||||
|
Future<void> getUnreadCount() async {
|
||||||
|
var result = await conversationManager.getTotalUnreadMessageCount();
|
||||||
|
unreadCount.value = result.data ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取会话信息
|
||||||
|
Future<V2TimConversation> getById(
|
||||||
|
String conversationID,
|
||||||
|
) async {
|
||||||
|
var result = await conversationManager.getConversation(
|
||||||
|
conversationID: conversationID,
|
||||||
|
);
|
||||||
|
return result.data!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 标记会话已读
|
||||||
|
Future<void> markAsRead(V2TimConversation conversation) async {
|
||||||
|
/// 标记会话内消息已读
|
||||||
|
if (conversation.type == ConversationType.V2TIM_GROUP) {
|
||||||
|
await messageManager.markGroupMessageAsRead(
|
||||||
|
groupID: conversation.groupID!,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await messageManager.markC2CMessageAsRead(
|
||||||
|
userID: conversation.userID!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fetchList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 从会话列表移除会话
|
||||||
|
Future<void> delete(V2TimConversation conversation) async {
|
||||||
|
await deleteById(conversation.conversationID);
|
||||||
|
fetchList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteById(String conversationID) async {
|
||||||
|
await conversationManager.deleteConversation(
|
||||||
|
conversationID: conversationID,
|
||||||
|
);
|
||||||
|
fetchList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 清空会话历史消息
|
||||||
|
Future<void> clearHistoryMessage(V2TimConversation conversation) async {
|
||||||
|
if (conversation.type == ConversationType.V2TIM_GROUP) {
|
||||||
|
await messageManager.clearGroupHistoryMessage(
|
||||||
|
groupID: conversation.groupID!,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await messageManager.clearC2CHistoryMessage(
|
||||||
|
userID: conversation.userID!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fetchList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置会话置顶/取消置顶
|
||||||
|
Future<bool> setOnTop(V2TimConversation conversation) async {
|
||||||
|
var result = await conversationManager.pinConversation(
|
||||||
|
conversationID: conversation.conversationID,
|
||||||
|
isPinned: !conversation.isPinned!,
|
||||||
|
);
|
||||||
|
fetchList();
|
||||||
|
|
||||||
|
if (result.code != 0) {
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.code == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 开启/关闭消息免打扰
|
||||||
|
Future<bool> setReceiveOpt(
|
||||||
|
V2TimConversation conversation,
|
||||||
|
) async {
|
||||||
|
V2TimCallback result;
|
||||||
|
|
||||||
|
if (conversation.type == ConversationType.V2TIM_GROUP) {
|
||||||
|
result = await messageManager.setGroupReceiveMessageOpt(
|
||||||
|
groupID: conversation.groupID!,
|
||||||
|
opt: (conversation.recvOpt == 0)
|
||||||
|
? ReceiveMsgOptEnum.V2TIM_NOT_RECEIVE_MESSAGE
|
||||||
|
: ReceiveMsgOptEnum.V2TIM_RECEIVE_MESSAGE,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
result = await messageManager.setC2CReceiveMessageOpt(
|
||||||
|
userIDList: [
|
||||||
|
conversation.userID!,
|
||||||
|
],
|
||||||
|
opt: (conversation.recvOpt == 0)
|
||||||
|
? ReceiveMsgOptEnum.V2TIM_NOT_RECEIVE_MESSAGE
|
||||||
|
: ReceiveMsgOptEnum.V2TIM_RECEIVE_MESSAGE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchList();
|
||||||
|
|
||||||
|
return result.code == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发送消息
|
||||||
|
Future<bool> sendTextMessage(
|
||||||
|
V2TimConversation conversation,
|
||||||
|
String text,
|
||||||
|
) async {
|
||||||
|
var msg = await messageManager.createTextAtMessage(
|
||||||
|
text: text,
|
||||||
|
atUserList: [],
|
||||||
|
);
|
||||||
|
if (msg.code == 0) {
|
||||||
|
return await _sendMessage(conversation, msg.data!);
|
||||||
|
} else {
|
||||||
|
UiTools.toast(msg.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发送图片消息
|
||||||
|
Future<bool> sendImageMessage(
|
||||||
|
V2TimConversation conversation,
|
||||||
|
AssetEntity asset,
|
||||||
|
) async {
|
||||||
|
var msg = await messageManager.createImageMessage(
|
||||||
|
imagePath: (await asset.file)!.path,
|
||||||
|
);
|
||||||
|
if (msg.code == 0) {
|
||||||
|
return await _sendMessage(conversation, msg.data!);
|
||||||
|
} else {
|
||||||
|
UiTools.toast(msg.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发送语音消息
|
||||||
|
Future<bool> sendSoundMessage(
|
||||||
|
V2TimConversation conversation,
|
||||||
|
String soundPath,
|
||||||
|
int duration,
|
||||||
|
) async {
|
||||||
|
var msg = await messageManager.createSoundMessage(
|
||||||
|
soundPath: soundPath, duration: duration);
|
||||||
|
if (msg.code == 0) {
|
||||||
|
return await _sendMessage(conversation, msg.data!);
|
||||||
|
} else {
|
||||||
|
UiTools.toast(msg.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发送视频消息
|
||||||
|
Future<bool> sendVideoMessage(
|
||||||
|
V2TimConversation conversation,
|
||||||
|
AssetEntity asset,
|
||||||
|
) async {
|
||||||
|
return false;
|
||||||
|
// final originFile = await asset.originFile;
|
||||||
|
// var size = await originFile!.length();
|
||||||
|
|
||||||
|
// if (size >= 104857600) {
|
||||||
|
// UiTools.toast('视频文件不能超过100M');
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// final duration = asset.videoDuration.inSeconds;
|
||||||
|
|
||||||
|
// String tempPath = (await getTemporaryDirectory()).path;
|
||||||
|
// String? thumbnail = await VideoThumbnail.thumbnailFile(
|
||||||
|
// video: originFile.path,
|
||||||
|
// thumbnailPath: tempPath,
|
||||||
|
// imageFormat: ImageFormat.JPEG,
|
||||||
|
// maxWidth: 256,
|
||||||
|
// quality: 25,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// var msg = await messageManager.createVideoMessage(
|
||||||
|
// videoFilePath: originFile.path,
|
||||||
|
// type: asset.mimeType!.replaceFirst('video/', ''),
|
||||||
|
// duration: duration,
|
||||||
|
// snapshotPath: thumbnail ?? '',
|
||||||
|
// );
|
||||||
|
// if (msg.code == 0) {
|
||||||
|
// return await _sendMessage(conversation, msg.data!);
|
||||||
|
// } else {
|
||||||
|
// UiTools.toast(msg.desc);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发送文件消息
|
||||||
|
Future<bool> sendFileMessage(
|
||||||
|
V2TimConversation conversation,
|
||||||
|
String fileName,
|
||||||
|
String filePath,
|
||||||
|
) async {
|
||||||
|
var msg = await messageManager.createFileMessage(
|
||||||
|
fileName: fileName,
|
||||||
|
filePath: filePath,
|
||||||
|
);
|
||||||
|
if (msg.code == 0) {
|
||||||
|
return await _sendMessage(conversation, msg.data!);
|
||||||
|
} else {
|
||||||
|
UiTools.toast(msg.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发送位置消息
|
||||||
|
Future<bool> sendLocationMessage(
|
||||||
|
V2TimConversation conversation,
|
||||||
|
LocationModel messageModel,
|
||||||
|
) async {
|
||||||
|
var msg = await messageManager.createLocationMessage(
|
||||||
|
desc: messageModel.name,
|
||||||
|
latitude: messageModel.latitude,
|
||||||
|
longitude: messageModel.longitude,
|
||||||
|
);
|
||||||
|
if (msg.code == 0) {
|
||||||
|
return await _sendMessage(conversation, msg.data!);
|
||||||
|
} else {
|
||||||
|
UiTools.toast(msg.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发送表情消息
|
||||||
|
Future<bool> sendFaceMessage(
|
||||||
|
V2TimConversation conversation,
|
||||||
|
) async {
|
||||||
|
var msg = await messageManager.createFaceMessage(
|
||||||
|
data: '',
|
||||||
|
index: 0,
|
||||||
|
);
|
||||||
|
if (msg.code == 0) {
|
||||||
|
return await _sendMessage(conversation, msg.data!);
|
||||||
|
} else {
|
||||||
|
UiTools.toast(msg.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发送聊天记录消息
|
||||||
|
Future<bool> sendMergerMessage(
|
||||||
|
V2TimConversation conversation,
|
||||||
|
) async {
|
||||||
|
var msg = await messageManager.createMergerMessage(
|
||||||
|
abstractList: [],
|
||||||
|
compatibleText: '',
|
||||||
|
msgIDList: [],
|
||||||
|
title: '',
|
||||||
|
);
|
||||||
|
if (msg.code == 0) {
|
||||||
|
return await _sendMessage(conversation, msg.data!);
|
||||||
|
} else {
|
||||||
|
UiTools.toast(msg.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发送自定义消息
|
||||||
|
Future<bool> sendCustomMessage(
|
||||||
|
V2TimConversation conversation,
|
||||||
|
CustomMessageModel customMessageModel,
|
||||||
|
String desc,
|
||||||
|
) async {
|
||||||
|
var msg = await messageManager.createCustomMessage(
|
||||||
|
data: customMessageModel.toJson(),
|
||||||
|
desc: desc,
|
||||||
|
);
|
||||||
|
if (msg.code == 0) {
|
||||||
|
return await _sendMessage(conversation, msg.data!);
|
||||||
|
} else {
|
||||||
|
UiTools.toast(msg.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _sendMessage(
|
||||||
|
V2TimConversation conversation,
|
||||||
|
V2TimMsgCreateInfoResult result,
|
||||||
|
) async {
|
||||||
|
var sendMessageRes = await messageManager.sendMessage(
|
||||||
|
id: result.id!,
|
||||||
|
receiver: conversation.type == ConversationType.V2TIM_C2C
|
||||||
|
? conversation.userID!
|
||||||
|
: '',
|
||||||
|
groupID: conversation.type == ConversationType.V2TIM_GROUP
|
||||||
|
? conversation.groupID!
|
||||||
|
: '',
|
||||||
|
// isExcludedFromUnreadCount: true,
|
||||||
|
// isExcludedFromLastMessage: true,
|
||||||
|
// needReadReceipt: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (sendMessageRes.code == 0) {
|
||||||
|
// TimMessageService.to
|
||||||
|
// .add(conversation.conversationID, result.messageInfo!);
|
||||||
|
// eventBus.fire(result.messageInfo!);
|
||||||
|
fetchList();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
UiTools.toast(sendMessageRes.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置会话草稿
|
||||||
|
Future<void> draft(
|
||||||
|
V2TimConversation conversation, {
|
||||||
|
String? draftText = "",
|
||||||
|
}) async {
|
||||||
|
await conversationManager.setConversationDraft(
|
||||||
|
conversationID: conversation.conversationID,
|
||||||
|
draftText: draftText,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
241
lib/services/tim/friend_service.dart
Normal file
241
lib/services/tim/friend_service.dart
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
import 'package:azlistview/azlistview.dart';
|
||||||
|
import 'package:chat/models/im/contact_info_model.dart';
|
||||||
|
import 'package:chat/models/im/search_user_model.dart';
|
||||||
|
import 'package:chat/services/tim_service.dart';
|
||||||
|
import 'package:chat/utils/im_tools.dart';
|
||||||
|
import 'package:chat/utils/request/http.dart';
|
||||||
|
import 'package:chat/utils/ui_tools.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:lpinyin/lpinyin.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/friend_type_enum.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/manager/v2_tim_friendship_manager.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_check_result.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_info.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_info_result.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_user_full_info.dart';
|
||||||
|
|
||||||
|
class TimFriendService extends GetxService {
|
||||||
|
static TimFriendService get to => Get.find<TimFriendService>();
|
||||||
|
|
||||||
|
/// 好友关系
|
||||||
|
V2TIMFriendshipManager get friendshipManager =>
|
||||||
|
TimService.to.instance.v2TIMFriendshipManager;
|
||||||
|
|
||||||
|
/// 好友列表
|
||||||
|
var friends = List<V2TimFriendInfo>.empty(growable: true).obs;
|
||||||
|
|
||||||
|
/// 格式化后的联系人信息
|
||||||
|
var contacts = List<ContactInfoModel>.empty(growable: true).obs;
|
||||||
|
|
||||||
|
Future<void> fetchList() async {
|
||||||
|
var result = await friendshipManager.getFriendList();
|
||||||
|
if (result.code == 0) {
|
||||||
|
friends.value = result.data!;
|
||||||
|
contacts.clear();
|
||||||
|
for (var element in result.data!) {
|
||||||
|
String name = ImTools.parseNicknameFromInfo(element);
|
||||||
|
String pinyin = PinyinHelper.getPinyinE(name);
|
||||||
|
String tag = pinyin.substring(0, 1).toUpperCase();
|
||||||
|
|
||||||
|
if (!RegExp('[A-Z]').hasMatch(tag)) {
|
||||||
|
tag = '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
contacts.add(ContactInfoModel(
|
||||||
|
name: name,
|
||||||
|
userID: element.userID,
|
||||||
|
tagIndex: tag,
|
||||||
|
namePinyin: pinyin,
|
||||||
|
friendInfo: element,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
SuspensionUtil.sortListBySuspensionTag(contacts);
|
||||||
|
SuspensionUtil.setShowSuspensionStatus(contacts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 添加好友
|
||||||
|
Future<bool> add(
|
||||||
|
String userID, {
|
||||||
|
String? remark,
|
||||||
|
String? addWording,
|
||||||
|
String? addSource,
|
||||||
|
}) async {
|
||||||
|
var result = await friendshipManager.addFriend(
|
||||||
|
userID: userID,
|
||||||
|
remark: remark,
|
||||||
|
addWording: addWording,
|
||||||
|
addSource: addSource,
|
||||||
|
addType: FriendTypeEnum.V2TIM_FRIEND_TYPE_BOTH,
|
||||||
|
);
|
||||||
|
if (result.code == 0) {
|
||||||
|
if (result.data!.resultCode == 0) {
|
||||||
|
fetchList();
|
||||||
|
return true;
|
||||||
|
} else if (result.data!.resultCode == 30539) {
|
||||||
|
return true;
|
||||||
|
} else if (result.data!.resultCode == 30010) {
|
||||||
|
UiTools.toast('好友数量已达上限');
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
UiTools.toast(
|
||||||
|
result.data!.resultInfo! + result.data!.resultCode.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 删除双向好友
|
||||||
|
Future<bool> delete(String userID) async {
|
||||||
|
var result = await friendshipManager.deleteFromFriendList(
|
||||||
|
deleteType: FriendTypeEnum.V2TIM_FRIEND_TYPE_BOTH,
|
||||||
|
userIDList: [userID],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
fetchList();
|
||||||
|
return result.data!.first.resultCode == 0;
|
||||||
|
} else {
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 检测好友是否有双向(单向)好友关系。
|
||||||
|
Future<V2TimFriendCheckResult?> check(String userID) async {
|
||||||
|
var result = await friendshipManager.checkFriend(
|
||||||
|
checkType: FriendTypeEnum.V2TIM_FRIEND_TYPE_BOTH,
|
||||||
|
userIDList: [userID],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return result.data!.first;
|
||||||
|
} else {
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取用户资料
|
||||||
|
Future<V2TimUserFullInfo?> userInfo(String userID) async {
|
||||||
|
var result = await TimService.to.instance.getUsersInfo(
|
||||||
|
userIDList: [userID],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return result.data!.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取好友资料
|
||||||
|
Future<V2TimFriendInfoResult?> friendInfo(String userID) async {
|
||||||
|
var result = await friendshipManager.getFriendsInfo(userIDList: [userID]);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
if (result.data!.isNotEmpty) {
|
||||||
|
return result.data!.first;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 修改个人资料
|
||||||
|
Future<bool> setSelfInfo({
|
||||||
|
String? nickname,
|
||||||
|
String? avatar,
|
||||||
|
}) async {
|
||||||
|
var result = await TimService.to.instance.setSelfInfo(
|
||||||
|
userFullInfo: V2TimUserFullInfo(
|
||||||
|
nickName: nickname,
|
||||||
|
faceUrl: avatar,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
// TimService.to.fetchSelfInfo();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置是否允许陌生人消息
|
||||||
|
Future<bool> allowStrangerMessage() async {
|
||||||
|
var result = await TimService.to.instance.setSelfInfo(
|
||||||
|
userFullInfo: V2TimUserFullInfo(
|
||||||
|
customInfo: {
|
||||||
|
'Stranger': 'TRUE',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 禁止陌生人消息
|
||||||
|
Future<bool> forbidStrangerMessage() async {
|
||||||
|
var result = await TimService.to.instance.setSelfInfo(
|
||||||
|
userFullInfo: V2TimUserFullInfo(
|
||||||
|
customInfo: {
|
||||||
|
'Stranger': '',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (result.code == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 修改好友备注信息
|
||||||
|
Future<bool> setFriendRemark(
|
||||||
|
String userID,
|
||||||
|
String friendRemark,
|
||||||
|
) async {
|
||||||
|
var result = await friendshipManager.setFriendInfo(
|
||||||
|
userID: userID,
|
||||||
|
friendRemark: friendRemark,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 从服务器查找用户
|
||||||
|
Future<List<SearchUserModel>?> searchUser(String keyword) async {
|
||||||
|
try {
|
||||||
|
var json = await Http.get(
|
||||||
|
'user/search',
|
||||||
|
params: {
|
||||||
|
'keyword': keyword,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return List<SearchUserModel>.from(
|
||||||
|
json.map(
|
||||||
|
(x) => SearchUserModel.fromJson(x),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
UiTools.toast(err.toString());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
403
lib/services/tim/group_service.dart
Normal file
403
lib/services/tim/group_service.dart
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
import 'package:chat/services/tim/conversation_service.dart';
|
||||||
|
import 'package:chat/services/tim_service.dart';
|
||||||
|
import 'package:chat/utils/ui_tools.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/group_add_opt_enum.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/group_application_type_enum.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/group_member_filter_enum.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/group_member_role_enum.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/group_type.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/manager/v2_tim_group_manager.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_conversation.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_info.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_application.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_info.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_member.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_member_full_info.dart';
|
||||||
|
|
||||||
|
class TimGroupService extends GetxService {
|
||||||
|
static TimGroupService get to => Get.find<TimGroupService>();
|
||||||
|
|
||||||
|
/// 群管理实例
|
||||||
|
V2TIMGroupManager get groupManager =>
|
||||||
|
TimService.to.instance.v2TIMGroupManager;
|
||||||
|
|
||||||
|
/// 我的群组列表
|
||||||
|
RxList<V2TimGroupInfo> groups =
|
||||||
|
List<V2TimGroupInfo>.empty(growable: true).obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() async {
|
||||||
|
super.onInit();
|
||||||
|
await fetchList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> fetchList() async {
|
||||||
|
var result = await groupManager.getJoinedGroupList();
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
groups.value = result.data!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 创建群
|
||||||
|
Future<String?> create(
|
||||||
|
String groupName,
|
||||||
|
List<V2TimFriendInfo> memberList, {
|
||||||
|
String? groupType,
|
||||||
|
}) async {
|
||||||
|
var result = await groupManager.createGroup(
|
||||||
|
groupType: groupType ?? GroupType.Public,
|
||||||
|
groupName: groupName,
|
||||||
|
notification: '',
|
||||||
|
introduction: '',
|
||||||
|
faceUrl: '',
|
||||||
|
isAllMuted: false,
|
||||||
|
isSupportTopic: false,
|
||||||
|
addOpt: GroupAddOptTypeEnum.V2TIM_GROUP_ADD_AUTH,
|
||||||
|
memberList: memberList.map((e) {
|
||||||
|
return V2TimGroupMember(
|
||||||
|
role: GroupMemberRoleTypeEnum.V2TIM_GROUP_MEMBER_ROLE_MEMBER,
|
||||||
|
userID: e.userID,
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code != 0) {
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
await fetchList();
|
||||||
|
await TimConversationService.to.fetchList();
|
||||||
|
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 加群申请列表
|
||||||
|
Future<List<V2TimGroupApplication?>?> applies(String groupID) async {
|
||||||
|
var result = await groupManager.getGroupApplicationList();
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return result.data?.groupApplicationList;
|
||||||
|
} else {
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 同意加群
|
||||||
|
Future<void> accept() async {
|
||||||
|
await groupManager.acceptGroupApplication(
|
||||||
|
fromUser: '',
|
||||||
|
groupID: '',
|
||||||
|
toUser: '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 拒绝加群请求
|
||||||
|
Future<void> refuse() async {
|
||||||
|
await groupManager.refuseGroupApplication(
|
||||||
|
addTime: 0,
|
||||||
|
fromUser: '',
|
||||||
|
groupID: '',
|
||||||
|
toUser: '',
|
||||||
|
type: GroupApplicationTypeEnum.V2TIM_GROUP_APPLICATION_GET_TYPE_JOIN,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 加入群组
|
||||||
|
Future<bool> join(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
String message,
|
||||||
|
) async {
|
||||||
|
var result = await TimService.to.instance.joinGroup(
|
||||||
|
groupID: group.groupID,
|
||||||
|
groupType: group.groupType,
|
||||||
|
message: message,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
await fetchList();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 邀请加群
|
||||||
|
Future<bool> invite(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
List<String> userList,
|
||||||
|
) async {
|
||||||
|
var result = await groupManager.inviteUserToGroup(
|
||||||
|
groupID: group.groupID,
|
||||||
|
userList: userList,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 退出群组
|
||||||
|
Future<void> quit(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
) async {
|
||||||
|
await TimService.to.instance.quitGroup(
|
||||||
|
groupID: group.groupID,
|
||||||
|
);
|
||||||
|
await fetchList();
|
||||||
|
await TimConversationService.to.deleteById(
|
||||||
|
'group_' + group.groupID,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解散群组
|
||||||
|
Future<bool> dismiss(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
V2TimConversation conversation,
|
||||||
|
) async {
|
||||||
|
var result = await TimService.to.instance.dismissGroup(
|
||||||
|
groupID: group.groupID,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取群资料
|
||||||
|
Future<V2TimGroupInfo?> info(String groupId) async {
|
||||||
|
var result = await groupManager.getGroupsInfo(groupIDList: [groupId]);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return result.data![0].groupInfo!;
|
||||||
|
} else {
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 更新群资料
|
||||||
|
Future<bool> updateName(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
String groupName,
|
||||||
|
) async {
|
||||||
|
var result = await groupManager.setGroupInfo(
|
||||||
|
info: V2TimGroupInfo.fromJson(
|
||||||
|
{
|
||||||
|
'groupID': group.groupID,
|
||||||
|
'groupType': group.groupType,
|
||||||
|
'groupName': groupName,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 更新群公告
|
||||||
|
Future<bool> updateNotification(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
String notification,
|
||||||
|
) async {
|
||||||
|
var result = await groupManager.setGroupInfo(
|
||||||
|
info: V2TimGroupInfo.fromJson(
|
||||||
|
{
|
||||||
|
'groupID': group.groupID,
|
||||||
|
'groupType': group.groupType,
|
||||||
|
'notification': notification,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
await TimConversationService.to.fetchList();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 修改加群方式
|
||||||
|
Future<bool> updateAddOpt(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
String notification,
|
||||||
|
) async {
|
||||||
|
var result = await groupManager.setGroupInfo(
|
||||||
|
info: V2TimGroupInfo.fromJson(
|
||||||
|
{
|
||||||
|
'groupID': group.groupID,
|
||||||
|
'groupType': group.groupType,
|
||||||
|
'groupAddOpt': group.groupAddOpt,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
await TimConversationService.to.fetchList();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取群组成员
|
||||||
|
Future<List<V2TimGroupMemberFullInfo?>?> members(
|
||||||
|
String groupID, {
|
||||||
|
int count = 10,
|
||||||
|
GroupMemberFilterTypeEnum filter =
|
||||||
|
GroupMemberFilterTypeEnum.V2TIM_GROUP_MEMBER_FILTER_ALL,
|
||||||
|
}) async {
|
||||||
|
var result = await groupManager.getGroupMemberList(
|
||||||
|
count: count,
|
||||||
|
filter: filter,
|
||||||
|
nextSeq: '0',
|
||||||
|
offset: 0,
|
||||||
|
groupID: groupID,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return result.data!.memberInfoList!;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取群成员资料
|
||||||
|
Future<V2TimGroupMemberFullInfo?> getMemberInfo(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
String userID,
|
||||||
|
) async {
|
||||||
|
var result = await groupManager.getGroupMembersInfo(
|
||||||
|
groupID: group.groupID,
|
||||||
|
memberList: [userID],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.data != null) {
|
||||||
|
return result.data!.first;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置群成员资料
|
||||||
|
Future<bool> setMemberInfo(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
String userID,
|
||||||
|
String nameCard,
|
||||||
|
) async {
|
||||||
|
var result = await groupManager.setGroupMemberInfo(
|
||||||
|
groupID: group.groupID,
|
||||||
|
userID: userID,
|
||||||
|
nameCard: nameCard,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 禁言群成员, 时间单位秒,如果是0,应该就解除禁言了
|
||||||
|
Future<bool> muteMember(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
String userID, {
|
||||||
|
int seconds = 60,
|
||||||
|
}) async {
|
||||||
|
var result = await groupManager.muteGroupMember(
|
||||||
|
groupID: group.groupID,
|
||||||
|
userID: userID,
|
||||||
|
seconds: seconds,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 全员禁言/解除全员禁言
|
||||||
|
Future<bool> mute(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
bool isAllMuted,
|
||||||
|
) async {
|
||||||
|
var result = await groupManager.setGroupInfo(
|
||||||
|
info: V2TimGroupInfo(
|
||||||
|
isAllMuted: isAllMuted,
|
||||||
|
groupID: group.groupID,
|
||||||
|
groupType: group.groupType,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 踢人
|
||||||
|
Future<bool> kickMember(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
List<String> memberList,
|
||||||
|
) async {
|
||||||
|
var result = await groupManager.kickGroupMember(
|
||||||
|
groupID: group.groupID,
|
||||||
|
memberList: memberList,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置管理员
|
||||||
|
Future<bool> setMemberRole(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
String userID,
|
||||||
|
GroupMemberRoleTypeEnum role,
|
||||||
|
) async {
|
||||||
|
var result = await groupManager.setGroupMemberRole(
|
||||||
|
groupID: group.groupID,
|
||||||
|
userID: userID,
|
||||||
|
role: role,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 转让群主
|
||||||
|
Future<bool> transfer(
|
||||||
|
V2TimGroupInfo group,
|
||||||
|
String userID,
|
||||||
|
) async {
|
||||||
|
var result = await groupManager.transferGroupOwner(
|
||||||
|
groupID: group.groupID,
|
||||||
|
userID: userID,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UiTools.toast(result.desc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
77
lib/services/tim/message_service.dart
Normal file
77
lib/services/tim/message_service.dart
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/conversation_type.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_conversation.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_message.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_value_callback.dart';
|
||||||
|
import 'conversation_service.dart';
|
||||||
|
|
||||||
|
class LoadMessageResult {
|
||||||
|
String? lastMsgId;
|
||||||
|
bool noMore;
|
||||||
|
|
||||||
|
LoadMessageResult({
|
||||||
|
this.lastMsgId,
|
||||||
|
required this.noMore,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class TimMessageService extends GetxService {
|
||||||
|
static TimMessageService get to => Get.find<TimMessageService>();
|
||||||
|
|
||||||
|
RxList<V2TimMessage> messages = List<V2TimMessage>.empty(growable: true).obs;
|
||||||
|
|
||||||
|
var curConversationId = '';
|
||||||
|
|
||||||
|
Future<String?> loadMessagesFromService(
|
||||||
|
V2TimConversation conversation,
|
||||||
|
String? lastMsgId,
|
||||||
|
AutoScrollController _scrollController,
|
||||||
|
) async {
|
||||||
|
if (lastMsgId == null && curConversationId != conversation.conversationID) {
|
||||||
|
messages.clear();
|
||||||
|
curConversationId = conversation.conversationID;
|
||||||
|
}
|
||||||
|
|
||||||
|
V2TimValueCallback<List<V2TimMessage>> result;
|
||||||
|
if (conversation.type == ConversationType.V2TIM_GROUP) {
|
||||||
|
result = await TimConversationService.to.messageManager
|
||||||
|
.getGroupHistoryMessageList(
|
||||||
|
groupID: conversation.groupID!,
|
||||||
|
count: 20,
|
||||||
|
lastMsgID: lastMsgId,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
result = await TimConversationService.to.messageManager
|
||||||
|
.getC2CHistoryMessageList(
|
||||||
|
userID: conversation.userID!,
|
||||||
|
count: 20,
|
||||||
|
lastMsgID: lastMsgId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
var list = result.data ?? [];
|
||||||
|
if (lastMsgId == null) {
|
||||||
|
messages.value = list.reversed.toList();
|
||||||
|
_scrollController.scrollToIndex(
|
||||||
|
messages.length - 1,
|
||||||
|
preferPosition: AutoScrollPosition.begin,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
//加载更多
|
||||||
|
if (list.isNotEmpty) {
|
||||||
|
messages.value = messages.reversed.toList(); //正常顺序
|
||||||
|
messages.addAll(list);
|
||||||
|
messages.value = messages.reversed.toList(); //反转一次
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (messages.isNotEmpty) {
|
||||||
|
return messages.first.msgID;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addMessage(V2TimMessage message) {
|
||||||
|
messages.add(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,395 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:chat/controllers/group_controller.dart';
|
||||||
|
import 'package:chat/models/im/custom_message_model.dart';
|
||||||
|
import 'package:chat/services/auth_service.dart';
|
||||||
|
import 'package:chat/services/tim/apply_service.dart';
|
||||||
|
import 'package:chat/services/tim/block_service.dart';
|
||||||
|
import 'package:chat/services/tim/conversation_service.dart';
|
||||||
|
import 'package:chat/services/tim/friend_service.dart';
|
||||||
|
import 'package:chat/services/tim/group_service.dart';
|
||||||
|
import 'package:chat/services/tim/message_service.dart';
|
||||||
|
import 'package:chat/utils/ui_tools.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/V2TimAdvancedMsgListener.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/V2TimFriendshipListener.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/V2TimGroupListener.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/V2TimSDKListener.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/log_level_enum.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/message_elem_type.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/manager/v2_tim_manager.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_application.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_info.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_change_info.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_member_change_info.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_member_info.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_message.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_message_receipt.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_topic_info.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_user_full_info.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
|
||||||
|
import 'package:vibration/vibration.dart';
|
||||||
|
|
||||||
class TimService extends GetxService {
|
class TimService extends GetxService {
|
||||||
Future<TimService> init() async {
|
static TimService get to => Get.find<TimService>();
|
||||||
return this;
|
|
||||||
|
/// 获取实例
|
||||||
|
V2TIMManager get instance => _getInstance();
|
||||||
|
|
||||||
|
/// 获取TIM实例
|
||||||
|
V2TIMManager _getInstance() {
|
||||||
|
return TencentImSDKPlugin.v2TIMManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sdkAppID = 1400719491;
|
||||||
|
|
||||||
|
String get _userSig => AuthService.to.userSig;
|
||||||
|
|
||||||
|
String get _userId => AuthService.to.userId;
|
||||||
|
|
||||||
|
RxString currentConversationId = ''.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
|
||||||
|
if (AuthService.to.isLogin.value) {
|
||||||
|
initSdk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future initSdk() async {
|
||||||
|
/// 初始化TIMSDK
|
||||||
|
await instance.initSDK(
|
||||||
|
sdkAppID: sdkAppID,
|
||||||
|
loglevel: LogLevelEnum.V2TIM_LOG_INFO,
|
||||||
|
listener: V2TimSDKListener(
|
||||||
|
onConnectFailed: (
|
||||||
|
int code,
|
||||||
|
String error,
|
||||||
|
) {},
|
||||||
|
onConnectSuccess: () async {},
|
||||||
|
onConnecting: () {},
|
||||||
|
onKickedOffline: () {
|
||||||
|
UiTools.toast('该账号在其他设备登录被迫下线');
|
||||||
|
AuthService.to.logout();
|
||||||
|
},
|
||||||
|
onSelfInfoUpdated: (
|
||||||
|
V2TimUserFullInfo info,
|
||||||
|
) {},
|
||||||
|
onUserSigExpired: () {},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// 登录
|
||||||
|
await instance.login(
|
||||||
|
userID: _userId,
|
||||||
|
userSig: _userSig,
|
||||||
|
);
|
||||||
|
|
||||||
|
Get.put(TimConversationService());
|
||||||
|
Get.put(TimFriendService());
|
||||||
|
Get.put(TimGroupService());
|
||||||
|
Get.put(TimBlockService());
|
||||||
|
Get.put(TimApplyService());
|
||||||
|
Get.put(TimMessageService());
|
||||||
|
|
||||||
|
/// 消息监听器
|
||||||
|
await TimConversationService.to.messageManager.addAdvancedMsgListener(
|
||||||
|
listener: V2TimAdvancedMsgListener(
|
||||||
|
/// 收到新消息
|
||||||
|
onRecvNewMessage: (
|
||||||
|
V2TimMessage msg,
|
||||||
|
) {
|
||||||
|
onRecvNewMessage(msg);
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 收到C2C已读回执
|
||||||
|
onRecvC2CReadReceipt: (
|
||||||
|
List<V2TimMessageReceipt> receiptList,
|
||||||
|
) {},
|
||||||
|
|
||||||
|
/// 消息撤回
|
||||||
|
onRecvMessageRevoked: (
|
||||||
|
String msgID,
|
||||||
|
) {},
|
||||||
|
|
||||||
|
/// 发送消息进度
|
||||||
|
onSendMessageProgress: (
|
||||||
|
V2TimMessage message,
|
||||||
|
int progress,
|
||||||
|
) {},
|
||||||
|
|
||||||
|
/// 消息更改
|
||||||
|
onRecvMessageModified: (
|
||||||
|
V2TimMessage msg,
|
||||||
|
) {},
|
||||||
|
|
||||||
|
/// 消息已读回执
|
||||||
|
onRecvMessageReadReceipts: (
|
||||||
|
List<V2TimMessageReceipt> receiptList,
|
||||||
|
) {},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
TimFriendService.to.friendshipManager.setFriendListener(
|
||||||
|
listener: V2TimFriendshipListener(
|
||||||
|
/// 有新的好友请求
|
||||||
|
onFriendApplicationListAdded: (
|
||||||
|
List<V2TimFriendApplication> applicationList,
|
||||||
|
) {
|
||||||
|
TimApplyService.to.fetchList();
|
||||||
|
},
|
||||||
|
onFriendApplicationListDeleted: (
|
||||||
|
List<String> userIDList,
|
||||||
|
) {
|
||||||
|
TimApplyService.to.fetchList();
|
||||||
|
},
|
||||||
|
onFriendApplicationListRead: () {
|
||||||
|
// UiTools.toast('onFriendApplicationListRead');
|
||||||
|
},
|
||||||
|
onFriendListAdded: (
|
||||||
|
List<V2TimFriendInfo> users,
|
||||||
|
) {
|
||||||
|
TimFriendService.to.fetchList();
|
||||||
|
},
|
||||||
|
onFriendListDeleted: (
|
||||||
|
List<String> userList,
|
||||||
|
) {
|
||||||
|
TimFriendService.to.fetchList();
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 有黑名单
|
||||||
|
onBlackListAdd: (
|
||||||
|
List<V2TimFriendInfo> infoList,
|
||||||
|
) {
|
||||||
|
TimFriendService.to.fetchList();
|
||||||
|
TimBlockService.to.fetchList();
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 移除黑名单
|
||||||
|
onBlackListDeleted: (
|
||||||
|
List<String> userList,
|
||||||
|
) {
|
||||||
|
TimFriendService.to.fetchList();
|
||||||
|
TimBlockService.to.fetchList();
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 好友资料修改
|
||||||
|
onFriendInfoChanged: (
|
||||||
|
List<V2TimFriendInfo> infoList,
|
||||||
|
) {
|
||||||
|
TimFriendService.to.fetchList();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
instance.setGroupListener(
|
||||||
|
listener: V2TimGroupListener(
|
||||||
|
/// 群组解散
|
||||||
|
onGroupDismissed: (
|
||||||
|
String groupID,
|
||||||
|
V2TimGroupMemberInfo opUser,
|
||||||
|
) async {
|
||||||
|
await TimConversationService.to.deleteById(
|
||||||
|
'group_$groupID',
|
||||||
|
);
|
||||||
|
await TimGroupService.to.fetchList();
|
||||||
|
|
||||||
|
/// 如果是在当前的会话中,关闭会话内容,返回主页面
|
||||||
|
if (currentConversationId.value == 'group_' + groupID) {
|
||||||
|
UiTools.toast('群已解散');
|
||||||
|
Navigator.popUntil(Get.context!, (route) => route.isFirst);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 群资料修改
|
||||||
|
onGroupInfoChanged: (
|
||||||
|
String groupID,
|
||||||
|
List<V2TimGroupChangeInfo?> changeInfos,
|
||||||
|
) {
|
||||||
|
TimConversationService.to.fetchList();
|
||||||
|
TimGroupService.to.fetchList();
|
||||||
|
},
|
||||||
|
onMemberEnter: (
|
||||||
|
String groupID,
|
||||||
|
List<V2TimGroupMemberInfo> memberList,
|
||||||
|
) {
|
||||||
|
UiTools.toast('onMemberEnter');
|
||||||
|
},
|
||||||
|
onMemberLeave: (
|
||||||
|
String groupID,
|
||||||
|
V2TimGroupMemberInfo member,
|
||||||
|
) {
|
||||||
|
UiTools.toast('onMemberLeave');
|
||||||
|
},
|
||||||
|
onMemberInvited: (
|
||||||
|
String groupID,
|
||||||
|
V2TimGroupMemberInfo opUser,
|
||||||
|
List<V2TimGroupMemberInfo> memberList,
|
||||||
|
) {
|
||||||
|
UiTools.toast('onMemberInvited');
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 有用户被移出群聊,判断是否当前用户,当前会话,关闭会话
|
||||||
|
onMemberKicked: (
|
||||||
|
String groupID,
|
||||||
|
V2TimGroupMemberInfo opUser,
|
||||||
|
List<V2TimGroupMemberInfo> memberList,
|
||||||
|
) {
|
||||||
|
bool isYou = memberList
|
||||||
|
.where((e) => true
|
||||||
|
// e.userID ==
|
||||||
|
// UserController.to.userInfo.value!.userId.toString(),
|
||||||
|
)
|
||||||
|
.isNotEmpty;
|
||||||
|
if (isYou) {
|
||||||
|
if (currentConversationId.value == 'group_' + groupID) {
|
||||||
|
UiTools.toast('您已被${opUser.nickName}移出当前群聊');
|
||||||
|
Navigator.popUntil(Get.context!, (route) => route.isFirst);
|
||||||
|
}
|
||||||
|
TimConversationService.to.deleteById(
|
||||||
|
'group_' + groupID,
|
||||||
|
);
|
||||||
|
TimGroupService.to.fetchList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMemberInfoChanged: (
|
||||||
|
String groupID,
|
||||||
|
List<V2TimGroupMemberChangeInfo> v2TIMGroupMemberChangeInfoList,
|
||||||
|
) {
|
||||||
|
UiTools.toast('onMemberInfoChanged');
|
||||||
|
},
|
||||||
|
onGroupRecycled: (
|
||||||
|
String groupID,
|
||||||
|
V2TimGroupMemberInfo opUser,
|
||||||
|
) {
|
||||||
|
UiTools.toast('onGroupRecycled');
|
||||||
|
},
|
||||||
|
onReceiveJoinApplication: (
|
||||||
|
String groupID,
|
||||||
|
V2TimGroupMemberInfo member,
|
||||||
|
String opReason,
|
||||||
|
) {
|
||||||
|
UiTools.toast('onReceiveJoinApplication');
|
||||||
|
},
|
||||||
|
onApplicationProcessed: (
|
||||||
|
String groupID,
|
||||||
|
V2TimGroupMemberInfo opUser,
|
||||||
|
bool isAgreeJoin,
|
||||||
|
String opReason,
|
||||||
|
) {
|
||||||
|
UiTools.toast('onApplicationProcessed');
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 有新的管理被授权
|
||||||
|
onGrantAdministrator: (
|
||||||
|
String groupID,
|
||||||
|
V2TimGroupMemberInfo opUser,
|
||||||
|
List<V2TimGroupMemberInfo> memberList,
|
||||||
|
) {
|
||||||
|
if (currentConversationId.value == 'group_' + groupID) {
|
||||||
|
GroupController.to.fetchGroupMemberList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 取消管理员
|
||||||
|
onRevokeAdministrator: (
|
||||||
|
String groupID,
|
||||||
|
V2TimGroupMemberInfo opUser,
|
||||||
|
List<V2TimGroupMemberInfo> memberList,
|
||||||
|
) {
|
||||||
|
if (currentConversationId.value == 'group_' + groupID) {
|
||||||
|
GroupController.to.fetchGroupMemberList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onQuitFromGroup: (
|
||||||
|
String groupID,
|
||||||
|
) {
|
||||||
|
UiTools.toast('onQuitFromGroup');
|
||||||
|
},
|
||||||
|
onReceiveRESTCustomData: (
|
||||||
|
String groupID,
|
||||||
|
String customData,
|
||||||
|
) {
|
||||||
|
UiTools.toast('onReceiveRESTCustomData');
|
||||||
|
},
|
||||||
|
onGroupAttributeChanged: (
|
||||||
|
String groupID,
|
||||||
|
Map<String, String> groupAttributeMap,
|
||||||
|
) {
|
||||||
|
UiTools.toast('onGroupAttributeChanged');
|
||||||
|
},
|
||||||
|
onTopicCreated: (
|
||||||
|
String groupID,
|
||||||
|
String topicID,
|
||||||
|
) {
|
||||||
|
UiTools.toast('onTopicCreated');
|
||||||
|
},
|
||||||
|
onTopicDeleted: (
|
||||||
|
String groupID,
|
||||||
|
List<String> topicIDList,
|
||||||
|
) {
|
||||||
|
UiTools.toast('onTopicDeleted');
|
||||||
|
},
|
||||||
|
onTopicInfoChanged: (
|
||||||
|
String groupID,
|
||||||
|
V2TimTopicInfo topicInfo,
|
||||||
|
) {
|
||||||
|
UiTools.toast('onTopicInfoChanged');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 有新消息的事件处理
|
||||||
|
Future<void> onRecvNewMessage(V2TimMessage message) async {
|
||||||
|
/// 过滤自定义消息中,用户输入状态的消息
|
||||||
|
if (message.elemType == MessageElemType.V2TIM_ELEM_TYPE_CUSTOM) {
|
||||||
|
var msgData = json.decode(message.customElem!.data!);
|
||||||
|
|
||||||
|
if (msgData['businessID'] == CustomMessageType.TYPING_STATUS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var conversation = await TimConversationService.to.getById(
|
||||||
|
getConversationIdByMessage(message),
|
||||||
|
);
|
||||||
|
if (conversation.recvOpt == 0) {
|
||||||
|
/// 振动提醒
|
||||||
|
Vibration.vibrate(duration: 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isMessageInCurrentConversation(message)) {
|
||||||
|
TimConversationService.to.markAsRead(conversation);
|
||||||
|
} else {
|
||||||
|
await TimConversationService.to.getUnreadCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 更新会话列表做了个延迟,要不然列表中的未读消息数量,不正确
|
||||||
|
Future.delayed(const Duration(milliseconds: 500), () async {
|
||||||
|
await TimConversationService.to.fetchList();
|
||||||
|
});
|
||||||
|
|
||||||
|
// eventBus.fire(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 通过消息判断是否是当前会话
|
||||||
|
bool _isMessageInCurrentConversation(V2TimMessage message) {
|
||||||
|
return getConversationIdByMessage(message) == currentConversationId.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 通过消息获取会话ID
|
||||||
|
String getConversationIdByMessage(V2TimMessage message) {
|
||||||
|
String conId;
|
||||||
|
if (message.groupID != null) {
|
||||||
|
conId = 'group_' + message.groupID!;
|
||||||
|
} else {
|
||||||
|
conId = 'c2c_' + message.userID!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
lib/utils/convert.dart
Normal file
30
lib/utils/convert.dart
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import 'package:dart_date/dart_date.dart';
|
||||||
|
|
||||||
|
class Convert {
|
||||||
|
/// 隐藏字符串中间位
|
||||||
|
///
|
||||||
|
/// * [str] 要处理的字符串
|
||||||
|
/// * [start] 字符串从0开始保留位数
|
||||||
|
/// * [end] 末尾保留的长度
|
||||||
|
static String hideCenterStr(
|
||||||
|
String str, {
|
||||||
|
int start = 8,
|
||||||
|
int end = 6,
|
||||||
|
}) {
|
||||||
|
if (str.length <= start + end) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
return '${str.substring(0, start)}...${str.substring(str.length - end)}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 时间戳转日期
|
||||||
|
///
|
||||||
|
/// [timestamp] 要转换的时间戳
|
||||||
|
/// [format] 日期格式
|
||||||
|
static String timeFormat(
|
||||||
|
int timestamp, {
|
||||||
|
String format = 'yyyy-MM-dd HH:mm:ss',
|
||||||
|
}) {
|
||||||
|
return DateTime.fromMillisecondsSinceEpoch(timestamp * 1000).format(format);
|
||||||
|
}
|
||||||
|
}
|
||||||
251
lib/utils/im_tools.dart
Normal file
251
lib/utils/im_tools.dart
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:azlistview/azlistview.dart';
|
||||||
|
import 'package:chat/configs/app_colors.dart';
|
||||||
|
import 'package:chat/models/im/custom_message_model.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/group_change_info_type.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/group_tips_elem_type.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/message_elem_type.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_info.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_info_result.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_message.dart';
|
||||||
|
|
||||||
|
class ImTools {
|
||||||
|
static String parseNicknameFromResult(V2TimFriendInfoResult? infoResult) {
|
||||||
|
if (infoResult == null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (infoResult.friendInfo!.friendRemark != null) {
|
||||||
|
if (infoResult.friendInfo!.friendRemark! == '') {
|
||||||
|
return infoResult.friendInfo!.userProfile!.nickName!;
|
||||||
|
} else {
|
||||||
|
return infoResult.friendInfo!.friendRemark!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return infoResult.friendInfo!.userProfile!.nickName!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String parseNicknameFromInfo(V2TimFriendInfo infoResult) {
|
||||||
|
if (infoResult.friendRemark != '') {
|
||||||
|
return infoResult.friendRemark!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return infoResult.userProfile!.nickName!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static parseMessage(V2TimMessage? message) {
|
||||||
|
String text = '';
|
||||||
|
switch (message?.elemType) {
|
||||||
|
case MessageElemType.V2TIM_ELEM_TYPE_TEXT:
|
||||||
|
text = message!.textElem!.text!;
|
||||||
|
break;
|
||||||
|
case MessageElemType.V2TIM_ELEM_TYPE_IMAGE:
|
||||||
|
text = '[图片]';
|
||||||
|
break;
|
||||||
|
case MessageElemType.V2TIM_ELEM_TYPE_SOUND:
|
||||||
|
text = '[语音]';
|
||||||
|
break;
|
||||||
|
case MessageElemType.V2TIM_ELEM_TYPE_VIDEO:
|
||||||
|
text = '[视频]';
|
||||||
|
break;
|
||||||
|
case MessageElemType.V2TIM_ELEM_TYPE_FILE:
|
||||||
|
text = '[文件]';
|
||||||
|
break;
|
||||||
|
case MessageElemType.V2TIM_ELEM_TYPE_LOCATION:
|
||||||
|
text = '[位置]';
|
||||||
|
break;
|
||||||
|
case MessageElemType.V2TIM_ELEM_TYPE_FACE:
|
||||||
|
text = '[表情]';
|
||||||
|
break;
|
||||||
|
case MessageElemType.V2TIM_ELEM_TYPE_MERGER:
|
||||||
|
text = '[聊天记录]';
|
||||||
|
break;
|
||||||
|
case MessageElemType.V2TIM_ELEM_TYPE_CUSTOM:
|
||||||
|
var data = json.decode(message!.customElem!.data!);
|
||||||
|
|
||||||
|
switch (data['businessID']) {
|
||||||
|
case CustomMessageType.CALL:
|
||||||
|
text = '通话';
|
||||||
|
break;
|
||||||
|
case CustomMessageType.DT_TRANSFER:
|
||||||
|
text = '[转账]';
|
||||||
|
break;
|
||||||
|
case CustomMessageType.NAME_CARD:
|
||||||
|
text = '[名片]';
|
||||||
|
break;
|
||||||
|
case CustomMessageType.GROUP_CARD:
|
||||||
|
text = '[群名片]';
|
||||||
|
break;
|
||||||
|
case CustomMessageType.EVALUATION:
|
||||||
|
text = '[评价]';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
text = '[${data['businessID']}]';
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MessageElemType.V2TIM_ELEM_TYPE_GROUP_TIPS:
|
||||||
|
switch (message?.groupTipsElem!.type) {
|
||||||
|
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_JOIN:
|
||||||
|
text = '加入群聊';
|
||||||
|
break;
|
||||||
|
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_INVITE:
|
||||||
|
text = '邀请入群';
|
||||||
|
break;
|
||||||
|
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_QUIT:
|
||||||
|
text = '退出群聊';
|
||||||
|
break;
|
||||||
|
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_KICKED:
|
||||||
|
text = '踢出群聊';
|
||||||
|
break;
|
||||||
|
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_SET_ADMIN:
|
||||||
|
text = '设置管理';
|
||||||
|
break;
|
||||||
|
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_CANCEL_ADMIN:
|
||||||
|
text = '取消管理';
|
||||||
|
break;
|
||||||
|
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_GROUP_INFO_CHANGE:
|
||||||
|
// (opMember 修改群资料:groupName & introduction & notification & faceUrl & owner & custom)
|
||||||
|
switch (message!.groupTipsElem!.groupChangeInfoList![0]!.type) {
|
||||||
|
case GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_NAME:
|
||||||
|
text = '修改群名称';
|
||||||
|
break;
|
||||||
|
case GroupChangeInfoType
|
||||||
|
.V2TIM_GROUP_INFO_CHANGE_TYPE_INTRODUCTION:
|
||||||
|
text = '群简介修改';
|
||||||
|
break;
|
||||||
|
case GroupChangeInfoType
|
||||||
|
.V2TIM_GROUP_INFO_CHANGE_TYPE_NOTIFICATION:
|
||||||
|
text = '修改群公告';
|
||||||
|
break;
|
||||||
|
case GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_FACE_URL:
|
||||||
|
text = '群头像修改';
|
||||||
|
break;
|
||||||
|
case GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_OWNER:
|
||||||
|
text = '群主变更';
|
||||||
|
break;
|
||||||
|
case GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_CUSTOM:
|
||||||
|
text = '群自定义字段变更';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
text = message.groupTipsElem!.groupChangeInfoList![0]!.type
|
||||||
|
.toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_MEMBER_INFO_CHANGE:
|
||||||
|
text = '群成员资料变更';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
text = message!.groupTipsElem!.type.toString();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
text = message!.elemType.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 右侧索引栏样式
|
||||||
|
static const IndexBarOptions indexBarOptions = IndexBarOptions(
|
||||||
|
needRebuild: true,
|
||||||
|
ignoreDragCancel: true,
|
||||||
|
downTextStyle: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
downItemDecoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: AppColors.primary,
|
||||||
|
),
|
||||||
|
indexHintWidth: 120 / 2,
|
||||||
|
indexHintHeight: 100 / 2,
|
||||||
|
indexHintDecoration: BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage(
|
||||||
|
'assets/chats/index_bar.png',
|
||||||
|
),
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
indexHintAlignment: Alignment.centerRight,
|
||||||
|
indexHintChildAlignment: Alignment(
|
||||||
|
-0.25,
|
||||||
|
0.0,
|
||||||
|
),
|
||||||
|
indexHintOffset: Offset(
|
||||||
|
-20,
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// 悬浮导航,显示ABCD~Z
|
||||||
|
static Widget susItem(
|
||||||
|
BuildContext context,
|
||||||
|
String tag, {
|
||||||
|
double susHeight = 40,
|
||||||
|
}) {
|
||||||
|
return Container(
|
||||||
|
height: susHeight,
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 16.0,
|
||||||
|
),
|
||||||
|
color: Colors.grey[200],
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(
|
||||||
|
tag,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static showTrtcMessage(String userID) {
|
||||||
|
return showModalBottomSheet(
|
||||||
|
context: Get.context!,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: AppColors.white,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(8)),
|
||||||
|
),
|
||||||
|
builder: (context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// PopMenuItem(
|
||||||
|
// '语音通话',
|
||||||
|
// onTap: () {
|
||||||
|
// TimService.to.tuiCalling.call(userID, CallingScenes.Audio);
|
||||||
|
// Get.back();
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// const Divider(height: 0),
|
||||||
|
// PopMenuItem(
|
||||||
|
// '视频通话',
|
||||||
|
// onTap: () {
|
||||||
|
// TimService.to.tuiCalling.call(userID, CallingScenes.Video);
|
||||||
|
// Get.back();
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
const Divider(height: 0),
|
||||||
|
Container(
|
||||||
|
height: 8,
|
||||||
|
color: AppColors.page,
|
||||||
|
),
|
||||||
|
const Divider(height: 0),
|
||||||
|
// PopMenuItem(
|
||||||
|
// '取消',
|
||||||
|
// onTap: () {
|
||||||
|
// Get.back();
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
121
lib/utils/request/http.dart
Normal file
121
lib/utils/request/http.dart
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import 'package:chat/utils/request/http_request.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
|
class Http {
|
||||||
|
/// 取消请求
|
||||||
|
static void cancelRequests({
|
||||||
|
required CancelToken token,
|
||||||
|
}) {
|
||||||
|
HttpRequest().cancelRequests(
|
||||||
|
token: token,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GET 请求
|
||||||
|
static Future<T> get<T>(
|
||||||
|
String path, {
|
||||||
|
Map<String, dynamic>? params,
|
||||||
|
Options? options,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
ProgressCallback? onReceiveProgress,
|
||||||
|
}) async {
|
||||||
|
return await HttpRequest().request(
|
||||||
|
path,
|
||||||
|
method: HttpMethod.get,
|
||||||
|
params: params,
|
||||||
|
options: options,
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// POST 请求
|
||||||
|
static Future<T> post<T>(
|
||||||
|
String path, {
|
||||||
|
Map<String, dynamic>? params,
|
||||||
|
dynamic data,
|
||||||
|
Options? options,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
ProgressCallback? onSendProgress,
|
||||||
|
ProgressCallback? onReceiveProgress,
|
||||||
|
}) async {
|
||||||
|
return await HttpRequest().request(
|
||||||
|
path,
|
||||||
|
method: HttpMethod.post,
|
||||||
|
params: params,
|
||||||
|
data: data,
|
||||||
|
options: options,
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
onReceiveProgress: onReceiveProgress,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PUT
|
||||||
|
static Future<T> put<T>(
|
||||||
|
String path, {
|
||||||
|
Map<String, dynamic>? params,
|
||||||
|
dynamic data,
|
||||||
|
Options? options,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
ProgressCallback? onSendProgress,
|
||||||
|
ProgressCallback? onReceiveProgress,
|
||||||
|
}) async {
|
||||||
|
return await HttpRequest().request(
|
||||||
|
path,
|
||||||
|
method: HttpMethod.put,
|
||||||
|
params: params,
|
||||||
|
data: data,
|
||||||
|
options: options,
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
onSendProgress: onSendProgress,
|
||||||
|
onReceiveProgress: onReceiveProgress,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DELETE
|
||||||
|
static Future<T> delete<T>(
|
||||||
|
String path, {
|
||||||
|
Map<String, dynamic>? params,
|
||||||
|
dynamic data,
|
||||||
|
Options? options,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
ProgressCallback? onSendProgress,
|
||||||
|
ProgressCallback? onReceiveProgress,
|
||||||
|
}) async {
|
||||||
|
return await HttpRequest().request(
|
||||||
|
path,
|
||||||
|
method: HttpMethod.delete,
|
||||||
|
params: params,
|
||||||
|
data: data,
|
||||||
|
options: options,
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
onSendProgress: onSendProgress,
|
||||||
|
onReceiveProgress: onReceiveProgress,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 上传文件
|
||||||
|
static Future<T> upload<T>(
|
||||||
|
String path, {
|
||||||
|
required String filePath,
|
||||||
|
Map<String, dynamic>? params,
|
||||||
|
Options? options,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
ProgressCallback? onSendProgress,
|
||||||
|
ProgressCallback? onReceiveProgress,
|
||||||
|
}) async {
|
||||||
|
var formData = FormData.fromMap({
|
||||||
|
'upload': await MultipartFile.fromFile(filePath),
|
||||||
|
});
|
||||||
|
|
||||||
|
return await HttpRequest().request(
|
||||||
|
path,
|
||||||
|
method: HttpMethod.post,
|
||||||
|
params: params,
|
||||||
|
data: formData,
|
||||||
|
options: options,
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
onSendProgress: onSendProgress,
|
||||||
|
onReceiveProgress: onReceiveProgress,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
97
lib/utils/request/http_interceptor.dart
Normal file
97
lib/utils/request/http_interceptor.dart
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import 'package:chat/services/auth_service.dart';
|
||||||
|
import 'package:chat/utils/ui_tools.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
|
class HttpInterceptor extends Interceptor {
|
||||||
|
@override
|
||||||
|
void onRequest(
|
||||||
|
RequestOptions options,
|
||||||
|
RequestInterceptorHandler handler,
|
||||||
|
) {
|
||||||
|
// 头部添加token
|
||||||
|
// options.headers['Authorization'] = AuthService.to.userToken;
|
||||||
|
options.headers['Accept'] = 'application/json';
|
||||||
|
|
||||||
|
super.onRequest(
|
||||||
|
options,
|
||||||
|
handler,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onResponse(
|
||||||
|
Response response,
|
||||||
|
ResponseInterceptorHandler handler,
|
||||||
|
) {
|
||||||
|
final apiStatusCode = response.data['status_code'];
|
||||||
|
|
||||||
|
if (apiStatusCode == 200) {
|
||||||
|
response.data = response.data['data'];
|
||||||
|
} else if (apiStatusCode == 401) {
|
||||||
|
throw DioError(
|
||||||
|
response: response,
|
||||||
|
error: "登录超时",
|
||||||
|
requestOptions: response.requestOptions,
|
||||||
|
);
|
||||||
|
} else if (apiStatusCode == 404) {
|
||||||
|
throw DioError(
|
||||||
|
response: response,
|
||||||
|
error: "请求的接口不存在",
|
||||||
|
requestOptions: response.requestOptions,
|
||||||
|
);
|
||||||
|
} else if (apiStatusCode == 0) {
|
||||||
|
throw DioError(
|
||||||
|
response: response,
|
||||||
|
error: response.data['message'],
|
||||||
|
requestOptions: response.requestOptions,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw DioError(
|
||||||
|
response: response,
|
||||||
|
error: response.data['message'],
|
||||||
|
requestOptions: response.requestOptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
super.onResponse(
|
||||||
|
response,
|
||||||
|
handler,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onError(
|
||||||
|
DioError err,
|
||||||
|
ErrorInterceptorHandler handler,
|
||||||
|
) {
|
||||||
|
switch (err.type) {
|
||||||
|
// 连接服务器超时
|
||||||
|
case DioErrorType.connectTimeout:
|
||||||
|
UiTools.toast('网络传输超时');
|
||||||
|
break;
|
||||||
|
// 响应超时
|
||||||
|
case DioErrorType.receiveTimeout:
|
||||||
|
UiTools.toast('网络传输超时');
|
||||||
|
break;
|
||||||
|
// 发送超时
|
||||||
|
case DioErrorType.sendTimeout:
|
||||||
|
UiTools.toast('网络传输超时');
|
||||||
|
break;
|
||||||
|
// 请求取消
|
||||||
|
case DioErrorType.cancel:
|
||||||
|
break;
|
||||||
|
// 404/503错误
|
||||||
|
case DioErrorType.response:
|
||||||
|
break;
|
||||||
|
// other 其他错误类型
|
||||||
|
case DioErrorType.other:
|
||||||
|
if (err.response?.data['status_code'] == 401) {
|
||||||
|
AuthService.to.logout();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
super.onError(
|
||||||
|
err,
|
||||||
|
handler,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
lib/utils/request/http_options.dart
Normal file
5
lib/utils/request/http_options.dart
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
class HttpOptions {
|
||||||
|
static const String baseUrl = 'http://api.gl.shangkelian.cn/api/';
|
||||||
|
static const int connectTimeout = 15000;
|
||||||
|
static const int receiveTimeout = 15000;
|
||||||
|
}
|
||||||
91
lib/utils/request/http_request.dart
Normal file
91
lib/utils/request/http_request.dart
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import 'package:chat/utils/request/http_interceptor.dart';
|
||||||
|
import 'package:chat/utils/request/http_options.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
|
enum HttpMethod {
|
||||||
|
get,
|
||||||
|
post,
|
||||||
|
put,
|
||||||
|
delete,
|
||||||
|
patch,
|
||||||
|
head,
|
||||||
|
}
|
||||||
|
|
||||||
|
class HttpRequest {
|
||||||
|
static HttpRequest? _instance;
|
||||||
|
static Dio _dio = Dio();
|
||||||
|
Dio get dio => _dio;
|
||||||
|
|
||||||
|
HttpRequest._internal() {
|
||||||
|
_instance = this;
|
||||||
|
_instance!._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
factory HttpRequest() => _instance ?? HttpRequest._internal();
|
||||||
|
|
||||||
|
static HttpRequest? getInstance() {
|
||||||
|
return _instance ?? HttpRequest._internal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 取消请求token
|
||||||
|
final CancelToken _cancelToken = CancelToken();
|
||||||
|
|
||||||
|
_init() {
|
||||||
|
BaseOptions options = BaseOptions(
|
||||||
|
baseUrl: HttpOptions.baseUrl,
|
||||||
|
connectTimeout: HttpOptions.connectTimeout,
|
||||||
|
receiveTimeout: HttpOptions.receiveTimeout,
|
||||||
|
);
|
||||||
|
_dio = Dio(options);
|
||||||
|
|
||||||
|
/// 添加拦截器
|
||||||
|
_dio.interceptors.add(HttpInterceptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 请求
|
||||||
|
Future request(
|
||||||
|
String path, {
|
||||||
|
HttpMethod method = HttpMethod.get,
|
||||||
|
Map<String, dynamic>? params,
|
||||||
|
dynamic data,
|
||||||
|
Options? options,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
ProgressCallback? onSendProgress,
|
||||||
|
ProgressCallback? onReceiveProgress,
|
||||||
|
}) async {
|
||||||
|
const methodValues = {
|
||||||
|
HttpMethod.get: 'get',
|
||||||
|
HttpMethod.post: 'post',
|
||||||
|
HttpMethod.put: 'put',
|
||||||
|
HttpMethod.delete: 'delete',
|
||||||
|
HttpMethod.patch: 'patch',
|
||||||
|
HttpMethod.head: 'head'
|
||||||
|
};
|
||||||
|
|
||||||
|
options ??= Options(method: methodValues[method]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Response response = await _dio.request(
|
||||||
|
path,
|
||||||
|
data: data,
|
||||||
|
queryParameters: params,
|
||||||
|
cancelToken: cancelToken ?? _cancelToken,
|
||||||
|
options: options,
|
||||||
|
onSendProgress: onSendProgress,
|
||||||
|
onReceiveProgress: onReceiveProgress,
|
||||||
|
);
|
||||||
|
// ignore: avoid_print
|
||||||
|
print('请求地址:${response.requestOptions.uri}');
|
||||||
|
// ignore: avoid_print
|
||||||
|
print('响应数据:${response.data}');
|
||||||
|
return response.data;
|
||||||
|
} on DioError catch (e) {
|
||||||
|
throw Exception(e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 取消网络请求
|
||||||
|
void cancelRequests({CancelToken? token}) {
|
||||||
|
token ?? _cancelToken.cancel('CANCELED');
|
||||||
|
}
|
||||||
|
}
|
||||||
15
lib/views/contact/group/create/index_page.dart
Normal file
15
lib/views/contact/group/create/index_page.dart
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ContactGroupCreatePage extends StatefulWidget {
|
||||||
|
const ContactGroupCreatePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ContactGroupCreatePageState createState() => _ContactGroupCreatePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ContactGroupCreatePageState extends State<ContactGroupCreatePage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
}
|
||||||
15
lib/views/contact/group/manage/index_page.dart
Normal file
15
lib/views/contact/group/manage/index_page.dart
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ContactGroupManagePage extends StatefulWidget {
|
||||||
|
const ContactGroupManagePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ContactGroupManagePage> createState() => _ContactGroupManagePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ContactGroupManagePageState extends State<ContactGroupManagePage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
}
|
||||||
17
lib/views/contact/group/notification/index_page.dart
Normal file
17
lib/views/contact/group/notification/index_page.dart
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ContactGroupNotificationPage extends StatefulWidget {
|
||||||
|
const ContactGroupNotificationPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ContactGroupNotificationPageState createState() =>
|
||||||
|
_ContactGroupNotificationPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ContactGroupNotificationPageState
|
||||||
|
extends State<ContactGroupNotificationPage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
}
|
||||||
82
lib/views/conversation/index_page.dart
Normal file
82
lib/views/conversation/index_page.dart
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import 'package:chat/controllers/group_controller.dart';
|
||||||
|
import 'package:chat/controllers/private_controller.dart';
|
||||||
|
import 'package:chat/routes/conversation_routes.dart';
|
||||||
|
import 'package:chat/services/tim/conversation_service.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/conversation_type.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_conversation.dart';
|
||||||
|
|
||||||
|
class ConversationPage extends StatefulWidget {
|
||||||
|
const ConversationPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ConversationPageState createState() => _ConversationPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ConversationPageState extends State<ConversationPage> {
|
||||||
|
late final V2TimConversation conversation;
|
||||||
|
final _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
final GlobalKey<dynamic> inputextField = GlobalKey();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
conversation = Get.arguments['conversation'];
|
||||||
|
|
||||||
|
/// 标记会话内消息已读
|
||||||
|
TimConversationService.to.markAsRead(conversation);
|
||||||
|
if (conversation.type == ConversationType.V2TIM_GROUP) {
|
||||||
|
GroupController.to.setCurrentGroup(conversation.groupID!);
|
||||||
|
} else {
|
||||||
|
PrivateController.to.setCurrentFriend(conversation.userID!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
inputextField.currentState.hideAllPanel();
|
||||||
|
},
|
||||||
|
child: Scaffold(
|
||||||
|
key: _scaffoldKey,
|
||||||
|
appBar: AppBar(
|
||||||
|
title: conversation.type == ConversationType.V2TIM_GROUP
|
||||||
|
? GetX<GroupController>(
|
||||||
|
builder: (_) {
|
||||||
|
return Text(
|
||||||
|
_.currentGroup.value.conversation?.showName ?? '',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: GetX<PrivateController>(
|
||||||
|
builder: (_) {
|
||||||
|
return Text(
|
||||||
|
_.currentFriend.value.conversation?.showName ?? '',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
_topRightAction(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _topRightAction() {
|
||||||
|
return IconButton(
|
||||||
|
icon: const Icon(Icons.more_horiz),
|
||||||
|
onPressed: () {
|
||||||
|
conversation.type == ConversationType.V2TIM_GROUP
|
||||||
|
? Get.toNamed(
|
||||||
|
ConversationRoutes.infoGroup,
|
||||||
|
)
|
||||||
|
: Get.toNamed(
|
||||||
|
ConversationRoutes.infoPrivate,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
235
lib/views/conversation/info/group_page.dart
Normal file
235
lib/views/conversation/info/group_page.dart
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
|
import 'package:chat/configs/app_colors.dart';
|
||||||
|
import 'package:chat/controllers/group_controller.dart';
|
||||||
|
import 'package:chat/routes/contact_routes.dart';
|
||||||
|
import 'package:chat/services/tim/conversation_service.dart';
|
||||||
|
import 'package:chat/services/tim/group_service.dart';
|
||||||
|
import 'package:chat/views/conversation/info/widgets/group_member_preview.dart';
|
||||||
|
import 'package:chat/views/home/widgets/action_button.dart';
|
||||||
|
import 'package:chat/views/home/widgets/action_item.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class ConversationInfoGroupPage extends StatelessWidget {
|
||||||
|
const ConversationInfoGroupPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
GroupController.to.fetchGroupMemberList();
|
||||||
|
|
||||||
|
return GetX<GroupController>(builder: (_) {
|
||||||
|
var currentGroup = _.currentGroup.value;
|
||||||
|
var group = _.currentGroup.value.group;
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('聊天信息(${group?.memberCount})'),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const GroupMemberPreview(),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Container(
|
||||||
|
color: AppColors.white,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ActionItem(
|
||||||
|
'群聊名称',
|
||||||
|
extend: group?.groupName,
|
||||||
|
onTap: () {
|
||||||
|
if (currentGroup.isAdmin || currentGroup.isOwner) {
|
||||||
|
// Get.toNamed(
|
||||||
|
// ImRoutes.groupName,
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 0, indent: 16),
|
||||||
|
ActionItem(
|
||||||
|
'群二维码',
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed(
|
||||||
|
ContactRoutes.groupQrCode,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 0, indent: 16),
|
||||||
|
ActionItem(
|
||||||
|
'群公告',
|
||||||
|
bottom: group?.notification,
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed(
|
||||||
|
ContactRoutes.groupNotification,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: currentGroup.isAdmin || currentGroup.isOwner,
|
||||||
|
child: const Divider(
|
||||||
|
height: 0,
|
||||||
|
indent: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: currentGroup.isAdmin || currentGroup.isOwner,
|
||||||
|
child: ActionItem(
|
||||||
|
'群管理',
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed(
|
||||||
|
ContactRoutes.groupManage,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: currentGroup.isAdmin || currentGroup.isOwner,
|
||||||
|
child: const Divider(
|
||||||
|
height: 0,
|
||||||
|
indent: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: currentGroup.isAdmin || currentGroup.isOwner,
|
||||||
|
child: ActionItem(
|
||||||
|
'加群申请',
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed(
|
||||||
|
ContactRoutes.groupApprove,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Container(
|
||||||
|
color: AppColors.white,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ActionItem(
|
||||||
|
'查找聊天记录',
|
||||||
|
onTap: () {
|
||||||
|
// Get.toNamed(
|
||||||
|
// ImRoutes.conversationSearch,
|
||||||
|
// );
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Container(
|
||||||
|
color: AppColors.white,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ActionItem(
|
||||||
|
'消息免打扰',
|
||||||
|
rightWidget: SizedBox(
|
||||||
|
height: 24,
|
||||||
|
child: Switch(
|
||||||
|
value:
|
||||||
|
_.currentGroup.value.conversation!.recvOpt == 1,
|
||||||
|
onChanged: (e) async {
|
||||||
|
_.toggleReceiveOpt();
|
||||||
|
},
|
||||||
|
materialTapTargetSize:
|
||||||
|
MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
height: 0,
|
||||||
|
indent: 16,
|
||||||
|
),
|
||||||
|
ActionItem(
|
||||||
|
'置顶聊天',
|
||||||
|
rightWidget: SizedBox(
|
||||||
|
height: 24,
|
||||||
|
child: Switch(
|
||||||
|
value: _.currentGroup.value.conversation!.isPinned!,
|
||||||
|
onChanged: (e) async {
|
||||||
|
_.togglePinned();
|
||||||
|
},
|
||||||
|
materialTapTargetSize:
|
||||||
|
MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Container(
|
||||||
|
color: AppColors.white,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ActionItem(
|
||||||
|
'我在群里的昵称',
|
||||||
|
extend: _.currentGroup.value.selfInfo?.nameCard ?? '',
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed(
|
||||||
|
ContactRoutes.groupNickname,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
ActionButton(
|
||||||
|
'清空聊天记录',
|
||||||
|
onTap: () async {
|
||||||
|
OkCancelResult result = await showOkCancelAlertDialog(
|
||||||
|
style: AdaptiveStyle.iOS,
|
||||||
|
context: Get.context!,
|
||||||
|
title: '系统提示',
|
||||||
|
message: '将删除该聊天记录,是否继续?',
|
||||||
|
okLabel: '确定',
|
||||||
|
cancelLabel: '取消',
|
||||||
|
defaultType: OkCancelAlertDefaultType.ok,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == OkCancelResult.ok) {
|
||||||
|
TimConversationService.to.clearHistoryMessage(
|
||||||
|
_.currentGroup.value.conversation!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (!currentGroup.isOwner) const Divider(height: 0),
|
||||||
|
ActionButton(
|
||||||
|
'删除并退出',
|
||||||
|
onTap: () async {
|
||||||
|
OkCancelResult result = await showOkCancelAlertDialog(
|
||||||
|
style: AdaptiveStyle.iOS,
|
||||||
|
context: Get.context!,
|
||||||
|
title: '系统提示',
|
||||||
|
message: '将删除并退出该群组,是否继续?',
|
||||||
|
okLabel: '确定',
|
||||||
|
cancelLabel: '取消',
|
||||||
|
defaultType: OkCancelAlertDefaultType.ok,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == OkCancelResult.ok) {
|
||||||
|
TimGroupService.to.quit(
|
||||||
|
group!,
|
||||||
|
);
|
||||||
|
Navigator.popUntil(context, (route) => route.isFirst);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
157
lib/views/conversation/info/private_page.dart
Normal file
157
lib/views/conversation/info/private_page.dart
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
|
import 'package:chat/configs/app_colors.dart';
|
||||||
|
import 'package:chat/controllers/private_controller.dart';
|
||||||
|
import 'package:chat/routes/contact_routes.dart';
|
||||||
|
import 'package:chat/services/tim/conversation_service.dart';
|
||||||
|
import 'package:chat/views/home/widgets/action_item.dart';
|
||||||
|
import 'package:chat/widgets/custom_avatar.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class ConversationInfoPrivatePage extends StatelessWidget {
|
||||||
|
const ConversationInfoPrivatePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GetX<PrivateController>(builder: (_) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('聊天信息'),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: const BoxDecoration(color: AppColors.white),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed(
|
||||||
|
ContactRoutes.friendProfile,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
CustomAvatar(
|
||||||
|
_.currentFriend.value.userProfile?.faceUrl,
|
||||||
|
size: 54,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
_.currentFriend.value.conversation!.showName!,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: AppColors.unactive,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed(
|
||||||
|
ContactRoutes.groupCreate,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 54,
|
||||||
|
height: 54,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.unactive.withOpacity(0.1),
|
||||||
|
border: Border.all(
|
||||||
|
color: AppColors.unactive.withOpacity(0.3),
|
||||||
|
width: 0.4,
|
||||||
|
style: BorderStyle.solid,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: const Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.add,
|
||||||
|
color: AppColors.unactive,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(height: 0),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Divider(height: 0),
|
||||||
|
ActionItem(
|
||||||
|
'查找聊天记录',
|
||||||
|
onTap: () {
|
||||||
|
// Get.toNamed(
|
||||||
|
// ImRoutes.conversationSearch,
|
||||||
|
// );
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 0),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Divider(height: 0),
|
||||||
|
ActionItem(
|
||||||
|
'消息免打扰',
|
||||||
|
rightWidget: SizedBox(
|
||||||
|
height: 24,
|
||||||
|
child: Switch(
|
||||||
|
value: _.currentFriend.value.conversation!.recvOpt == 1,
|
||||||
|
onChanged: (e) async {
|
||||||
|
_.changeReceiveOpt();
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
height: 0,
|
||||||
|
indent: 16,
|
||||||
|
),
|
||||||
|
ActionItem(
|
||||||
|
'置顶聊天',
|
||||||
|
rightWidget: SizedBox(
|
||||||
|
height: 24,
|
||||||
|
child: Switch(
|
||||||
|
value: _.currentFriend.value.conversation!.isPinned!,
|
||||||
|
onChanged: (e) async {
|
||||||
|
_.togglePinned();
|
||||||
|
},
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(height: 0),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Divider(height: 0),
|
||||||
|
ActionItem(
|
||||||
|
'清空聊天记录',
|
||||||
|
onTap: () async {
|
||||||
|
OkCancelResult result = await showOkCancelAlertDialog(
|
||||||
|
style: AdaptiveStyle.iOS,
|
||||||
|
context: Get.context!,
|
||||||
|
title: '系统提示',
|
||||||
|
message: '将删除该聊天记录,是否继续?',
|
||||||
|
okLabel: '确定',
|
||||||
|
cancelLabel: '取消',
|
||||||
|
defaultType: OkCancelAlertDefaultType.ok,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == OkCancelResult.ok) {
|
||||||
|
TimConversationService.to.clearHistoryMessage(
|
||||||
|
_.currentFriend.value.conversation!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 0),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
209
lib/views/conversation/info/widgets/group_member_preview.dart
Normal file
209
lib/views/conversation/info/widgets/group_member_preview.dart
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
|
import 'package:chat/configs/app_colors.dart';
|
||||||
|
import 'package:chat/controllers/group_controller.dart';
|
||||||
|
import 'package:chat/controllers/private_controller.dart';
|
||||||
|
import 'package:chat/routes/contact_routes.dart';
|
||||||
|
import 'package:chat/widgets/custom_avatar.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/group_member_role.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_member_full_info.dart';
|
||||||
|
|
||||||
|
class GroupMemberPreview extends StatelessWidget {
|
||||||
|
const GroupMemberPreview({
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GetX<GroupController>(builder: (_) {
|
||||||
|
List<Widget> items = List<Widget>.empty(growable: true);
|
||||||
|
|
||||||
|
if (_.currentGroup.value.memberList != null) {
|
||||||
|
var members = _.currentGroup.value.memberList!;
|
||||||
|
|
||||||
|
if (members.length > 13) {
|
||||||
|
members = members.sublist(0, 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var item in members) {
|
||||||
|
items.add(_memberItem(item!));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 因为 Public 类型的群,不支持邀请功能,用户只能主动申请加群
|
||||||
|
items.add(
|
||||||
|
InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
OkCancelResult result = await showOkCancelAlertDialog(
|
||||||
|
style: AdaptiveStyle.iOS,
|
||||||
|
context: Get.context!,
|
||||||
|
title: '系统提示',
|
||||||
|
message: '当前群聊不支持邀请用户,请分享群二维码至您要邀请的好友。',
|
||||||
|
okLabel: '去分享',
|
||||||
|
cancelLabel: '取消',
|
||||||
|
defaultType: OkCancelAlertDefaultType.ok,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == OkCancelResult.ok) {
|
||||||
|
Get.toNamed(ContactRoutes.groupQrCode);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.unactive.withOpacity(0.1),
|
||||||
|
border: Border.all(
|
||||||
|
color: AppColors.unactive.withOpacity(0.3),
|
||||||
|
width: 0.4,
|
||||||
|
style: BorderStyle.solid,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
child: const Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.add,
|
||||||
|
color: AppColors.unactive,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
'邀请',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: AppColors.unactive,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_.currentGroup.value.isAdmin || _.currentGroup.value.isOwner) {
|
||||||
|
items.add(
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed(
|
||||||
|
ContactRoutes.groupKick,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.unactive.withOpacity(0.1),
|
||||||
|
border: Border.all(
|
||||||
|
color: AppColors.unactive.withOpacity(0.3),
|
||||||
|
width: 0.4,
|
||||||
|
style: BorderStyle.solid,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
child: const Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.remove,
|
||||||
|
color: AppColors.unactive,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
'移除',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: AppColors.unactive,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
color: AppColors.white,
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: GridView.count(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
crossAxisCount: 5,
|
||||||
|
childAspectRatio: 1 / 1.1,
|
||||||
|
crossAxisSpacing: 12,
|
||||||
|
mainAxisSpacing: 12,
|
||||||
|
children: items,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _memberItem(V2TimGroupMemberFullInfo member) {
|
||||||
|
double w = 40;
|
||||||
|
double h = 12;
|
||||||
|
return InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
await PrivateController.to.setCurrentFriend(member.userID);
|
||||||
|
Get.toNamed(
|
||||||
|
ContactRoutes.friendProfile,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
CustomAvatar(member.faceUrl),
|
||||||
|
if (member.role ==
|
||||||
|
GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN ||
|
||||||
|
member.role ==
|
||||||
|
GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER)
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
top: sqrt(w * w / 2 - sqrt2 * w * h + h * h),
|
||||||
|
child: Transform.rotate(
|
||||||
|
angle: -0.25 * pi,
|
||||||
|
alignment: Alignment.bottomLeft,
|
||||||
|
child: Container(
|
||||||
|
color: member.role ==
|
||||||
|
GroupMemberRoleType
|
||||||
|
.V2TIM_GROUP_MEMBER_ROLE_OWNER
|
||||||
|
? AppColors.red
|
||||||
|
: AppColors.golden,
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
member.role ==
|
||||||
|
GroupMemberRoleType
|
||||||
|
.V2TIM_GROUP_MEMBER_ROLE_OWNER
|
||||||
|
? '群主'
|
||||||
|
: '管理员',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: AppColors.white,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(
|
||||||
|
member.nameCard!.isNotEmpty ? member.nameCard! : member.nickName!,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: AppColors.unactive,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,219 @@
|
|||||||
|
import 'package:chat/configs/app_colors.dart';
|
||||||
|
import 'package:chat/routes/app_routes.dart';
|
||||||
|
import 'package:chat/routes/contact_routes.dart';
|
||||||
|
import 'package:chat/routes/user_routes.dart';
|
||||||
|
import 'package:chat/services/tim/conversation_service.dart';
|
||||||
|
import 'package:chat/views/home/widgets/conversation_item.dart';
|
||||||
|
import 'package:chat/widgets/custom_easy_refresh.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_easyrefresh/easy_refresh.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatelessWidget {
|
||||||
const HomePage({Key? key}) : super(key: key);
|
const HomePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
|
||||||
_HomePageState createState() => _HomePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _HomePageState extends State<HomePage> {
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
backgroundColor: AppColors.white,
|
||||||
title: const Text('消息'),
|
appBar: _appBar(),
|
||||||
|
body: EasyRefresh(
|
||||||
|
header: CustomEasyRefresh.header,
|
||||||
|
// firstRefresh: true,
|
||||||
|
onRefresh: () async {
|
||||||
|
await TimConversationService.to.fetchList();
|
||||||
|
},
|
||||||
|
child: GetX<TimConversationService>(
|
||||||
|
builder: (_) {
|
||||||
|
return _.conversationList.isEmpty
|
||||||
|
? CustomEasyRefresh.empty()
|
||||||
|
: ListView.separated(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const ClampingScrollPhysics(),
|
||||||
|
itemCount: _.conversationList.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return ConversationItem(_.conversationList[index]!);
|
||||||
|
},
|
||||||
|
separatorBuilder: (context, index) {
|
||||||
|
return const Divider(
|
||||||
|
height: 0,
|
||||||
|
indent: 72,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PreferredSizeWidget _appBar() {
|
||||||
|
return AppBar(
|
||||||
|
title: const Text('聊聊'),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Get.toNamed(AppRoutes.search);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.search_outlined),
|
||||||
|
),
|
||||||
|
PopupMenuButton<String>(
|
||||||
|
onSelected: (String value) {
|
||||||
|
switch (value) {
|
||||||
|
case 'A':
|
||||||
|
Get.toNamed(UserRoutes.qrCode);
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
Get.toNamed(ContactRoutes.groupCreate);
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
Get.toNamed(ContactRoutes.friendSearch);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
Permission.camera.request().isGranted.then((value) {
|
||||||
|
if (value) {
|
||||||
|
Get.toNamed(AppRoutes.scan);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: '',
|
||||||
|
offset: const Offset(0, 56),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.add,
|
||||||
|
),
|
||||||
|
itemBuilder: (_) {
|
||||||
|
return [
|
||||||
|
_popupMenuItem('我的二维码', 'A', Icons.qr_code_outlined),
|
||||||
|
_popupMenuItem('发起群聊', 'B', Icons.textsms),
|
||||||
|
_popupMenuItem('添加朋友', 'C', Icons.person_add_alt),
|
||||||
|
_popupMenuItem('扫一扫', 'D', Icons.photo_camera),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 右上角弹出菜单
|
||||||
|
PopupMenuItem<String> _popupMenuItem(
|
||||||
|
String text,
|
||||||
|
String value,
|
||||||
|
IconData icon,
|
||||||
|
) {
|
||||||
|
return PopupMenuItem(
|
||||||
|
value: value,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
icon,
|
||||||
|
color: AppColors.primary,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
text,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// /// 左侧抽题
|
||||||
|
// Widget _drawer() {
|
||||||
|
// return Drawer(
|
||||||
|
// child: ListView(
|
||||||
|
// children: [
|
||||||
|
// GetX<UserController>(builder: (_) {
|
||||||
|
// return DrawerHeader(
|
||||||
|
// child: Column(
|
||||||
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
// children: [
|
||||||
|
// CustomCircleAvatar(
|
||||||
|
// _.userInfo.value!.avatar,
|
||||||
|
// size: 72,
|
||||||
|
// ),
|
||||||
|
// const SizedBox(height: 8),
|
||||||
|
// Text(
|
||||||
|
// _.userInfo.value!.nickname,
|
||||||
|
// style: const TextStyle(
|
||||||
|
// fontSize: 24,
|
||||||
|
// fontWeight: FontWeight.bold,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }),
|
||||||
|
// ListTile(
|
||||||
|
// onTap: () {
|
||||||
|
// Get.back();
|
||||||
|
// Get.toNamed(UserRoutes.info);
|
||||||
|
// },
|
||||||
|
// leading: const Icon(Icons.info_outlined),
|
||||||
|
// title: const Text('修改资料'),
|
||||||
|
// ),
|
||||||
|
// const Divider(height: 0),
|
||||||
|
// ListTile(
|
||||||
|
// onTap: () {
|
||||||
|
// Get.back();
|
||||||
|
// Get.toNamed(
|
||||||
|
// ImRoutes.friend,
|
||||||
|
// arguments: {
|
||||||
|
// 'name_card': false,
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// leading: const Icon(Icons.person_outlined),
|
||||||
|
// title: const Text('我的好友'),
|
||||||
|
// ),
|
||||||
|
// const Divider(height: 0),
|
||||||
|
// ListTile(
|
||||||
|
// onTap: () {
|
||||||
|
// Get.back();
|
||||||
|
// Get.toNamed(ImRoutes.group);
|
||||||
|
// },
|
||||||
|
// leading: const Icon(Icons.group_outlined),
|
||||||
|
// title: const Text('我的群组'),
|
||||||
|
// ),
|
||||||
|
// const Divider(height: 0),
|
||||||
|
// ListTile(
|
||||||
|
// onTap: () {
|
||||||
|
// Get.back();
|
||||||
|
// Get.toNamed(ImRoutes.blcok);
|
||||||
|
// },
|
||||||
|
// leading: const Icon(Icons.block_flipped),
|
||||||
|
// title: const Text('黑名单'),
|
||||||
|
// ),
|
||||||
|
// const Divider(height: 0),
|
||||||
|
// ListTile(
|
||||||
|
// onTap: () {
|
||||||
|
// Get.back();
|
||||||
|
// Get.toNamed(ImRoutes.friendRequest);
|
||||||
|
// },
|
||||||
|
// leading: const Icon(Icons.person_add_alt_outlined),
|
||||||
|
// title: const Text('好友申请'),
|
||||||
|
// ),
|
||||||
|
// const Divider(height: 0),
|
||||||
|
// ListTile(
|
||||||
|
// onTap: () {
|
||||||
|
// Get.back();
|
||||||
|
// Get.toNamed(ImRoutes.setting);
|
||||||
|
// },
|
||||||
|
// leading: const Icon(Icons.settings_outlined),
|
||||||
|
// title: const Text('消息设置'),
|
||||||
|
// ),
|
||||||
|
// const Divider(height: 0),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
39
lib/views/home/widgets/action_button.dart
Normal file
39
lib/views/home/widgets/action_button.dart
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import 'package:chat/configs/app_colors.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ActionButton extends StatelessWidget {
|
||||||
|
final String text;
|
||||||
|
final Color color;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
const ActionButton(
|
||||||
|
this.text, {
|
||||||
|
this.color = AppColors.red,
|
||||||
|
this.onTap,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
onTap?.call();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
color: AppColors.white,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
76
lib/views/home/widgets/action_item.dart
Normal file
76
lib/views/home/widgets/action_item.dart
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import 'package:chat/configs/app_colors.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ActionItem extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final String? extend;
|
||||||
|
final Widget? rightWidget;
|
||||||
|
final String? bottom;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
|
const ActionItem(
|
||||||
|
this.title, {
|
||||||
|
this.extend,
|
||||||
|
this.rightWidget,
|
||||||
|
this.bottom,
|
||||||
|
this.onTap,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
onTap?.call();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
color: AppColors.white,
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(child: Container()),
|
||||||
|
if (extend != null)
|
||||||
|
Text(
|
||||||
|
extend!,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: AppColors.unactive,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
rightWidget ??
|
||||||
|
const Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 16,
|
||||||
|
color: AppColors.unactive,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (bottom != null && bottom!.isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
bottom!,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: AppColors.unactive,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
212
lib/views/home/widgets/conversation_item.dart
Normal file
212
lib/views/home/widgets/conversation_item.dart
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
import 'package:chat/configs/app_colors.dart';
|
||||||
|
import 'package:chat/routes/conversation_routes.dart';
|
||||||
|
import 'package:chat/services/tim/conversation_service.dart';
|
||||||
|
import 'package:chat/utils/convert.dart';
|
||||||
|
import 'package:chat/views/home/widgets/group_avatar.dart';
|
||||||
|
import 'package:chat/views/home/widgets/message_preview_widget.dart';
|
||||||
|
import 'package:chat/views/home/widgets/pop_menu_item.dart';
|
||||||
|
import 'package:chat/widgets/custom_avatar.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/enum/conversation_type.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_conversation.dart';
|
||||||
|
|
||||||
|
class ConversationItem extends StatelessWidget {
|
||||||
|
final V2TimConversation conversation;
|
||||||
|
const ConversationItem(this.conversation, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: 68,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: conversation.isPinned! ? AppColors.page : null,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
top: 12,
|
||||||
|
bottom: 12,
|
||||||
|
),
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed(
|
||||||
|
ConversationRoutes.index,
|
||||||
|
arguments: {
|
||||||
|
'conversation': conversation,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onLongPress: () async {
|
||||||
|
await _showLongPressMenu();
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
children: [
|
||||||
|
conversation.type == ConversationType.V2TIM_C2C
|
||||||
|
? CustomAvatar(
|
||||||
|
conversation.faceUrl,
|
||||||
|
)
|
||||||
|
: GroupAvatar(conversation.groupID!),
|
||||||
|
Visibility(
|
||||||
|
visible: conversation.recvOpt == 0 &&
|
||||||
|
conversation.unreadCount! > 0,
|
||||||
|
child: Positioned(
|
||||||
|
right: -5,
|
||||||
|
top: -5,
|
||||||
|
child: Container(
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(18),
|
||||||
|
color: AppColors.red,
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
conversation.unreadCount! > 99
|
||||||
|
? '99+'
|
||||||
|
: conversation.unreadCount.toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: AppColors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: conversation.recvOpt == 1 &&
|
||||||
|
conversation.unreadCount! > 0,
|
||||||
|
child: const Positioned(
|
||||||
|
right: -3,
|
||||||
|
top: -3,
|
||||||
|
child: Icon(
|
||||||
|
Icons.circle_rounded,
|
||||||
|
color: AppColors.red,
|
||||||
|
size: 8,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
conversation.showName!,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MessagePreviewWidget(conversation.lastMessage),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
conversation.lastMessage == null
|
||||||
|
? ''
|
||||||
|
: Convert.timeFormat(
|
||||||
|
conversation.lastMessage!.timestamp!,
|
||||||
|
format: 'MM/dd HH:mm',
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (conversation.recvOpt == 1)
|
||||||
|
const Icon(
|
||||||
|
Icons.notifications_off_outlined,
|
||||||
|
size: 14,
|
||||||
|
color: AppColors.unactive,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showLongPressMenu() async {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: Get.context!,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: AppColors.white,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(8)),
|
||||||
|
),
|
||||||
|
builder: (context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
PopMenuItem(
|
||||||
|
conversation.isPinned! ? '取消置顶' : '聊天置顶',
|
||||||
|
onTap: () {
|
||||||
|
TimConversationService.to.setOnTop(conversation);
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 0),
|
||||||
|
PopMenuItem(
|
||||||
|
conversation.recvOpt == 1 ? '取消免打扰' : '消息免打扰',
|
||||||
|
onTap: () {
|
||||||
|
TimConversationService.to.setReceiveOpt(conversation);
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 0),
|
||||||
|
PopMenuItem(
|
||||||
|
'清空聊天记录',
|
||||||
|
onTap: () {
|
||||||
|
TimConversationService.to.clearHistoryMessage(conversation);
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 0),
|
||||||
|
PopMenuItem(
|
||||||
|
'标为已读',
|
||||||
|
onTap: () {
|
||||||
|
TimConversationService.to.markAsRead(conversation);
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 0),
|
||||||
|
PopMenuItem(
|
||||||
|
'删除该聊天',
|
||||||
|
onTap: () {
|
||||||
|
TimConversationService.to.delete(conversation);
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 0.4),
|
||||||
|
Container(
|
||||||
|
color: AppColors.page,
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
const Divider(height: 0.4),
|
||||||
|
PopMenuItem(
|
||||||
|
'取消',
|
||||||
|
onTap: () {
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
140
lib/views/home/widgets/friend_selector.dart
Normal file
140
lib/views/home/widgets/friend_selector.dart
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import 'package:azlistview/azlistview.dart';
|
||||||
|
import 'package:chat/configs/app_colors.dart';
|
||||||
|
import 'package:chat/models/im/contact_info_model.dart';
|
||||||
|
import 'package:chat/services/tim/friend_service.dart';
|
||||||
|
import 'package:chat/utils/im_tools.dart';
|
||||||
|
import 'package:chat/widgets/custom_avatar.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_info.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_member_full_info.dart';
|
||||||
|
|
||||||
|
class FriendSelector extends StatefulWidget {
|
||||||
|
final Function(List<V2TimFriendInfo>) onChanged;
|
||||||
|
final List<V2TimGroupMemberFullInfo?>? lockedUsers;
|
||||||
|
const FriendSelector({
|
||||||
|
required this.onChanged,
|
||||||
|
this.lockedUsers,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FriendSelector> createState() => _FriendSelectorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FriendSelectorState extends State<FriendSelector> {
|
||||||
|
/// 选中的好友列表
|
||||||
|
List<V2TimFriendInfo> selectList =
|
||||||
|
List<V2TimFriendInfo>.empty(growable: true);
|
||||||
|
|
||||||
|
/// 选择列表改变的事件,更新选中列表
|
||||||
|
_selectListChange(id) {
|
||||||
|
setState(() {
|
||||||
|
if (selectList.contains(id)) {
|
||||||
|
selectList.remove(id);
|
||||||
|
} else {
|
||||||
|
selectList.add(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
widget.onChanged(selectList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GetX<TimFriendService>(
|
||||||
|
builder: (_) {
|
||||||
|
return AzListView(
|
||||||
|
data: _.contacts,
|
||||||
|
itemCount: _.contacts.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
ContactInfoModel info = _.contacts[index];
|
||||||
|
return _contactItem(info);
|
||||||
|
},
|
||||||
|
susItemBuilder: (BuildContext context, int index) {
|
||||||
|
ContactInfoModel model = _.contacts[index];
|
||||||
|
return ImTools.susItem(
|
||||||
|
context,
|
||||||
|
model.getSuspensionTag(),
|
||||||
|
susHeight: 32,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
indexBarData: SuspensionUtil.getTagIndexList(_.contacts).toList(),
|
||||||
|
indexBarOptions: ImTools.indexBarOptions,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _contactItem(
|
||||||
|
ContactInfoModel info,
|
||||||
|
) {
|
||||||
|
bool isDisable = widget.lockedUsers == null
|
||||||
|
? false
|
||||||
|
: widget.lockedUsers!
|
||||||
|
.where(
|
||||||
|
(e) => e!.userID == info.friendInfo!.userID,
|
||||||
|
)
|
||||||
|
.isNotEmpty;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {},
|
||||||
|
child: Container(
|
||||||
|
color: AppColors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: isDisable
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
setState(() {
|
||||||
|
_selectListChange(info.friendInfo);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Radio<V2TimFriendInfo>(
|
||||||
|
groupValue:
|
||||||
|
selectList.contains(info.friendInfo) || isDisable
|
||||||
|
? info.friendInfo
|
||||||
|
: null,
|
||||||
|
onChanged: isDisable
|
||||||
|
? null
|
||||||
|
: (value) {
|
||||||
|
setState(() {
|
||||||
|
_selectListChange(info.friendInfo);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
value: info.friendInfo!,
|
||||||
|
toggleable: true,
|
||||||
|
),
|
||||||
|
CustomAvatar(
|
||||||
|
info.friendInfo!.userProfile!.faceUrl,
|
||||||
|
size: 32,
|
||||||
|
radius: 2,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
info.name,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
height: 0,
|
||||||
|
indent: 92,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
68
lib/views/home/widgets/group_avatar.dart
Normal file
68
lib/views/home/widgets/group_avatar.dart
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import 'package:chat/configs/app_colors.dart';
|
||||||
|
import 'package:chat/services/tim/group_service.dart';
|
||||||
|
import 'package:chat/widgets/custom_avatar.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_member_full_info.dart';
|
||||||
|
|
||||||
|
class GroupAvatar extends StatefulWidget {
|
||||||
|
final String groupID;
|
||||||
|
final double size;
|
||||||
|
const GroupAvatar(
|
||||||
|
this.groupID, {
|
||||||
|
this.size = 44,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<GroupAvatar> createState() => _GroupAvatarState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GroupAvatarState extends State<GroupAvatar> {
|
||||||
|
List<V2TimGroupMemberFullInfo?>? members;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
TimGroupService.to.members(widget.groupID, count: 9).then((value) {
|
||||||
|
setState(() {
|
||||||
|
members = value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: widget.size,
|
||||||
|
height: widget.size,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: AppColors.border,
|
||||||
|
width: 0.4,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
child: members == null
|
||||||
|
? CustomAvatar('')
|
||||||
|
: GridView.builder(
|
||||||
|
padding: const EdgeInsets.all(1),
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount:
|
||||||
|
(members != null && members!.length > 4) ? 3 : 2,
|
||||||
|
childAspectRatio: 1,
|
||||||
|
crossAxisSpacing: 1,
|
||||||
|
mainAxisSpacing: 1,
|
||||||
|
),
|
||||||
|
itemCount: members!.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return CustomAvatar(
|
||||||
|
members![index]?.faceUrl,
|
||||||
|
size: widget.size / 3,
|
||||||
|
radius: 2,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
111
lib/views/home/widgets/group_user_selector.dart
Normal file
111
lib/views/home/widgets/group_user_selector.dart
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import 'package:chat/configs/app_colors.dart';
|
||||||
|
import 'package:chat/widgets/custom_avatar.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_member_full_info.dart';
|
||||||
|
|
||||||
|
class GroupUserSelector extends StatefulWidget {
|
||||||
|
final Function(List<V2TimGroupMemberFullInfo>) onChanged;
|
||||||
|
final List<V2TimGroupMemberFullInfo> selectedList;
|
||||||
|
final List<V2TimGroupMemberFullInfo?> originalList;
|
||||||
|
|
||||||
|
const GroupUserSelector({
|
||||||
|
required this.onChanged,
|
||||||
|
required this.selectedList,
|
||||||
|
required this.originalList,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<GroupUserSelector> createState() => _GroupUserSelectorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GroupUserSelectorState extends State<GroupUserSelector> {
|
||||||
|
/// 选中的好友列表
|
||||||
|
List<V2TimGroupMemberFullInfo> selectList =
|
||||||
|
List<V2TimGroupMemberFullInfo>.empty(growable: true);
|
||||||
|
|
||||||
|
/// 选择列表改变的事件,更新选中列表
|
||||||
|
_selectListChange(id) {
|
||||||
|
setState(() {
|
||||||
|
if (selectList.contains(id)) {
|
||||||
|
selectList.remove(id);
|
||||||
|
} else {
|
||||||
|
selectList.add(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
widget.onChanged(selectList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListView.separated(
|
||||||
|
itemBuilder: (_, index) {
|
||||||
|
return _contactItem(widget.originalList[index]!);
|
||||||
|
},
|
||||||
|
separatorBuilder: (_, index) {
|
||||||
|
return const Divider(
|
||||||
|
height: 0,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
itemCount: widget.originalList.length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _contactItem(
|
||||||
|
V2TimGroupMemberFullInfo info,
|
||||||
|
) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {},
|
||||||
|
child: Container(
|
||||||
|
color: AppColors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_selectListChange(info);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Radio<V2TimGroupMemberFullInfo>(
|
||||||
|
groupValue: selectList.contains(info) ? info : null,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_selectListChange(info);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
value: info,
|
||||||
|
toggleable: true,
|
||||||
|
),
|
||||||
|
CustomAvatar(
|
||||||
|
info.faceUrl,
|
||||||
|
size: 32,
|
||||||
|
radius: 2,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
info.nickName!,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
height: 0,
|
||||||
|
indent: 92,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
lib/views/home/widgets/message_preview_widget.dart
Normal file
26
lib/views/home/widgets/message_preview_widget.dart
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import 'package:chat/configs/app_colors.dart';
|
||||||
|
import 'package:chat/utils/im_tools.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:tencent_im_sdk_plugin/models/v2_tim_message.dart';
|
||||||
|
|
||||||
|
class MessagePreviewWidget extends StatelessWidget {
|
||||||
|
final V2TimMessage? message;
|
||||||
|
const MessagePreviewWidget(this.message, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (message == null) {
|
||||||
|
return const Text('');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Text(
|
||||||
|
ImTools.parseMessage(message),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: AppColors.unactive,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
lib/views/home/widgets/pop_menu_item.dart
Normal file
29
lib/views/home/widgets/pop_menu_item.dart
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class PopMenuItem extends StatelessWidget {
|
||||||
|
final String text;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
|
const PopMenuItem(
|
||||||
|
this.text, {
|
||||||
|
this.onTap,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
onTap?.call();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
height: 52,
|
||||||
|
width: Get.width,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(text),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,19 +20,23 @@ class AppPage extends StatelessWidget {
|
|||||||
|
|
||||||
final List<Map> _tabBarList = [
|
final List<Map> _tabBarList = [
|
||||||
{
|
{
|
||||||
'icon': 'tabBar_03.png',
|
'icon': Icons.message_outlined,
|
||||||
|
'active_icon': Icons.message,
|
||||||
'label': '消息',
|
'label': '消息',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'icon': 'tabBar_03.png',
|
'icon': Icons.contact_page_outlined,
|
||||||
|
'active_icon': Icons.contact_page,
|
||||||
'label': '通讯录',
|
'label': '通讯录',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'icon': 'tabBar_03.png',
|
'icon': Icons.explore_outlined,
|
||||||
|
'active_icon': Icons.explore,
|
||||||
'label': '发现',
|
'label': '发现',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'icon': 'tabBar_03.png',
|
'icon': Icons.person_outline,
|
||||||
|
'active_icon': Icons.person,
|
||||||
'label': '我的',
|
'label': '我的',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -47,16 +51,14 @@ class AppPage extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
items: _tabBarList.map((item) {
|
items: _tabBarList.map((item) {
|
||||||
return BottomNavigationBarItem(
|
return BottomNavigationBarItem(
|
||||||
icon: Image.asset(
|
icon: Icon(
|
||||||
'assets/icons/${item['icon']}',
|
item['icon'],
|
||||||
width: 20,
|
size: 24,
|
||||||
height: 20,
|
|
||||||
),
|
),
|
||||||
activeIcon: Image.asset(
|
activeIcon: Icon(
|
||||||
'assets/icons/${item['icon']}',
|
item['active_icon'],
|
||||||
|
size: 24,
|
||||||
color: AppColors.primary,
|
color: AppColors.primary,
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
),
|
),
|
||||||
label: item['label'],
|
label: item['label'],
|
||||||
tooltip: '',
|
tooltip: '',
|
||||||
|
|||||||
15
lib/views/user/qr_code/index_page.dart
Normal file
15
lib/views/user/qr_code/index_page.dart
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class UserQrCodePage extends StatefulWidget {
|
||||||
|
const UserQrCodePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_UserQrCodePageState createState() => _UserQrCodePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _UserQrCodePageState extends State<UserQrCodePage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
}
|
||||||
177
pubspec.lock
177
pubspec.lock
@@ -122,6 +122,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.2"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.17.2"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -129,6 +136,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
|
dart_date:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: dart_date
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -143,6 +157,20 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.6"
|
version: "4.0.6"
|
||||||
|
extended_image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: extended_image
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.0"
|
||||||
|
extended_image_library:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: extended_image_library
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.0"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -277,6 +305,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.2.0"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.1"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -284,6 +319,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.5"
|
version: "0.13.5"
|
||||||
|
http_client_helper:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_client_helper
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -319,6 +361,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
|
intl:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.17.0"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -375,6 +424,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.7.0"
|
||||||
|
nested:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: nested
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
octo_image:
|
octo_image:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -473,6 +529,48 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.1"
|
version: "1.11.1"
|
||||||
|
permission_handler:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: permission_handler
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "10.2.0"
|
||||||
|
permission_handler_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_android
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "10.2.0"
|
||||||
|
permission_handler_apple:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_apple
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "9.0.7"
|
||||||
|
permission_handler_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_platform_interface
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.9.0"
|
||||||
|
permission_handler_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_windows
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.2"
|
||||||
|
photo_manager:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: photo_manager
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
pinput:
|
pinput:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -515,6 +613,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.4"
|
version: "0.2.4"
|
||||||
|
provider:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: provider
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.4"
|
||||||
qr:
|
qr:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -543,6 +648,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.0"
|
version: "1.6.0"
|
||||||
|
scroll_to_index:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: scroll_to_index
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
scrollable_positioned_list:
|
scrollable_positioned_list:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -617,7 +729,14 @@ packages:
|
|||||||
name: tencent_im_sdk_plugin
|
name: tencent_im_sdk_plugin
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.5.1"
|
version: "4.1.9"
|
||||||
|
tencent_im_sdk_plugin_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: tencent_im_sdk_plugin_platform_interface
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.5"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -632,6 +751,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.8"
|
version: "0.4.8"
|
||||||
|
timeago:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: timeago
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.3.0"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -639,13 +765,6 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
unorm_dart:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: unorm_dart
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.0"
|
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -667,6 +786,48 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.6"
|
version: "1.7.6"
|
||||||
|
video_player:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: video_player
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.7"
|
||||||
|
video_player_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_android
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.9"
|
||||||
|
video_player_avfoundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_avfoundation
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.7"
|
||||||
|
video_player_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_platform_interface
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.4"
|
||||||
|
video_player_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_web
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.12"
|
||||||
|
wechat_assets_picker:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: wechat_assets_picker
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "7.2.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
10
pubspec.yaml
10
pubspec.yaml
@@ -1,7 +1,7 @@
|
|||||||
name: chat
|
name: chat
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||||
version: 1.0.0+1
|
version: 1.0.0
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.16.2 <3.0.0"
|
sdk: ">=2.16.2 <3.0.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -15,7 +15,6 @@ dependencies:
|
|||||||
azlistview: ^2.0.0
|
azlistview: ^2.0.0
|
||||||
lpinyin: ^2.0.3
|
lpinyin: ^2.0.3
|
||||||
vibration: ^1.7.6
|
vibration: ^1.7.6
|
||||||
tencent_im_sdk_plugin: ^3.5.1
|
|
||||||
scan: ^1.6.0
|
scan: ^1.6.0
|
||||||
package_info_plus: ^0.0.1
|
package_info_plus: ^0.0.1
|
||||||
device_info_plus: ^0.0.1
|
device_info_plus: ^0.0.1
|
||||||
@@ -39,7 +38,12 @@ dependencies:
|
|||||||
ref: master
|
ref: master
|
||||||
fast_base58: ^0.2.1
|
fast_base58: ^0.2.1
|
||||||
hash: ^1.0.4
|
hash: ^1.0.4
|
||||||
unorm_dart: ^0.2.0
|
tencent_im_sdk_plugin: ^4.1.9
|
||||||
|
wechat_assets_picker: ^7.2.0
|
||||||
|
video_player: ^2.4.7
|
||||||
|
scroll_to_index: ^2.1.1
|
||||||
|
dart_date: ^1.1.1
|
||||||
|
permission_handler: ^10.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
@@ -6,9 +6,12 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||||
#include <smart_auth/smart_auth_plugin.h>
|
#include <smart_auth/smart_auth_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||||
SmartAuthPluginRegisterWithRegistrar(
|
SmartAuthPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("SmartAuthPlugin"));
|
registry->GetRegistrarForPlugin("SmartAuthPlugin"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
permission_handler_windows
|
||||||
smart_auth
|
smart_auth
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user