20

基于Flutter+Dart聊天实例|flutter仿微信界面聊天室

 3 years ago
source link: https://segmentfault.com/a/1190000022625399
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1、项目介绍

Flutter 是目前比较流行的跨平台开发技术,凭借其出色的性能获得很多前端技术爱好者的关注,比如 阿里闲鱼美团腾讯 等大公司都有投入相关案例生产使用。

flutter_chatroom项目 是基于 Flutter+Dart+chewie+photo_view+image_picker 等技术开发的跨平台仿微信app聊天界面应用,实现了消息/表情发送、图片预览、长按菜单、红包/小视频/朋友圈等功能。

MF7ruuv.png!web

2、技术框架

  • 使用技术:Flutter 1.12.13/Dart 2.7.0
  • 视频组件:chewie: ^0.9.7
  • 图片/拍照:image_picker: ^0.6.6+1
  • 图片预览组件:photo_view: ^0.9.2
  • 弹窗组件:showModalBottomSheet/AlertDialog/SnackBar
  • 本地存储:shared_preferences: ^0.5.7+1
  • 字体图标:阿里iconfont字体图标库

Zzmq6fN.png!web

u6BZ7vI.png!web

yiyqyyB.png!web

3uABnye.png!web

AvUreyV.png!web

zeuIFbZ.png!web

aANzqyN.png!web

Vz67jey.png!web

VFBvime.png!web

VneEjef.png!web

7zaYVvm.png!web

FVrAJvJ.png!web

uqUN7fJ.png!web

FRZfIvF.png!web

E7zIJr3.png!web

aE3UraQ.png!web

v6vMZfy.png!web

ANZ7Fbr.png!web

鉴于flutter基于dart语言,需要安装 Dart Sdk / Flutter Sdk ,至于如何搭建开发环境,可以去官网查阅文档资料

https://flutter.cn/

https://flutterchina.club/

https://pub.flutter-io.cn/

https://www.dartcn.com/

使用vscode编辑器,可先安装 DartFlutterFlutter widget snippets 等扩展插件

3、flutter沉浸式状态栏/底部tabbar

flutter中如何实现顶部全背景沉浸式透明状态栏(去掉状态栏黑色半透明背景),去掉右上角banner,可以去看这篇文章

https://segmentfault.com/a/11...

4、flutter图标组件/IconData自定义封装组件

Icon(Icons.search) 
Icon(IconData(0xe60e, fontFamily:'iconfont'), size:24.0)

使用第二种方式需要先下载阿里图标库字体文件,然后在pubspec.yaml中引入字体

7ZnEB3I.png!web

class GStyle {
    // __ 自定义图标
    static iconfont(int codePoint, {double size = 16.0, Color color}) {
        return Icon(
            IconData(codePoint, fontFamily: 'iconfont', matchTextDirection: true),
            size: size,
            color: color,
        );
    }
}

调用非常简单,可自定义颜色、字体大小;

GStyle.iconfont(0xe635, color: Colors.orange, size: 17.0)

5、flutter实现badge红点/圆点提示

Enqyuyz.png!web

如上图:在flutter中没有圆点提示组件,需要自己封装实现;

class GStyle {
    // 消息红点
    static badge(int count, {Color color = Colors.red, bool isdot = false, double height = 18.0, double width = 18.0}) {
        final _num = count > 99 ? '···' : count;
        return Container(
            alignment: Alignment.center, height: !isdot ? height : height/2, width: !isdot ? width : width/2,
            decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(100.0)),
            child: !isdot ? Text('$_num', style: TextStyle(color: Colors.white, fontSize: 12.0)) : null
        );
    }
}

支持自定义红点大小、颜色,默认数字超过99就...显示;

GStyle.badge(0, isdot:true)
GStyle.badge(13)

GStyle.badge(29, color: Colors.orange, height: 15.0, width: 15.0)

6、flutter长按自定义弹窗

  • 在flutter中如何实现长按,并在长按位置弹出菜单,类似微信消息长按弹窗效果;

7ZvaQrI.png!web

通过 InkWell 组件提供的 onTapDown 事件获取坐标点实现

InkWell(
    splashColor: Colors.grey[200],
    child: Container(...),
    onTapDown: (TapDownDetails details) {
        _globalPositionX = details.globalPosition.dx;
        _globalPositionY = details.globalPosition.dy;
    },
    onLongPress: () {
        _showPopupMenu(context);
    },
),
// 长按弹窗
double _globalPositionX = 0.0; //长按位置的横坐标
double _globalPositionY = 0.0; //长按位置的纵坐标
void _showPopupMenu(BuildContext context) {
    // 确定点击位置在左侧还是右侧
    bool isLeft = _globalPositionX > MediaQuery.of(context).size.width/2 ? false : true;
    // 确定点击位置在上半屏幕还是下半屏幕
    bool isTop = _globalPositionY > MediaQuery.of(context).size.height/2 ? false : true;

    showDialog(
      context: context,
      builder: (context) {
        return Stack(
          children: <Widget>[
            Positioned(
              top: isTop ? _globalPositionY : _globalPositionY - 200.0,
              left: isLeft ? _globalPositionX : _globalPositionX - 120.0,
              width: 120.0,
              child: Material(
                ...
              ),
            )
          ],
        );
      }
    );
}
  • flutter如何实现去掉AlertDialog弹窗大小限制?

可通过 SizedBox 和无限制容器 UnconstrainedBox 组件实现

void _showCardPopup(BuildContext context) {
    showDialog(
      context: context,
      builder: (context) {
        return UnconstrainedBox(
          constrainedAxis: Axis.vertical,
          child: SizedBox(
            width: 260,
            child: AlertDialog(
              content: Container(
                ...
              ),
              elevation: 0,
              contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
            ),
          ),
        );
      }
    );
}

7、flutter登录/注册表单|本地存储

flutter提供了两个文本框组件: TextFieldTextFormField
本文是使用 TextField 实现,并在文本框后添加清空文本框/密码查看图标

TextField(
  keyboardType: TextInputType.phone,
  controller: TextEditingController.fromValue(TextEditingValue(
    text: formObj['tel'],
    selection: new TextSelection.fromPosition(TextPosition(affinity: TextAffinity.downstream, offset: formObj['tel'].length))
  )),
  decoration: InputDecoration(
    hintText: "请输入手机号",
    isDense: true,
    hintStyle: TextStyle(fontSize: 14.0),
    suffixIcon: Visibility(
      visible: formObj['tel'].isNotEmpty,
      child: InkWell(
        child: GStyle.iconfont(0xe69f, color: Colors.grey, size: 14.0), onTap: () {
          setState(() { formObj['tel'] = ''; });
        }
      ),
    ),
    border: OutlineInputBorder(borderSide: BorderSide.none)
  ),
  onChanged: (val) {
    setState(() { formObj['tel'] = val; });
  },
)

TextField(
  decoration: InputDecoration(
    hintText: "请输入密码",
    isDense: true,
    hintStyle: TextStyle(fontSize: 14.0),
    suffixIcon: InkWell(
      child: Icon(formObj['isObscureText'] ? Icons.visibility_off : Icons.visibility, color: Colors.grey, size: 14.0),
      onTap: () {
        setState(() {
          formObj['isObscureText'] = !formObj['isObscureText'];
        });
      },
    ),
    border: OutlineInputBorder(borderSide: BorderSide.none)
  ),
  obscureText: formObj['isObscureText'],
  onChanged: (val) {
    setState(() { formObj['pwd'] = val; });
  },
)

验证消息提示则是使用flutter提供的SnackBar实现

// SnackBar提示
final _scaffoldkey = new GlobalKey<ScaffoldState>();
void _snackbar(String title, {Color color}) {
    _scaffoldkey.currentState.showSnackBar(SnackBar(
      backgroundColor: color ?? Colors.redAccent,
      content: Text(title),
      duration: Duration(seconds: 1),
    ));
}

另外本地存储使用的是 shared\_preferences ,至于如何使用可参看

https://pub.flutter-io.cn/packages/shared_preferences
void handleSubmit() async {
    if(formObj['tel'] == '') {
      _snackbar('手机号不能为空');
    }else if(!Util.checkTel(formObj['tel'])) {
      _snackbar('手机号格式有误');
    }else if(formObj['pwd'] == '') {
      _snackbar('密码不能为空');
    }else {
      // ...接口数据

      // 设置存储信息
      final prefs = await SharedPreferences.getInstance();
      prefs.setBool('hasLogin', true);
      prefs.setInt('user', int.parse(formObj['tel']));
      prefs.setString('token', Util.setToken());

      _snackbar('恭喜你,登录成功', color: Colors.greenAccent[400]);
      Timer(Duration(seconds: 2), (){
        Navigator.pushNamedAndRemoveUntil(context, '/tabbarpage', (route) => route == null);
      });
    }
}

8、flutter聊天页面功能

eYJvInM.png!web

  • 在flutter中如何实现类似上图编辑器功能?通过TextField提供的多行文本框属性 maxLines 就可实现。
Container(
    margin: GStyle.margin(10.0),
    decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(3.0)),
    constraints: BoxConstraints(minHeight: 30.0, maxHeight: 150.0),
    child: TextField(
        maxLines: null,
        keyboardType: TextInputType.multiline,
        decoration: InputDecoration(
          hintStyle: TextStyle(fontSize: 14.0),
          isDense: true,
          contentPadding: EdgeInsets.all(5.0),
          border: OutlineInputBorder(borderSide: BorderSide.none)
        ),
        controller: _textEditingController,
        focusNode: _focusNode,
        onChanged: (val) {
          setState(() {
            editorLastCursor = _textEditingController.selection.baseOffset;
          });
        },
        onTap: () {handleEditorTaped();},
    ),
),
  • flutter实现滚动聊天信息到最底部

通过ListView里controller属性提供的 jumpTo 方法及 _msgController.position.maxScrollExtent

ScrollController _msgController = new ScrollController();
...
ListView(
    controller: _msgController,
    padding: EdgeInsets.all(10.0),
    children: renderMsgTpl(),
)

// 滚动消息至聊天底部
void scrollMsgBottom() {
    timer = Timer(Duration(milliseconds: 100), () => _msgController.jumpTo(_msgController.position.maxScrollExtent));
}

行了,基于flutter/dart开发聊天室实例就介绍到这里,希望能喜欢~~:muscle::muscle:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK