时思美 发表于 2025-7-11 10:07:01

最新版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, 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),
      );
    }

    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,
          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, 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聊天语音/朋友圈

 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 最新版Flutter3.32+Dart3.8跨平台仿微信app聊天界面|朋友圈