flutter3_wchat全新跨平台flutter3.0仿微信app聊天应用。
基于多端跨平台技术
flutter3+dart3+materialUI+shared_preferences+easy_refresh
全新研发的一款仿微信app应用聊天实战项目。实现发送文字emoj消息+gif动图、长按仿微信发送语音、图片预览、红包及朋友圈等功能。
使用技术
编码工具:vscode
技术框架:flutter3.16.5+dart3.2.3
UI组件库:material-design3
弹窗组件:showDialog/SimpleDialog/showModalBottomSheet/AlertDialog
图片预览:photo_view^0.14.0
缓存组件:shared_preferences^2.2.2
下拉组件:easy_refresh^3.3.4
toast提示框:toast^0.3.0
网址预览组件:url_launcher^6.2.4
flutter3开发的应用支持运行到 android/ios/macos/linux/windows/web 等多个平台。
项目结构
通过flutter create xxx
命令创建项目,目录结构如下图:
导航栏采用全屏沉浸式,支持透明背景图。
在项目开发初期,大家需要自行配置flutter/dart开发环境SDK。
https://flutter.dev/
https://flutter.cn/
https://pub.flutter-io.cn/
https://www.dartcn.com/
如果使用vscode开发项目,可自行按住flutter/dart扩展。
通过flutter doctor
来检查所需环境是否缺失。一切准备就绪接 下来就是愉快的开发了。
既然看到了这里,是不是感觉还不错~~😁
开发初期是在windows调试比较方便,也可以运行到web上。后期可以直接打包apk运行到手机上。
运行到桌面默认是1280大小,大家可以修改windows/runner/main.cpp文件里面窗口尺寸,适应手机端。
如果大家习惯在模拟器上面运行调试项目,下面列出了一些常见的模拟器及端口连接。
通过adb connect
连接上模拟器之后,flutter devices
查看当前连接设备,执行flutter run
命令即可运行项目到指定的模拟器。
入口main.dart
/// flutter入口文件import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:toast/toast.dart';// 引入公共样式
import 'styles/index.dart';// 引入底部tabbar
import 'components/tabbar.dart';// 引入路由管理
import 'router/index.dart';// 错误模块
import '../views/error/index.dart';void main() {runApp(const MyApp());
}DateTime? lastPopTime;class MyApp extends StatelessWidget {const MyApp({ super.key });// 退出app提示Future<bool> appOnPopInvoked(didPop) async {if(lastPopTime == null || DateTime.now().difference(lastPopTime!) > const Duration(seconds: 2)) {lastPopTime = DateTime.now();Toast.show('再按一次退出应用');return false;}SystemNavigator.pop();return true;}@overrideWidget build(BuildContext context){ToastContext().init(context);return MaterialApp(title: 'Flutter Chat',debugShowCheckedModeBanner: false,theme: ThemeData(primaryColor: FStyle.primaryColor,useMaterial3: true,// windows桌面端字体粗细不一样fontFamily: Platform.isWindows ? 'Microsoft YaHei' : null,),// home: const FTabBar(),home: PopScope(// canPop: false,onPopInvoked: appOnPopInvoked,child: const FTabBar(),),// 初始路由// initialRoute: '/',// 自定义路由onGenerateRoute: onGenerateRoute,// 错误路由onUnknownRoute: (settings) {return MaterialPageRoute(builder: (context) => const Error());},);}
}
Flutter3圆角/渐变背景色
如上图:flutter实现文本框圆角及按钮圆角渐变色。
Container(height: 40.0,margin: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 30.0),decoration: BoxDecoration(color: Colors.white,border: Border.all(color: const Color(0xffdddddd)),borderRadius: BorderRadius.circular(15.0),),child: Row(children: [Expanded(child: TextField(keyboardType: TextInputType.phone,controller: fieldController,decoration: InputDecoration(hintText: '输入手机号',suffixIcon: Visibility(visible: authObj['tel'].isNotEmpty,child: InkWell(hoverColor: Colors.transparent,highlightColor: Colors.transparent,splashColor: Colors.transparent,onTap: handleClear,child: const Icon(Icons.clear, size: 16.0,),)),contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 12.0),border: const OutlineInputBorder(borderSide: BorderSide.none),),onChanged: (value) {setState(() {authObj['tel'] = value;});},),)],),
),
通过Container组件gradient实现渐变色。
Container(margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 30.0),decoration: BoxDecoration(borderRadius: BorderRadius.circular(15.0),// 自定义按钮渐变色gradient: const LinearGradient(begin: Alignment.topLeft,end: Alignment.bottomRight,colors: [Color(0xFF0091EA), Color(0xFF07C160)],)),child: SizedBox(width: double.infinity,height: 45.0,child: FilledButton(style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.transparent),shadowColor: MaterialStateProperty.all(Colors.transparent),shape: MaterialStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)))),onPressed: handleSubmit,child: const Text('登录', style: TextStyle(fontSize: 18.0),),),)
),
flutter3登录验证60s倒计时
Timer? timer;
String vcodeText = '获取验证码';
bool disabled = false;
int time = 60;// 60s倒计时
void handleVcode() {if(authObj['tel'] == '') {snackbar('手机号不能为空');}else if(!Utils.checkTel(authObj['tel'])) {snackbar('手机号格式不正确');}else {setState(() {disabled = true;});startTimer();}
}
startTimer() {timer = Timer.periodic(const Duration(seconds: 1), (timer) {setState(() {if(time > 0) {vcodeText = '获取验证码(${time--})';}else {vcodeText = '获取验证码';time = 60;disabled = false;timer.cancel();}});});snackbar('短信验证码已发送,请注意查收', color: Colors.green);
}
Flutter3全屏沉浸导航状态栏渐变
由于AppBar
提供的background
属性不能设置渐变颜色,但是可以使用flexibleSpace
属性 配合Container组件gradient
实现导航栏渐变背景色。
AppBar(title: Text('Flutter3-Chat'),flexibleSpace: Container(decoration: const BoxDecoration(gradient: LinearGradient(begin: Alignment.topLeft,end: Alignment.bottomRight,colors: [Color(0xFF0091EA), Color(0xFF07C160)],)),)
),
flutter自定义badge红点/iconfont图标
FStyle.badge(23)
FStyle.badge(2, color: Colors.pink, height: 10.0, width: 10.0)
FStyle.badge(0, isdot: true)
自定义图标使用的阿里iconfont图表库。
在pubspec.yaml中引入字体文件。
class FStyle {// 自定义iconfont图标static iconfont(int codePoint, {double size = 16.0, Color? color}) {return Icon(IconData(codePoint, fontFamily: 'iconfont', matchTextDirection: true),size: size,color: color,);}// 自定义Badge红点static badge(int count, {Color color = Colors.redAccent,bool isdot = false,double height = 18.0,double width = 18.0}) {final num = count > 99 ? '99+' : count;return Container(alignment: Alignment.center,height: isdot ? height / 2 : height,width: isdot ? width / 2 : width,decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(100.00)),child: isdot ? null : Text('$num', style: const TextStyle(color: Colors.white, fontSize: 12.0)),);}
}
Flutter3仿微信快捷下拉菜单
通过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;}},
)// 下拉菜单项
static popupMenuItem(int codePoint, String title, value) {return PopupMenuItem(value: value,child: Row(mainAxisAlignment: MainAxisAlignment.start,children: [const SizedBox(width: 10.0,),FStyle.iconfont(codePoint, size: 21.0, color: Colors.white),const SizedBox(width: 10.0,),Text(title, style: const TextStyle(fontSize: 16.0, color: Colors.white),),],),);
}
flutter弹窗展示
项目中使用到了flutter各种弹窗应用场景。
// 关于弹窗
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实现微信群聊九宫格图像组
支持1-9张图片不同的排列组合。
const size = 44.0; // 默认一张图片尺寸
const padding = 2.0;
const margin = 2.0;int row = 0; // 图片行数
int col = 0; // 图片列数class GroupAvatar extends StatelessWidget {const GroupAvatar({super.key,this.avatars,});final List<String>? avatars;// 创建图片createImage(String avatar, double width) {return Image.asset(avatar,height: width,width: width,fit: BoxFit.fill,);}// 顶部一张图片avatarOne(List<Widget> stacks, Widget child, int count, i, imgWidth, left, top) {// ...}// 顶部两张图片avatarTwo(List<Widget> stacks, Widget child, int count, i, imgWidth, left, top) {// ...}// 其它情况avatarOther(List<Widget> stacks, Widget child, int count, i, imgWidth, left, top, colMax) {// ...}@overrideWidget build(BuildContext context){var count = avatars?.length;int colMax;List<Widget> icons = [];List<Widget> stacks = [];double imgWidth;if(count! == 1) {return SizedBox(width: size,height: size,child: createImage(avatars![0], size),);}if(count >= 5) {colMax = 3;imgWidth = (size - (padding * colMax) - margin) / colMax;}else {colMax = 2;imgWidth = (size - (padding * colMax) - margin) / colMax;}for(var i = 0; i < count; i++) {icons.add(createImage(avatars![i], imgWidth));}row = 0;col = 0;var centerTop = 0.0;if(count == 2 || count == 5 || count == 6) {centerTop = (imgWidth + margin) / 2;}for(var i = 0; i < count; i++) {var left = imgWidth * row + padding * (row + 1);var top = imgWidth * col + padding * col + centerTop;switch(count) {case 3:case 7:avatarOne(stacks, icons[i], count, i, imgWidth, left, top);break;case 5:case 8:avatarTwo(stacks, icons[i], count, i, imgWidth, left, top);break;default:avatarOther(stacks, icons[i], count, i, imgWidth, left, top, colMax);break;}}return Container(width: size,height: size,color: const Color(0xFFEEEEEE),padding: const EdgeInsets.only(top: padding),child: Stack(children: stacks,),);}
}
flutter聊天功能模块
// 输入框
Offstage(offstage: voiceBtnEnable,child: TextField(decoration: const InputDecoration(isDense: true,hoverColor: Colors.transparent,contentPadding: EdgeInsets.all(8.0),border: OutlineInputBorder(borderSide: BorderSide.none),),style: const TextStyle(fontSize: 16.0,),maxLines: null,controller: editorController,focusNode: editorFocusNode,cursorColor: const Color(0xFF07C160),onChanged: (value) {},),
),
文本框支持文字/emoj输入,多行文本输入。输入链接,消息自动转换为网址通过浏览器打开。
仿微信按住说话语音面板模块。
// 语音
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绘制箭头,flutter提供了自定义绘图画板能力。
// 绘制气泡箭头
class ArrowShape extends CustomPainter {ArrowShape({required this.arrowColor,this.arrowSize = 7,});final Color arrowColor; // 箭头颜色final double arrowSize; // 箭头大小@overridevoid 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);}@overridebool shouldRepaint(CustomPainter oldDelegate) {return false;}
}
OK,综上就是flutter3/dart3开发跨平台聊天应用的一些知识分享。
最后附上两个最近的实例项目
-
基于uni-app+vue3+pinia跨端仿抖音直播商城
https://blog.csdn.net/yanxinyun1990/article/details/135329724 -
react18+electron27桌面端macos管理系统
https://blog.csdn.net/yanxinyun1990/article/details/134567716