flutter3-chat:基于flutter3.x+dart3聊天实例|flutter3仿微信App界面

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;
  }
  
	@override
	Widget 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) {
    // ...
  }

  @override
  Widget 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; // 箭头大小

  @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;
  }
}

OK,综上就是flutter3/dart3开发跨平台聊天应用的一些知识分享。

最后附上两个最近的实例项目

在这里插入图片描述

  • 34
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
基于 Flutter 3.x 进行仿抖音跨平台混合开发,是一种使用最新版本的Flutter框架来开发类似抖音的应用程序的方法。 Flutter是一种跨平台的开发框架,可以让开发者使用同一套代码同时在iOS和Android平台上构建高性能的应用程序。它提供了丰富的UI组件和工具,使开发过程更加简单和高效。 要实现仿抖音的跨平台混合开发,首先需要对抖音的功能和界面进行分析和设计。然后,使用Flutter框架来实现这些功能和界面Flutter提供了丰富的UI组件,如按钮、文本、图片、视频播放等,可以用于构建抖音的各种界面元素。开发者可以使用Dart语言来编写业务逻辑,同时可以使用Flutter提供的hot reload功能实时预览和调试应用程序。 为了实现跨平台开发,开发者可以使用Flutter的多平台支持。Flutter可以生成原生的iOS和Android应用程序,以及Web和桌面应用程序。这使得开发者可以在不同的平台上发布和部署仿抖音的应用程序。 在开发过程中,开发者可以使用Flutter的插件来集成各种第三方功能和服务,如视频播放、数据存储、社交分享等。这些插件可以帮助开发者更加方便地实现仿抖音的各种功能。 总之,基于Flutter 3.x进行仿抖音跨平台混合开发,是一种高效、灵活的开发方法。通过使用Flutter框架和相关工具,开发者可以快速构建出功能完善、界面精美的仿抖音应用程序,并在多个平台上进行发布和部署。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaoyan_2018

你的鼓励将是我持续作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值