最新版Flutter3.32+Dart3.8跨平台仿微信app聊天界面|朋友圈

2025原创研发flutter3+dart3实战仿微信App 聊天系统Flutter3Chat

flutter3_wechat :基于最新跨平台框架flutter3.32+dart3.8+get_storage+photo_view从0-1打造仿微信app聊天项目。包含聊天、通讯录、我的及朋友圈 等模块。实现发送文字+emo表情消息、长按仿微信语音操作、图片/链接预览等功能。

技术栈

  • 编辑器:VScode
  • 框架技术:Flutter3.32+Dart3.8
  • 组件库:material-design3
  • 弹窗组件:showDialog/SimpleDialog/showModalBottomSheet/AlertDialog
  • 图片预览:photo_view^0.15.0
  • 存储组件:get_storage^2.1.1
  • 下拉刷新:easy_refresh^3.4.0
  • toast提示:toast^0.3.0
  • 网址预览组件:url_launcher^6.3.1

项目框架目录

flutter3-chat聊天app项目已经更新到我的原创作品集。

flutter3.32+dart3.8仿微信App聊天界面|朋友圈

flutter3沉浸式渐变导航条

通过配置AppBar提供的可伸缩灵活区域属性 flexibleSpace 搭配gradient即可快速实现渐变导航栏。

复制代码
AppBar(
  title: Text('Flutter3-Chat'),
  flexibleSpace: Container(
    decoration: const BoxDecoration(
      gradient: LinearGradient(
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
        colors: [
          Color(0xFF0091EA), Color(0xFF07C160)
        ],
      )
    ),
  )
),

flutter3仿微信PopupMenu下拉菜单/下拉刷新

flutter提供的PopupMenuButton组件实现下拉菜单功能。

复制代码
PopupMenuButton(
  icon: FStyle.iconfont(0xe62d, size: 17.0),
  offset: const Offset(0, 50.0),
  tooltip: '',
  color: const Color(0xFF353535),
  itemBuilder: (BuildContext context) {
    return <PopupMenuItem>[
      popupMenuItem(0xe666, '发起群聊', 0),
      popupMenuItem(0xe75c, '添加朋友', 1),
      popupMenuItem(0xe603, '扫一扫', 2),
      popupMenuItem(0xe6ab, '收付款', 3),
    ];
  },
  onSelected: (value) {
    switch(value) {
      case 0:
        print('发起群聊');
        break;
      case 1:
        Navigator.pushNamed(context, '/addfriends');
        break;
      case 2:
        print('扫一扫');
        break;
      case 3:
        print('收付款');
        break;
    }
  },
)

下拉刷新、上拉加载更多是通过 easy_refresh 组件实现功能。

复制代码
EasyRefresh(
  // 下拉加载提示
  header: const ClassicHeader(
    // showMessage: false,
  ),
  // 加载更多提示
  footer: ClassicFooter(),
  // 下拉刷新逻辑
  onRefresh: () async {
    // ...下拉逻辑
    await Future.delayed(const Duration(seconds: 2));
  },
  // 上拉加载逻辑
  onLoad: () async {
    // ...
  },
  child: ListView.builder(
    itemCount: chatList.length,
    itemBuilder: (context, index) {
      return Ink(
        // ...
      );
    },
  ),
)

弹窗功能均是自定义AlertDialog实现效果。通过无限制容器UnconstrainedBox配合SizedBox组件实现自定义窗口大小。

复制代码
// 关于弹窗
void aboutAlertDialog(BuildContext context) {
  showDialog(
    context: context,
    builder: (context) {
      return UnconstrainedBox(
        constrainedAxis: Axis.vertical,
        child: SizedBox(
          width: 320.0,
          child: AlertDialog(
            contentPadding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
            backgroundColor: Colors.white,
            surfaceTintColor: Colors.white,
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
            content: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 10.0),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Image.asset('assets/images/logo.png', width: 90.0, height: 90.0, fit: BoxFit.cover,),
                  const SizedBox(height: 10.0),
                  const Text('Flutter3-WChat', style: TextStyle(color: Color(0xFF0091EA), fontSize: 22.0),),
                  const SizedBox(height: 5.0),
                  const Text('基于flutter3+dart3开发跨平台仿微信App聊天实例。', style: TextStyle(color: Colors.black45),),
                  const SizedBox(height: 20.0),
                  Text('©2024/01 Andy   Q: 282310962', style: TextStyle(color: Colors.grey[400], fontSize: 12.0),),
                ],
              ),
            ),
          ),
        ),
      );
    }
  );
}

// 二维码名片弹窗
void qrcodeAlertDialog(BuildContext context) {
  showDialog(
    context: context,
    builder: (context) {
      return UnconstrainedBox(
        constrainedAxis: Axis.vertical,
        child: SizedBox(
          width: 320.0,
          child: AlertDialog(
            contentPadding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
            backgroundColor: const Color(0xFF07C160),
            surfaceTintColor: const Color(0xFF07C160),
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(3.0)),
            content: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 10.0),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Image.asset('assets/images/qrcode.png', width: 250.0, fit: BoxFit.cover,),
                  const SizedBox(height: 15.0),
                  const Text('扫一扫,加我公众号', style: TextStyle(color: Colors.white60, fontSize: 14.0,),),
                ],
              ),
            ),
          ),
        ),
      );
    }
  );
}

// 退出登录弹窗
void logoutAlertDialog(BuildContext context) {
  showDialog(
    context: context,
    builder: (context) {
      return AlertDialog(
        content: const Text('确定要退出登录吗?', style: TextStyle(fontSize: 16.0),),
        backgroundColor: Colors.white,
        surfaceTintColor: Colors.white,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
        elevation: 2.0,
        actionsPadding: const EdgeInsets.all(15.0),
        actions: [
          TextButton(
            onPressed: () {Navigator.of(context).pop();},
            child: const Text('取消', style: TextStyle(color: Colors.black54),)
          ),
          TextButton(
            onPressed: handleLogout,
            child: const Text('退出登录', style: TextStyle(color: Colors.red),)
          ),
        ],
      );
    }
  );
}

flutter3实现微信朋友圈

复制代码
ImageGroup(images: item['images'])

ImageGroup(
  images: uploadList,
  album: true,
  onChoose: () async {
    Toast.show('选择手机相册图片', duration: 2, gravity: 1);
  },
)
复制代码
/// 微信朋友圈九宫格图片
library;

import 'package:flutter/material.dart';
import '../router/fade_route.dart';
import 'image_viewer.dart';

import '../utils/index.dart';

class ImageGroup extends StatelessWidget {
  const ImageGroup({
    super.key,
    this.images,
    this.width = 200.0,
    this.album = false,
    this.limit = 9,
    this.onChoose,
  });

  final List<String>? images; // 图片组
  final double width; // 图片宽度
  final bool album; // 是否相册/专辑(最后面显示+可选择图片)
  final int limit; // 限制多少张
  final Function? onChoose; // 选择图片回调

  int? get count => images?.length;
  List<String>? get imgList => count! >= limit ? images?.sublist(0, limit) : images;

  // 创建可点击预览图片
  createImage(BuildContext context, String img, int key) {
    return GestureDetector(
      child: Hero(
        tag: 'image_${key}_$img', // 放大缩小动画效果标识
        child: img == '+' ? 
        Container(color: Colors.transparent, child: const Icon(Icons.add, size: 30.0, color: Colors.black45),)
        :
        Utils.isUrl(img) ?
        Image.network(
          img,
          width: width,
          fit: BoxFit.contain,
        )
        :
        Image.asset(
          img,
          width: width,
          fit: BoxFit.contain,
        ),
      ),
      onTap: () {
        // 选择图片
        if(img == '+') {
          onChoose!();
        }else {
          Navigator.of(context).push(FadeRoute(route: ImageViewer(
            images: album ? imgList!.sublist(0, imgList!.length - 1) : imgList,
            index: key,
            heroTags: imgList!.asMap().entries.map((e) => 'image_${e.key}_${e.value}').toList(),
          )));
        }
      },
    );
  }

  @override
  Widget build(BuildContext context){
    // 一张图片
    if(count == 1 && !album) {
      return SizedBox(
        width: width,
        child: createImage(context, imgList![0], 0),
      );
    }

    if(album && count! < limit) {
      imgList?.add('+');
    }
    
    // 多张图片
    return GridView(
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        // 横轴元素个数
        crossAxisCount: 3,
        // 纵轴间距
        mainAxisSpacing: 5.0,
        // 横轴间距
        crossAxisSpacing: 5.0,
        // 子组件宽高比例
        childAspectRatio: 1,
      ),
      children: imgList!.asMap().entries.map((e) {
        return Container(
          color: Colors.grey[100],
          child: createImage(context, e.value, e.key),
        );
      }).toList(),
    );
  }
}

flutter3聊天功能

文本框TextField设置maxLines: null即可实现多行文本输入,支持图文emoj混排,网址连接识别等功能。

复制代码
// 输入框
Offstage(
  offstage: voiceBtnEnable,
  child: ConstrainedBox(
    constraints: BoxConstraints(maxHeight: 300.0),
    child: TextField(
      decoration: InputDecoration(
        isDense: true,
        hoverColor: Colors.transparent,
        border: OutlineInputBorder(borderSide: BorderSide.none),
        contentPadding: EdgeInsets.fromLTRB(10, 0, 10, 0)
      ),
      style: const TextStyle(fontSize: 16.0,),
      maxLines: null,
      controller: editorController,
      focusNode: editorFocusNode,
      cursorColor: const Color(0xFF07C160),
      onChanged: (value) {},
    ),
  ),
)
复制代码
// 语音
Offstage(
  offstage: !voiceBtnEnable,
  child: GestureDetector(
    child: Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(5),
      ),
      alignment: Alignment.center,
      height: 40.0,
      width: double.infinity,
      child: Text(voiceTypeMap[voiceType], style: const TextStyle(fontSize: 15.0),),
    ),
    onPanStart: (details) {
      setState(() {
        voiceType = 1;
        voicePanelEnable = true;
      });
    },
    onPanUpdate: (details) {
      Offset pos = details.globalPosition;
      double swipeY = MediaQuery.of(context).size.height - 120;
      double swipeX = MediaQuery.of(context).size.width / 2 + 50;
      setState(() {
        if(pos.dy >= swipeY) {
          voiceType = 1; // 松开发送
        }else if (pos.dy < swipeY && pos.dx < swipeX) {
          voiceType = 2; // 左滑松开取消
        }else if (pos.dy < swipeY && pos.dx >= swipeX) {
          voiceType = 3; // 右滑语音转文字
        }
      });
    },
    onPanEnd: (details) {
      // print('停止录音');
      setState(() {
        switch(voiceType) {
          case 1:
            Toast.show('发送录音文件', duration: 1, gravity: 1);
            voicePanelEnable = false;
            break;
          case 2:
            Toast.show('取消发送', duration: 1, gravity: 1);
            voicePanelEnable = false;
            break;
          case 3:
            Toast.show('语音转文字', duration: 1, gravity: 1);
            voicePanelEnable = true;
            voiceToTransfer = true;
            break;
        }
        voiceType = 0;
      });
    },
  ),
)

flutter3绘制聊天箭头

复制代码
// 绘制聊天箭头
class ArrowShape extends CustomPainter {
  ArrowShape({
    required this.arrowColor,
    this.arrowSize = 7,
  });

  final Color arrowColor; // 箭头颜色
  final double arrowSize; // 箭头大小

  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()..color = arrowColor;

    var path = Path();
    path.lineTo(-arrowSize, 0);
    path.lineTo(0, arrowSize);
    path.lineTo(arrowSize, 0);
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}

综上就是Flutter3+Dart3实战仿微信App聊天项目的一些知识分享,感谢大家的阅读与支持!

附上几个最新项目实例

最新版uniapp+vue3+uv-ui跨三端短视频+直播+聊天【H5+小程序+App端】

Uniapp-DeepSeek跨三端AI助手|uniapp+vue3+deepseek-v3流式ai聊天模板

Electron35-DeepSeek桌面端AI系统|vue3.5+electron+arco客户端ai模板

vue3-webseek网页版AI问答|Vite6+DeepSeek+Arco流式ai聊天打字效果

flutter3-dymall仿抖音直播商城|Flutter3.27短视频+直播+聊天App实例

tauri2.0-admin桌面端后台系统|Tauri2+Vite5+ElementPlus管理后台EXE程序

Tauri2.0+Vite5聊天室|vue3+tauri2+element-plus仿微信|tauri聊天应用

Electron32-ViteOS桌面版os系统|vue3+electron+arco客户端OS管理模板

uniapp+vue3聊天室|uni-app+vite4+uv-ui跨端仿微信app聊天语音/朋友圈

相关推荐
xiaoyan20171 年前
flutter3+dart3聊天室|Flutter3跨平台仿微信App语音聊天/朋友圈
flutter3+dart3聊天·flutter3仿微信·flutter3聊天室·flutter3朋友圈·flutter3语音聊天