flutter简易聊天页面

首页 home_page

import 'package:fchatapp/src/pages/group/group_page.dart';
import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // 输入框控制器
  TextEditingController nameController = TextEditingController();

  // form的key
  final formKey = GlobalKey<FormState>();

  // user唯一标识
  var uuid = Uuid();

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: const Text('Group Chat App'),
      ),
      body: Center(
        child: TextButton(
          onPressed: () => showDialog(
            context: context,
            builder: (BuildContext context) => AlertDialog(
              title: Text('请输入你的昵称'),
              // 在form里才能触发校验
              content: Form(
                key: formKey,
                child: TextFormField(
                  // 输入框
                  controller: nameController,
                  // 校验
                  validator: (value) {
                    if (value == null || value.length < 3) {
                      return 'User must provide name(at least 3 char)';
                    }
                    return null;
                  },
                ),
              ),
              actions: [
                TextButton(
                  onPressed: () {
                    // 清空表单
                    nameController.clear();
                    // 从navigation里弹出当前页(dialog)
                    Navigator.pop(context);
                  },
                  child: Text(
                    '取消',
                    style: TextStyle(
                      fontSize: 16,
                      color: Colors.green,
                    ),
                  ),
                ),
                TextButton(
                  onPressed: () {
                    // 如果校验不通过
                    if (!formKey.currentState!.validate()) {
                      return;
                    }
                    String name = nameController.text;
                    // 清空表单
                    nameController.clear();
                    // 关闭dialog(否则当从group_page返回时,dialog依然存在)
                    Navigator.pop(context);
                    Navigator.push(
                      context,
                      // 跳转到group_page
                      MaterialPageRoute(
                        builder: (context) => GroupPage(
                          name: name,
                          userId: uuid.v1(),
                        ),
                      ),
                    );
                  },
                  child: Text(
                    '进入',
                    style: TextStyle(
                      fontSize: 16,
                      color: Colors.blue,
                    ),
                  ),
                ),
              ],
            ),
          ),
          child: Text(
            '进入聊天室',
            style: TextStyle(
              color: Colors.teal,
              fontSize: 16,
            ),
          ),
        ),
      ),
    );
  }
}

依赖(socketio 2.0 对应 node 的 socketio 服务器 3.x 和 4.x)

image.png

other_msg_widget.dart

import 'package:flutter/material.dart';

class OtherMsgWidget extends StatelessWidget {
  final String sender;
  final String msg;

  const OtherMsgWidget({Key? key, required this.msg, required this.sender})
      : super(key: key);

  
  Widget build(BuildContext context) {
    return Align(
      // 居于右下侧
      alignment: Alignment.bottomLeft,
      child: ConstrainedBox(
        constraints: BoxConstraints(
          // 当文字占满一行,让container不要占整个屏幕
          maxWidth: MediaQuery.of(context).size.width - 60,
        ),
        child: Card(
          // 圆角边框
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(10),
          ),
          color: Colors.orange,
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
            child: Column(
              // 对齐
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                // 昵称
                Text(
                  sender,
                  style: const TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.bold,
                    color: Colors.red,
                  ),
                ),
                // 间隔
                const SizedBox(
                  height: 3,
                ),
                // 信息
                Text(
                  msg,
                  style: const TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.bold,
                    color: Colors.black,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

own_msg_widget.dart

import 'package:flutter/material.dart';

class OwnMsgWidget extends StatelessWidget {
  final String sender;
  final String msg;

  const OwnMsgWidget({Key? key, required this.msg, required this.sender})
      : super(key: key);

  
  Widget build(BuildContext context) {
    return Align(
      // 居于右下侧
      alignment: Alignment.bottomRight,
      child: ConstrainedBox(
        constraints: BoxConstraints(
          // 当文字占满一行,让container不要占整个屏幕
          maxWidth: MediaQuery.of(context).size.width - 60,
        ),
        child: Card(
          // 圆角边框
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(10),
          ),
          color: Colors.teal,
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
            child: Column(
              // 对齐
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                // 昵称
                Text(
                  sender,
                  style: const TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.bold,
                    color: Colors.yellow,
                  ),
                ),
                // 间隔
                const SizedBox(
                  height: 3,
                ),
                // 信息
                Text(
                  msg,
                  style: const TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.bold,
                    color: Colors.white,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

group_page.dart

import 'package:fchatapp/src/foundation/msg_widget/other_msg_widget.dart';
import 'package:fchatapp/src/foundation/msg_widget/own_msg_widget.dart';
import 'package:fchatapp/src/pages/group/msg_model.dart';
import 'package:flutter/material.dart';
import 'package:socket_io_client/socket_io_client.dart' as IO;

class GroupPage extends StatefulWidget {
  // 用户名称
  final String name;
  final String userId;

  const GroupPage({Key? key, required this.name, required this.userId})
      : super(key: key);

  
  State<GroupPage> createState() => _GroupPageState();
}

class _GroupPageState extends State<GroupPage> {
  IO.Socket? socket;
  List<MsgMoel> msgList = [];
  TextEditingController _msgController = TextEditingController();

  // 初始化时执行
  
  void initState() {
    connect();
  }

  void connect() {
    // 本地测试,记得在同一个网络(热点/wifi)下
    socket = IO.io("http://192.168.2.228:3000", <String, dynamic>{
      'transports': ['websocket'],
      'autoConnect': false,
    });
    socket!.connect();
    print("we're here");
    socket!.onConnect((_) {
      print("connect");
      socket!.on("broadcastMsg", (model) {
        print(model);
        // 动态改变list,才能触发页面变化
        setState(() {
          // 不是我们发的信息才添加到列表
          if (model['userId'] != widget.userId) {
            setState(() {
              // 形参是动态类型(dynamic),不能直接作为msgModel加到list里
              msgList.add(
                MsgMoel(
                  msg: model['msg'],
                  type: model["type"],
                  sender: model['senderName'],
                ),
              );
            });
          }
        });
      });
    });
  }

  // 通过服务器发送msg
  void sendMsg(String msg, String senderName) {
    MsgMoel ownMsg = MsgMoel(msg: msg, type: "ownMsg", sender: senderName);
    msgList.add(ownMsg);
    setState(() {
      msgList;
    });
    // 触发msg事件
    socket!.emit(
      'sendMsg',
      {
        "type": "ownMsg",
        "msg": msg,
        "senderName": senderName,
        "userId": widget.userId,
      },
    );
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text('Anomynous Group'),
      ),
      body: Column(
        children: [
          // Expanded占据所有剩余空间
          Expanded(
            child: ListView.builder(
              itemCount: msgList.length,
              itemBuilder: (context, index) {
                if (msgList[index].type == "ownMsg") {
                  return OwnMsgWidget(
                      msg: msgList[index].msg, sender: msgList[index].sender);
                } else {
                  return OtherMsgWidget(
                      msg: msgList[index].msg, sender: msgList[index].sender);
                }
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),
            child: Row(
              children: <Widget>[
                // Expanded占据所有剩余空间
                Expanded(
                  child: TextFormField(
                    controller: _msgController,
                    // 边框样式
                    decoration: const InputDecoration(
                      // placeholder
                      hintText: "在这里输入内容...",
                      border: OutlineInputBorder(
                        borderSide: BorderSide(
                          width: 2,
                        ),
                      ),
                    ),
                  ),
                ),
                IconButton(
                    onPressed: () {
                      String msg = _msgController.text;
                      // 清空表单
                      _msgController.clear();
                      if (msg.isEmpty) {
                        return;
                      }
                      // 发信息
                      sendMsg(msg, widget.name);
                    },
                    icon: const Icon(
                      Icons.send,
                      color: Colors.teal,
                    ))
              ],
            ),
          ),
        ],
      ),
    );
  }
}

msg_model.dart

class MsgMoel {
  String type;
  String msg;
  String sender;

  MsgMoel({required this.msg, required this.type, required this.sender});
}

  转载请注明: malred-blog flutter简易聊天页面

 上一篇
手写redux(一种思路) 手写redux(一种思路)
初始项目 使用原生的provider和context import React, { useState, useContext } from "react"; // 创建上下文 const appContext = React.creat
下一篇 
手写pinia(一种思路) 手写pinia(一种思路)
本笔记内容来自B站up前端小野森森-1的视频创建项目vue create mypinia # 添加pinia依赖 yarn add pinia store/index.jsimport useTodoListStore from '.
  目录