免费建站的网址,移动商城积分兑换,成都建站模板网站开发,wordpress 小工具代码环信即时通讯 IM 为开发者提供高可靠、低时延、高并发、安全、全球化的通信云服务#xff0c;帮助开发者快速构建端到端通信的场景。环信提供 SDK 和 RESTful API#xff0c;支持同时在线人数无上限#xff0c;聊天室亿级消息并发#xff0c;全球平均延时小于 200 毫秒帮助开发者快速构建端到端通信的场景。环信提供 SDK 和 RESTful API支持同时在线人数无上限聊天室亿级消息并发全球平均延时小于 200 毫秒相同区域平均延时小于 100 毫秒。支持单聊、群聊、聊天室服务提供服务端 RESTful API 和 回调服务提供多平台 SDK包括 Android、iOS、Web、HarmonyOS、Windows、Linux、Unity、Flutter、React Native、小程序、uni-app 和 Electron提供 Demo 和 UIKit。如果你的Flutter项目想运行在鸿蒙端而且你还使用了 uikit 层面的依赖库那么还是需要做一些兼容和割舍的。「博主并不建议你这么操作毕竟官方并不提供支持」接下来就让博主带着这些刚需的朋友们来看一下该如何使用环信em_chat_uikit运行到鸿蒙端。配置鸿蒙环境参照官方说明配置环境依赖Flutter版本调整为支持鸿蒙的版本3.22.0-ohos目前我使用的是3.22.1-ohos-1.0.4也是可以的environment: sdk: 3.4.0 flutter: 3.22.1-ohos-1.0.4依赖调整由于涉及到ui层面的修改因此需要本地依赖em_chat_uikit添加im sdk插件dependencies: flutter: sdk: flutter ... em_chat_uikit: path: ../em_chat_uikit-2.2.0 im_flutter_sdk_ohos: git: url: https://github.com/easemob/im_flutter_sdk_oh.git ref: 1.5.3替换所有需要兼容鸿蒙的第三方flutter库dependency_overrides: im_flutter_sdk: 4.13.0 im_flutter_sdk_ios: 4.13.0 im_flutter_sdk_android: 4.13.0 im_flutter_sdk_interface: 4.13.0 chat_uikit_keyboard_panel: path: ../chat_uikit_keyboard_panel record: git: url: https://gitcode.com/openharmony-sig/fluttertpc_record.git path: record ref: d40e26bd4052362d505ef8c2c600ac69aa5a967a record_platform_interface: git: url: https://gitcode.com/openharmony-sig/fluttertpc_record.git path: record_platform_interface ref: d40e26bd4052362d505ef8c2c600ac69aa5a967a shared_preferences: git: url: https://gitcode.com/openharmony-sig/flutter_packages.git path: packages/shared_preferences/shared_preferences path_provider: git: url: https://gitcode.com/openharmony-sig/flutter_packages.git path: packages/path_provider/path_provider file_picker: git: url: https://gitcode.com/openharmony-sig/fluttertpc_file_picker.git ref: br_v8.0.7_ohos image_picker: git: url: https://gitcode.com/openharmony-sig/flutter_packages.git path: packages/image_picker/image_picker audioplayers: git: url: https://gitcode.com/openharmony-sig/flutter_audioplayers.git path: packages/audioplayers video_compress: git: url: https://gitcode.com/openharmony-sig/fluttertpc_video_compress.git video_player: git: url: https://gitcode.com/openharmony-sig/flutter_packages.git path: packages/video_player/video_player flutter_localization: git: url: https://gitcode.com/openharmony-sig/flutter_localization.git sqflite: git: url: https://gitcode.com/OpenHarmony-SIG/flutter_sqflite.git ref: github.com/tekartik/sqflite.git/v2.3.31 path: sqflite注意chat_uikit_keyboard_panel插件需要额外兼容鸿蒙目前仅在本地做修改需要插件的可以私信博主或者参照我之前的文章自行编写。代码调整检索im_flutter_sdk_oh插件中所有调用noSupport实现的方法将em_chat_uikit中所有使用到的地方进行调整或隐藏、或替换、或修改、或删除例如聊天页面获取子区方法// 修改前 ChatThread? threadOverView await msg.chatThread(); MessagePinInfo? pinInfo await msg.pinInfo(); modelLists.add( MessageModel( message: msg, reactions: reactions, thread: threadOverView, pinInfo: pinInfo, ), ); // 修改后 MessagePinInfo? pinInfo await msg.pinInfo(); modelLists.add( MessageModel( message: msg, reactions: reactions, thread: null, pinInfo: pinInfo, ), );以下做修改记录如有缺失请继续补充子区功能调整隐藏子区ChatUIKitSettings.enableMessageThread false;翻译功能调整translateMessage以及fetchSupportedLanguages均未实现因此翻译目标语言不支持// 隐藏消息菜单 ChatUIKitSettings.msgItemLongPressActions .remove(ChatUIKitActionType.translate);举报功能调整// 隐藏举报菜单 ChatUIKitSettings.msgItemLongPressActions .remove(ChatUIKitActionType.report);ChatUIKitPopupMenu溢出适配// 1.移除Container的vertical padding - 将Container改为SizedBox去除了上下各4像素的padding这样释放了8像素的可用空间 // 2.减小图标和文本之间的间隔 - 将SizedBox(height: 4)改为SizedBox(height: 2)进一步减少2像素 override Widget build(BuildContext context) { ... Widget content Wrap( direction: Axis.horizontal, alignment: WrapAlignment.start, children: widget.actions.map((item) { return InkWell( onTap: () { widget.close?.call(); item.onTap?.call(); }, child: SizedBox( width: itemWidth, height: itemHeight, child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ if (item.icon ! null) SizedBox( height: 28, width: 28, child: item.icon!, ), const SizedBox(height: 2), Text( item.label, maxLines: 1, overflow: TextOverflow.ellipsis, textScaler: TextScaler.noScaling, style: TextStyle( color: widget.style.foregroundColor, fontSize: 12, fontWeight: FontWeight.w500, ), ), ], ), ), ); }).toList(), ); ... return content; } }注意在调用sdk的api实现自己的功能时需要确认一下sdk是否真正的实现了该api而不是nosupport权限调整添加麦克风权限在项目目录/ohos/entry/src/main/module.json5中新增麦克风权限{ module: { ... requestPermissions: [ { name: ohos.permission.MICROPHONE, reason: $string:reason, usedScene: { when: always, abilities: [ EntryAbility ] } } ] } }在项目目录/ohos/AppScope/resources/base/element/string.json中添加reason描述{ string: [ ... { name: reason, value: 录制音频音视频通话等需要麦克风权限 } ] }处理异常问题目前record_bar中对于获取用户麦克风权限失败后直接抛出异常最好是给用户一个简单的提示因此做以下修改Futurevoid startRecording() async { if (await record.hasPermission()) { if (await record.isRecording()) { return; } try { fileName ${DateTime.now().millisecondsSinceEpoch.toString()}.$extensionName; record.start(recordConfig, path: ${_directory!.path}/$fileName); _state?.switchRecordType(RecordBarRecordType.recording); } catch (e) { throw RecordError(recordFailed, Failed to start recording); } } else { // 发送麦克风权限未授权事件 ChatUIKit.instance .sendChatUIKitEvent(ChatUIKitEvent.noMicrophonePermission); // 显示提示对话框 _showPermissionDeniedDialog(); } } void _showPermissionDeniedDialog() { final context _state?.context; if (context null || !context.mounted) return; showChatUIKitDialog( context: context, title: ChatUIKitLocal.microphonePermissionDeniedTitle.localString(context), content: ChatUIKitLocal.microphonePermissionDeniedContent.localString(context), actionItems: [ ChatUIKitDialogAction.confirm( label: ChatUIKitLocal.confirm.localString(context), onTap: () { Navigator.of(context).pop(); }, ), ], ); }chat_uikit_localizayions.dart中添加国际化字符串microphonePermissionDeniedTitle: 麦克风权限未授权, microphonePermissionDeniedContent: 需要麦克风权限才能录制语音消息请前往设备设置中开启当前应用的麦克风权限。, microphonePermissionDeniedTitle: Microphone Permission Denied, microphonePermissionDeniedContent: Microphone permission is required to record voice messages. Please enable it in Settings.,其他问题如果项目中有使用到open_file插件需要更换成open_filex因为查看open_file源码发现它并没有对鸿蒙平台做兼容// 嗯可能是BUG吧 class OpenFile { static const MethodChannel _channel const MethodChannel(open_file); OpenFile._(); ///[filePath] On web you need to pass the file name to determine the file type ///[linuxDesktopName] like xdg/gnome static FutureOpenResult open(String? filePath, {String? type, String? uti, String linuxDesktopName xdg, bool linuxUseGio false, bool linuxByProcess false, Uint8List? webData}) async { assert(filePath ! null); assert(linuxUseGio ! false || linuxByProcess ! false, cant have both linuxUseGio and linuxByProcess); if (!Platform.isMacOS !Platform.isIOS !Platform.isAndroid) { int _result; var _windowsResult; if (Platform.isLinux) { var filePathLinux Uri.file(filePath!); if (linuxByProcess) { _result Process.runSync(xdg-open, [filePathLinux.toString()]).exitCode; } else if (linuxUseGio) { _result linux.system([gio, open, filePathLinux.toString()]); } else { _result linux .system([$linuxDesktopName-open, filePathLinux.toString()]); } } else if (Platform.isWindows) { _windowsResult windows.shellExecute(open, filePath!); _result _windowsResult 32 ? 1 : 0; } else { _result -1; } return OpenResult( type: _result 0 ? ResultType.done : ResultType.error, message: _result 0 ? done : _result -1 ? This operating system is not currently supported : there are some errors when open $filePath${Platform.isWindows ? HINSTANCE$_windowsResult : }); } MapString, String? map { file_path: filePath!, type: type, uti: uti, }; final _result await _channel.invokeMethod(open_file, map); final resultMap json.decode(_result) as MapString, dynamic; return OpenResult.fromJson(resultMap); } }在使用open_filex打开文件是需要对下载的文件路径做编码处理因为我们的appkey是包含#字符的直接访问会查找不到文件这不禁让我想起了刚开始接触鸿蒙时安装ide的路径不能包含中文「嗯是国产没错了」String filePath com.example.chat_uikit_harmony$path; Uri fileUri Uri.file(filePath); final result await OpenFilex.open(fileUri.toString()); debugPrint(result: ${result.toString()});Reaction添加或者移除页面未更新在消息列表页面添加更新操作/// 消息列表控制器 class MessagesViewController extends ChangeNotifier with SafeAreaDisposed, ChatObserver, MessageObserver, ThreadObserver { ... Futurevoid updateReaction( String messageId, String reaction, bool isAdd, ) async { try { ... // 操作成功后立即刷新本地 UI await refreshMessageReaction(messageId); } catch (e) { chatPrint(updateReaction: $e); } } Futurevoid refreshMessageReaction(String messageId) async { final index msgModelList .indexWhere((element) element.message.msgId messageId); if (index ! -1) { Message? msg await ChatUIKit.instance.loadMessage(messageId: messageId); if (msg ! null) { ListMessageReaction? reactions await msg.reactionList(); msgModelList[index] msgModelList[index].copyWith( message: msg, reactions: reactions, ); lastActionType MessageLastActionType.originalPosition; refresh(); } } } ... }在ReactionInfo添加onReactionChanged回调/// Reaction页面 /// 添加onReactionChanged回调 class ChatUIKitMessageReactionInfo extends StatefulWidget { const ChatUIKitMessageReactionInfo( this.model, { this.onReactionChanged, super.key, }); final MessageModel model; /// 当 reaction 发生变化时的回调添加或删除 final VoidCallback? onReactionChanged; override StateChatUIKitMessageReactionInfo createState() _ChatUIKitMessageReactionInfoState(); } class _ChatUIKitMessageReactionInfoState extends StateChatUIKitMessageReactionInfo with SingleTickerProviderStateMixin, ChatUIKitThemeMixin { ... override Widget themeBuilder(BuildContext context, ChatUIKitTheme theme) { return Column( children: [ Container( margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), height: 28, child: ListView.builder( scrollDirection: Axis.horizontal, itemCount: reactions.length, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: InkWell( highlightColor: Colors.transparent, splashColor: Colors.transparent, onTap: () { tabController.animateTo(index); }, child: ChatUIkitReactionWidget( reactions[index], highlightColor: Colors.transparent, highlightTextColor: theme.color.isDark ? theme.color.neutralColor95 : theme.color.neutralColor3, bgColor: selectIndex index ? (theme.color.isDark ? theme.color.neutralColor3 : theme.color.neutralColor9) : Colors.transparent, ), ), ); }, ), ), Expanded( child: TabBarView( controller: tabController, children: reactions .map( (e) ChatReactionInfoWidget( msgId: messageID, reaction: e, onReactionDeleteTap: () { onReactionDeleteTap(e); // 通知消息列表页面刷新 widget.onReactionChanged?.call(); }, ), ) .toList(), ), ), ], ); } ... }在消息页面处理reaction变化逻辑/// 消息页面 class MessagesView extends StatefulWidget { ... override StateMessagesView createState() _MessagesViewState(); } class _MessagesViewState extends StateMessagesView with ChatObserver, ChatUIKitThemeMixin { ... void showReactionsInfo(BuildContext context, MessageModel model) { showChatUIKitBottomSheet( context: context, showCancel: false, body: ChatUIKitMessageReactionInfo( model, onReactionChanged: () { // 刷新消息列表中的 reaction 显示 controller.refreshMessageReaction(model.message.msgId); }, ), ); } ... }以上我们基本上就完成了 Flutter 向鸿蒙端的整体适配如有其他问题也可以私信博主进行讨论。