diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 345b4a5..a2ad459 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + LaunchScreen UIMainStoryboardFile Main + NSPhotoLibraryUsageDescription + Replace with your permission description. UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/lib/controller/message/chat_controller.dart b/lib/controller/message/chat_controller.dart index 98473b4..c5cee71 100644 --- a/lib/controller/message/chat_controller.dart +++ b/lib/controller/message/chat_controller.dart @@ -65,6 +65,25 @@ class ChatController extends GetxController { } } + /// 发送图片消息 + Future sendImageMessage(String imagePath) async { + try { + final message = await IMManager.instance.sendImageMessage(imagePath, userId); + if (message != null) { + // 发送成功后将消息添加到列表开头 + messages.insert(0, message); + update(); + return true; + } + return false; + } catch (e) { + if (Get.isLogEnable) { + Get.log('发送图片消息失败: $e'); + } + return false; + } + } + /// 获取消息列表 Future fetchMessages({bool loadMore = false}) async { try { diff --git a/lib/pages/message/chat_page.dart b/lib/pages/message/chat_page.dart index a1749e3..a673afa 100644 --- a/lib/pages/message/chat_page.dart +++ b/lib/pages/message/chat_page.dart @@ -80,6 +80,12 @@ class ChatPage extends StatelessWidget { onSendMessage: (message) async { await controller.sendMessage(message); }, + onImageSelected: (imagePaths) async { + // 为每个图片路径调用控制器的方法发送图片消息 + for (var imagePath in imagePaths) { + await controller.sendImageMessage(imagePath); + } + }, ), ], ), diff --git a/lib/widget/message/chat_input_bar.dart b/lib/widget/message/chat_input_bar.dart index c3e69cc..79622a0 100644 --- a/lib/widget/message/chat_input_bar.dart +++ b/lib/widget/message/chat_input_bar.dart @@ -4,11 +4,13 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import '../../generated/assets.dart'; +import 'more_options_view.dart'; class ChatInputBar extends StatefulWidget { final ValueChanged onSendMessage; + final ValueChanged>? onImageSelected; - const ChatInputBar({required this.onSendMessage, super.key}); + const ChatInputBar({required this.onSendMessage, this.onImageSelected, super.key}); @override State createState() => _ChatInputBarState(); @@ -38,6 +40,26 @@ class _ChatInputBarState extends State { }); } + void _handleImageTap(List imagePaths) { + if (Get.isLogEnable) { + Get.log("选择了图片: $imagePaths"); + } + // 将图片路径列表传递给父组件 + if (widget.onImageSelected != null) { + widget.onImageSelected!(imagePaths); + } + } + + void _handleCameraTap(String imagePath) { + if (Get.isLogEnable) { + Get.log("拍摄了照片: $imagePath"); + } + // 将单个图片路径包装成列表传递给父组件 + if (widget.onImageSelected != null) { + widget.onImageSelected!([imagePath]); + } + } + @override Widget build(BuildContext context) { return Column( @@ -110,80 +132,14 @@ class _ChatInputBarState extends State { ), ), // 更多选项展开视图 - _buildMoreOptionsView(), + MoreOptionsView( + isVisible: _isMoreOptionsVisible, + onImageSelected: _handleImageTap, + onCameraSelected: _handleCameraTap, + ) ], ); } - // 构建更多选项展开视图 - Widget _buildMoreOptionsView() { - return AnimatedContainer( - duration: Duration(milliseconds: 300), - height: _isMoreOptionsVisible ? 180.h : 0, - color: Colors.white, - child: _isMoreOptionsVisible - ? Container( - padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 20.h), - child: Column( - children: [ - SizedBox(height: 10.h), - // 第一行选项 - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - // 图片选项 - Column( - children: [ - Container( - width: 60.w, - height: 60.w, - decoration: BoxDecoration( - color: Color(0xffF0F5FF), - borderRadius: BorderRadius.circular(8.w), - ), - padding: EdgeInsets.all(10.w), - child: Image.asset(Assets.imagesPhoto, width: 40.w, height: 40.w), - ), - SizedBox(height: 8.h), - Text( - "图片", - style: TextStyle( - fontSize: 12.sp, - color: Colors.black, - ), - ), - ], - ), - SizedBox(width: 40.w), - // 相机选项 - Column( - children: [ - Container( - width: 60.w, - height: 60.w, - decoration: BoxDecoration( - color: Color(0xffF0F5FF), - borderRadius: BorderRadius.circular(8.w), - ), - padding: EdgeInsets.all(10.w), - child: Image.asset(Assets.imagesCamera, width: 40.w, height: 40.w), - ), - SizedBox(height: 8.h), - Text( - "相机", - style: TextStyle( - fontSize: 12.sp, - color: Colors.black, - ), - ), - ], - ), - ], - ), - ], - ), - ) - : null, - ); - } + } diff --git a/lib/widget/message/more_options_view.dart b/lib/widget/message/more_options_view.dart new file mode 100644 index 0000000..9dd5750 --- /dev/null +++ b/lib/widget/message/more_options_view.dart @@ -0,0 +1,137 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:wechat_assets_picker/wechat_assets_picker.dart'; +import 'package:wechat_camera_picker/wechat_camera_picker.dart'; + +import '../../generated/assets.dart'; + +class MoreOptionsView extends StatelessWidget { + final bool isVisible; + final ValueChanged> onImageSelected; + final ValueChanged onCameraSelected; + + const MoreOptionsView({ + required this.isVisible, + required this.onImageSelected, + required this.onCameraSelected, + super.key, + }); + + @override + Widget build(BuildContext context) { + return AnimatedContainer( + duration: Duration(milliseconds: 300), + height: isVisible ? 180.h : 0, + color: Colors.white, + child: isVisible + ? Container( + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 20.h), + child: Column( + children: [ + SizedBox(height: 10.h), + // 第一行选项 + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + // 图片选项 + GestureDetector( + onTap: () async{ + try { + List? result = await AssetPicker.pickAssets(context); + if (result != null && result.isNotEmpty) { + // 获取所有选中图片的文件路径 + List imagePaths = []; + for (var asset in result) { + final file = await asset.file; + if (file != null) { + imagePaths.add(file.path); + } + } + if (imagePaths.isNotEmpty) { + onImageSelected(imagePaths); + } + } + } catch (e) { + if (Get.isLogEnable) { + Get.log("选择图片失败: $e"); + } + } + }, + child: Column( + children: [ + Container( + width: 60.w, + height: 60.w, + decoration: BoxDecoration( + color: Color(0xffF0F5FF), + borderRadius: BorderRadius.circular(8.w), + ), + padding: EdgeInsets.all(10.w), + child: Image.asset(Assets.imagesPhoto, width: 40.w, height: 40.w), + ), + SizedBox(height: 8.h), + Text( + "图片", + style: TextStyle( + fontSize: 12.sp, + color: Colors.black, + ), + ), + ], + ), + ), + SizedBox(width: 40.w), + // 相机选项 + GestureDetector( + onTap: () async{ + try { + AssetEntity? entity = await CameraPicker.pickFromCamera( + context, + pickerConfig: const CameraPickerConfig(), + ); + if (entity != null) { + // 获取拍摄照片的文件路径 + final file = await entity.file; + if (file != null) { + onCameraSelected(file.path); + } + } + } catch (e) { + if (Get.isLogEnable) { + Get.log("拍照失败: $e"); + } + } + }, + child: Column( + children: [ + Container( + width: 60.w, + height: 60.w, + decoration: BoxDecoration( + color: Color(0xffF0F5FF), + borderRadius: BorderRadius.circular(8.w), + ), + padding: EdgeInsets.all(10.w), + child: Image.asset(Assets.imagesCamera, width: 40.w, height: 40.w), + ), + SizedBox(height: 8.h), + Text( + "相机", + style: TextStyle( + fontSize: 12.sp, + color: Colors.black, + ), + ), + ], + ), + ), + ], + ), + ], + ), + ) + : null, + ); + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 29dbbaa..9102ef1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -105,6 +105,46 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "8.12.0" + camera: + dependency: transitive + description: + name: camera + sha256: dfa8fc5a1adaeb95e7a54d86a5bd56f4bb0e035515354c8ac6d262e35cec2ec8 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.10.6" + camera_android: + dependency: transitive + description: + name: camera_android + sha256: "8397c4fcec4f4dbafdeff994adc6ed16dcaa4a25f02e70e3b8fbe59c9a88807f" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.10.10+11" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + sha256: "34bcd5db30e52414f1f0783c5e3f566909fab14141a21b3b576c78bd35382bf6" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.9.22+4" + camera_platform_interface: + dependency: transitive + description: + name: camera_platform_interface + sha256: "98cfc9357e04bad617671b4c1f78a597f25f08003089dd94050709ae54effc63" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.12.0" + camera_web: + dependency: transitive + description: + name: camera_web + sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.3.5" characters: dependency: transitive description: @@ -257,6 +297,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.0.1" + extended_image: + dependency: transitive + description: + name: extended_image + sha256: f6cbb1d798f51262ed1a3d93b4f1f2aa0d76128df39af18ecb77fa740f88b2e0 + url: "https://pub.flutter-io.cn" + source: hosted + version: "10.0.1" + extended_image_library: + dependency: transitive + description: + name: extended_image_library + sha256: "1f9a24d3a00c2633891c6a7b5cab2807999eb2d5b597e5133b63f49d113811fe" + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.0.1" fake_async: dependency: transitive description: @@ -461,6 +517,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.5.0" + http_client_helper: + dependency: transitive + description: + name: http_client_helper + sha256: "8a9127650734da86b5c73760de2b404494c968a3fd55602045ffec789dac3cb1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.0" http_multi_server: dependency: transitive description: @@ -597,6 +661,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.5" + js: + dependency: transitive + description: + name: js + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.7.2" json_annotation: dependency: transitive description: @@ -693,6 +765,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.6" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.0" package_config: dependency: transitive description: @@ -829,6 +909,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "7.0.1" + photo_manager: + dependency: transitive + description: + name: photo_manager + sha256: a0d9a7a9bc35eda02d33766412bde6d883a8b0acb86bbe37dac5f691a0894e8a + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.7.1" + photo_manager_image_provider: + dependency: transitive + description: + name: photo_manager_image_provider + sha256: b6015b67b32f345f57cf32c126f871bced2501236c405aafaefa885f7c821e4f + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.0" platform: dependency: transitive description: @@ -869,6 +965,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "5.1.0" + provider: + dependency: transitive + description: + name: provider + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.1.5+1" pub_semver: dependency: transitive description: @@ -909,6 +1013,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "10.1.3" + sensors_plus: + dependency: transitive + description: + name: sensors_plus + sha256: "89e2bfc3d883743539ce5774a2b93df61effde40ff958ecad78cd66b1a8b8d52" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.1.2" + sensors_plus_platform_interface: + dependency: transitive + description: + name: sensors_plus_platform_interface + sha256: "58815d2f5e46c0c41c40fb39375d3f127306f7742efe3b891c0b1c87e2b5cd5d" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.1" shared_preferences: dependency: transitive description: @@ -1098,6 +1218,54 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" + video_player: + dependency: transitive + description: + name: video_player + sha256: "0d55b1f1a31e5ad4c4967bfaa8ade0240b07d20ee4af1dfef5f531056512961a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.10.0" + video_player_android: + dependency: transitive + description: + name: video_player_android + sha256: cf768d02924b91e333e2bc1ff928528f57d686445874f383bafab12d0bdfc340 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.8.17" + video_player_avfoundation: + dependency: transitive + description: + name: video_player_avfoundation + sha256: "19ed1162a7a5520e7d7791e0b7b73ba03161b6a69428b82e4689e435b325432d" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.8.5" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + sha256: "57c5d73173f76d801129d0531c2774052c5a7c11ccb962f1830630decd9f24ec" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.6.0" + video_player_web: + dependency: transitive + description: + name: video_player_web + sha256: "9f3c00be2ef9b76a95d94ac5119fb843dca6f2c69e6c9968f6f2b6c9e7afbdeb" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.0" + visibility_detector: + dependency: transitive + description: + name: visibility_detector + sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.4.0+2" vm_service: dependency: transitive description: @@ -1138,6 +1306,30 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.0.3" + wechat_assets_picker: + dependency: "direct main" + description: + name: wechat_assets_picker + sha256: c307e50394c1e6dfcd5c4701e84efb549fce71444fedcf2e671c50d809b3e2a1 + url: "https://pub.flutter-io.cn" + source: hosted + version: "9.8.0" + wechat_camera_picker: + dependency: "direct main" + description: + name: wechat_camera_picker + sha256: "776ce32feda72d84b63425533a27d3b822bfb93cc063d2aef3cc6d788769f36b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.4.0" + wechat_picker_library: + dependency: transitive + description: + name: wechat_picker_library + sha256: "5cb61b9aa935b60da5b043f8446fbb9c5077419f20ccc4856bf444aec4f44bc1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.7" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ce17b03..5ed3197 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,6 +53,8 @@ dependencies: permission_handler: ^12.0.1 flustars: ^2.0.1 easy_refresh: ^3.4.0 + wechat_assets_picker: ^9.8.0 + wechat_camera_picker: ^4.4.0 dev_dependencies: flutter_test: