Flutter 开发的极简风格聊天界面

总结

  • 花时间重构了之前写的聊天程序的页面。
  • 使用最新的Flutter Sdk 重写。
  • 优化了高斯模糊的写法。
  • 优化了对安全区域的判断逻辑。
  • 增加文字和图片的发送效果。
  • 增加图片查看功能。

项目中使用到的一些第三方组件

yaml 复制代码
get: ^4.7.3

# 系统接口
device_info_plus: ^12.2.0           # 设备信息(全平台支持)
permission_handler: ^12.0.1         # 权限处理(不支持PC - MacOS)

# 图像渲染
flutter_svg: ^2.2.3
extended_image: ^10.0.1             # 图片展示

# 数据存储
xml: ^6.6.1                         # xml解析 - 解析表情包文件
shared_preferences: ^2.5.4

# UI交互
easy_refresh: ^3.4.0                # 下拉刷新
flutter_slidable: ^4.0.3            # 滑动删除
flutter_smart_dialog: ^4.9.8+9      # 消息弹窗
flutter_keyboard_visibility: ^6.0.0 # 键盘状态监听
chat_bottom_container: ^0.4.0       # 输入框切换动画组件

# 本地资源选择
wechat_assets_picker: ^10.0.0       # 图片\视频选择器(不支持PC)
wechat_camera_picker: ^4.4.0        # 图片\视频拍摄器(不支持PC)

关于IOS上的一些权限配置

Podfile 文件

js 复制代码
post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
      target.build_configurations.each do |config|
        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
      '$(inherited)',
      'PERMISSION_CAMERA=1',
      'PERMISSION_PHOTOS=1',
      'PERMISSION_MICROPHONE=1',
      ]
    end
  end
end

Info.plist 中增加对权限的描述

xml 复制代码
	<key>NSCameraUsageDescription</key>
	<string>xxx</string>
	<key>NSAppleMusicUsageDescription</key>
	<string>xxx</string>
	<key>NSMicrophoneUsageDescription</key>
	<string>xxx</string>
	<key>NSPhotoLibraryAddUsageDescription</key>
	<string>xxx</string>
	<key>NSPhotoLibraryUsageDescription</key>
  <string>xxx</string>

自定义滚动透明化的Appbar标题

dart 复制代码
/// 滚动动画的App标题
mixin AppBarMixin on GetxController {
  /// 控制Appbar标题透明度的控制器
  AnimationController? fadeController;

  /// 透明度动画参数
  late Animation<double> fadeAnimation;

  /// 滚动列表的控制器
  final ScrollController scrollController = ScrollController();

  /// 初始化
  void onInitAnimation(GetSingleTickerProviderStateMixin item) {
    fadeController = AnimationController(
      duration: Duration(milliseconds: 300),
      vsync: item,
    );
    fadeAnimation = Tween<double>(begin: 0, end: 1).animate(fadeController!);

    scrollController.addListener(() {
      if (scrollController.offset >= 50) {
        //titleOpacity.value = 1;
        fadeController?.forward();
      } else {
        if (fadeAnimation.value != 0 && scrollController.offset > 0) {
          var fade = scrollController.offset / 50;
          fadeController?.animateTo(fade);
        } else {
          //titleOpacity.value = 0;
          fadeController?.reverse();
        }
      }
    });
  }

  @override
  void onClose() {
    fadeController?.dispose();
    scrollController.dispose();
    super.onClose();
  }
}

Hero动画

动画的Key需要传递到第二个页面。

dart 复制代码
Get.to(
       () => PreviewImagePage(),
        transition: Transition.noTransition,
        arguments: {
       "hero": message.heroKey,
       "source": imageMessage.image,
  },
);

 /// Hero 动画 Key
String heroKey = Get.arguments['hero'];

Hero(
   tag: heroKey,
   child: widget,
),

输入框切换动画

dart 复制代码
使用的第三方组件
chat_bottom_container: ^0.4.0       # 输入框切换动画组件

消息定义

dart 复制代码
import 'dart:math';

/// 消息基类
abstract class Message {
  final String name;
  final String avatar;
  final bool self;

  final String id = Random().nextInt(100000000).toString();

  /// 正在发送
  final bool sending = false;

  /// 发送失败
  final bool failed = false;

  /// 是否群聊
  final bool isGroup = false;

  abstract final bool center;
  abstract final bool canClick;
  abstract final MessageKind kind;

  Message({
    required this.avatar,
    required this.name,
    required this.self,
  });
}

enum MessageKind {
  unkonw,
  text,
  image,
  video,
}

/// 文本消息
class TextMessage extends Message {
  final String text;

  TextMessage({
    required this.text,
    required super.avatar,
    required super.name,
    required super.self,
  });

  @override
  bool get center => false;

  @override
  bool get canClick => false;

  @override
  MessageKind get kind => MessageKind.text;
}

/// 图片消息
class ImageMessage extends Message {
  final String image;
  final double w;
  final double h;

  ImageMessage({
    required this.image,
    required super.avatar,
    required super.name,
    required super.self,
    required this.w,
    required this.h,
  });

  /// 跳转的页动画Key
  String get heroKey => "PreviewImage-$id";

  @override
  bool get center => false;

  @override
  bool get canClick => true;

  @override
  MessageKind get kind => MessageKind.image;
}
相关推荐
kirk_wang3 小时前
Flutter Catcher 在鸿蒙端的错误捕获与上报适配指南
flutter·移动开发·跨平台·arkts·鸿蒙
未来猫咪花5 小时前
flutter 确实不需要 hooks
flutter
音浪豆豆_Rachel6 小时前
Flutter鸿蒙化之深入解析Pigeon可空返回与参数设计:nullable_returns.dart全解
flutter·harmonyos
音浪豆豆_Rachel6 小时前
Flutter鸿蒙跨平台测试策略解析:从基础Widget测试到平台集成验证
flutter·harmonyos
音浪豆豆_Rachel6 小时前
Flutter鸿蒙跨平台通信协议解析:Pigeon生成的Dart端桥接艺术
flutter·华为·harmonyos
子榆.7 小时前
Flutter 与开源鸿蒙(OpenHarmony)分布式能力实战:基于软总线实现跨设备协同
flutter·开源·harmonyos
光影少年7 小时前
前端开发桌面应用开发,Flutter 与 Electron如何选?
javascript·flutter·electron
音浪豆豆_Rachel8 小时前
Flutter鸿蒙文件选择器平台适配层:标准化接口与平台实现的桥梁
flutter·harmonyos