flutter3-wchat:基于flutter3.x/dart3+material-ui聊天|flutter3仿微信

全新研发 flutter3+dart3 跨多平台仿微信 App 界面聊天 Flutter3-Chat。

使用技术#

  • 框架技术: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

项目结构框架#

项目搭建#

前期大家可以编译到 windows 调试,后期再 release 到手机。
运行到 windows 默认窗口大小为 1280x720,可以修改 windows/runner/main.cpp 文件里面的窗口尺寸。

flutter3-wchat:基于flutter3.x/dart3+material-ui聊天|flutter3仿微信app|flutter朋友圈

选择模拟器调试。目前市面上有很多类型模拟器,他们使用 adb 连接时都会有不同的默认端口,下面列出了一些常用的模拟器及端口号。通过 adb connect 连接上指定模拟器之后,执行 flutter run 命令即可运行项目到模拟器上面。

flutter3-wchat:基于flutter3.x/dart3+material-ui聊天|flutter3仿微信app|flutter朋友圈

使用 flutter create project 命令快速创建一个 flutter 应用。

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),),
    ),
  )
),

flutter 实现渐变导航条#

flutter3-wchat:基于flutter3.x/dart3+material-ui聊天|flutter3仿微信app|flutter朋友圈

配置 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)
        ],
      )
    ),
  )
),

flutter 自定义 iconfont 字体及 badge#

flutter 提供了非常丰富的字体图标,通过图标组件 Icon(Icons.add) 引入即可使用。

api.flutter-io.cn/flutter/material...

另外还支持通过自定义 IconData 方式自定义图标,如使用阿里 iconfont 图表库图标。

Icon(IconData(0xe666, fontFamily: 'iconfont'), size: 18.0)

把下载的字体文件放到 assets 目录

flutter3-wchat:基于flutter3.x/dart3+material-ui聊天|flutter3仿微信app|flutter朋友圈

pubspec.yaml 中引入字体

flutter3-wchat:基于flutter3.x/dart3+material-ui聊天|flutter3仿微信app|flutter朋友圈

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-wchat:基于flutter3.x/dart3+material-ui聊天|flutter3仿微信app|flutter朋友圈

下拉刷新、上拉加载更多是通过 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(
        // ...
      );
    },
  ),
)

flutter3-wchat:基于flutter3.x/dart3+material-ui聊天|flutter3仿微信app|flutter朋友圈

GroupZone(images: item['images']),

GroupZone(
  images: uploadList,
  album: true,
  onChoose: () async {
    Toast.show('选择手机相册图片', duration: 2, gravity: 1);
  },
),

通过 photo_view 实现九宫格预览大图功能。

import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';

class ImageViewer extends StatefulWidget {
  const ImageViewer({
    super.key,
    this.images,
    this.index = 0,
  });

  final List? images; // 预览图列表
  final int index; // 当前预览图索引

  @override
  State<ImageViewer> createState() => _ImageViewerState();
}

class _ImageViewerState extends State<ImageViewer> {
  int currentIndex = 0;

  @override
  void initState() {
    super.initState();
    currentIndex = widget.index;
  }

  @override
  Widget build(BuildContext context) {
    var imgCount = widget.images?.length;

    return Scaffold(
      body: Stack(
        children: [
          Positioned(
            top: 0,
            left: 0,
            bottom: 0,
            right: 0,
            child: GestureDetector(
              child: imgCount == 1 ? PhotoView(
                imageProvider: AssetImage(widget.images![0]),
                backgroundDecoration: const BoxDecoration(
                  color: Colors.black,
                ),
                minScale: PhotoViewComputedScale.contained,
                maxScale: PhotoViewComputedScale.covered * 2,
                heroAttributes: PhotoViewHeroAttributes(tag: widget.images![0]),
                enableRotation: true,
              )
              :
              PhotoViewGallery.builder(
                itemCount: widget.images?.length,
                builder: (context, index) {
                  return PhotoViewGalleryPageOptions(
                    imageProvider: AssetImage(widget.images![index]),
                    minScale: PhotoViewComputedScale.contained,
                    maxScale: PhotoViewComputedScale.covered * 2,
                    heroAttributes: PhotoViewHeroAttributes(tag: widget.images![index]),
                  );
                },
                scrollPhysics: const BouncingScrollPhysics(),
                backgroundDecoration: const BoxDecoration(
                  color: Colors.black,
                ),
                pageController: PageController(initialPage: widget.index),
                enableRotation: true,
                onPageChanged: (index) {
                  setState(() {
                    currentIndex = index;
                  });
                },
              ),
              onTap: () {
                Navigator.of(context).pop();
              },
            ),
          ),
          // 图片索引index
          Positioned(
            top: MediaQuery.of(context).padding.top + 15,
            width: MediaQuery.of(context).size.width,
            child: Center(
              child: Visibility(
                visible: imgCount! > 1 ? true : false,
                child: Text('${currentIndex+1} / ${widget.images?.length}', style: const TextStyle(color: Colors.white)),
              )
            ),
          ),
        ],
      ),
    );
  }
}

flutter 语音聊天模块#

flutter3-wchat:基于flutter3.x/dart3+material-ui聊天|flutter3仿微信app|flutter朋友圈

// 语音
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.x+dart3 实现微信 app 聊天的一些知识分享。

博客:uniapp-welive:基于 uni-app+vue3+pinia 多端直播商城实例 | uniapp 仿抖...
博客:Electron-React-MacOs 基于 electron27+react18 客户端 os 系统

本作品采用《CC 协议》,转载必须注明作者和本文链接
本文为原创文章,未经作者允许不得转载,欢迎大家一起交流 QQ(282310962) wx(xy190310)
文章
70
粉丝
54
喜欢
108
收藏
60
排名:352
访问:2.7 万
私信
所有博文
博客标签
展开
社区赞助商