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;
}
相关推荐
程序员老刘3 小时前
跨平台开发地图 | 2026年6月
flutter·ai编程·客户端
悟空瞎说1 天前
Flutter 架构详解:新手必懂底层原理
flutter
SoaringHeart1 天前
Flutter最佳实践:IM聊天文字链接自动识别跳转
前端·flutter
恋猫de小郭1 天前
KMP / CMP 鸿蒙版本 Beta 发布,他有什么特别之处?
android·前端·flutter
风华圆舞2 天前
Flutter + 鸿蒙 Intents Kit:页面直达能力的完整接入方案
flutter·ui·华为·harmonyos
韩曙亮2 天前
【Flutter】Flutter 组件 ④ ( 组件渲染 的 三棵树理论 | Widget 树 → Element 树 → RenderObject 树 )
flutter·element·widget·renderobject
恋猫de小郭2 天前
Android 17 正式版发布,全新 AI 和各种破坏性更新
android·前端·flutter
kingbal2 天前
Windows:flutter环境搭建
windows·flutter
911hzh2 天前
Flutter MethodChannel 跨端通信框架 zh_native_channel:快速入门、优势分析与 Pigeon 对比
flutter
911hzh2 天前
Flutter 快速搭建新项目:用 Flutter Foundation Kit 一条命令生成带基础架构的 App 模板
flutter