Flutter Templates
使用 Back4App 在 Flutter 中构建实时聊天应用程序
35 分
介绍 创建聊天应用程序涉及管理实时数据、用户身份验证、媒体处理和高效的数据存储。在本教程中,您将学习如何在flutter中构建一个支持一对一和群组对话、消息状态和媒体共享的实时聊天应用程序。我们将使用back4app——一个由parse server提供支持的后端即服务——来处理后端功能。 到本教程结束时,您将拥有一个功能齐全的聊天应用程序,具备以下功能: 用户身份验证 安全的注册和登录流程。 实时消息传递 使用实时查询进行即时消息传递。 用户在线状态 跟踪用户的在线/离线状态。 媒体存储 在聊天中发送和接收图像。 消息历史 为用户持久化聊天记录。 先决条件 要跟随本教程,您需要: flutter sdk 安装在您的机器上。请遵循 官方 flutter 安装指南 https //flutter dev/docs/get started/install flutter 和 dart 的基本知识 一个 ide 或文本编辑器 ,例如 visual studio code 或 android studio 一个 back4app 账户 在 back4app https //www back4app com/ 注册一个免费账户 parse server sdk for flutter 添加到您的项目中 概述 我们将构建一个聊天应用程序,包含以下组件 用户认证 用户可以注册和登录 联系人列表 显示可聊天的用户列表 聊天界面 实时消息界面 媒体共享 能够发送和接收图片 在线状态 显示用户的在线/离线状态 步骤 1 – 设置 flutter 项目 1 1 创建一个新的 flutter 项目 打开终端并运行 flutter create chat app 导航到项目目录 cd chat app 1 2 添加依赖 打开 pubspec yaml 并添加以下依赖 dependencies flutter sdk flutter parse server sdk flutter ^4 0 1 image picker ^0 8 4+3 cached network image ^3 2 0 uuid ^3 0 4 运行 flutter pub get 来安装包 parse server sdk flutter 与 back4app 交互 image picker 从图库或相机中选择图片 cached network image 高效的图像加载和缓存 uuid 生成唯一标识符 步骤 2 – 设置 back4app 2 1 创建一个新的 back4app 应用程序 登录到您的 back4app 控制面板 https //dashboard back4app com/ 点击 "创建新应用" 输入您的应用程序名称,例如 "chatapp" , 然后点击 "创建" 2 2 配置类和数据模型 我们将创建以下类 用户 (默认解析类) 存储用户信息 消息 存储聊天消息 聊天室 代表用户之间的聊天 2 2 1 用户类 字段 用户名 字符串 密码 字符串 电子邮件 字符串 在线状态 布尔值 头像 文件(可选) 这个 用户 类是内置的;我们只需要确保它具有额外的字段。 2 2 2 消息类 字段 发送者 指针< 用户> 接收者 指针< 用户> 聊天房间id 字符串 内容 字符串 图片 文件(可选) 创建时间 日期时间(自动添加) 2 2 3 聊天室类 字段 聊天房间id 字符串 用户 指向< user>的数组 最后消息 字符串 更新时间 日期时间(自动更新) 2 3 获取应用凭证 导航到 应用设置 > 安全与密钥 记下你的 应用id 和 客户端密钥 步骤 3 – 在你的 flutter 应用中初始化 parse 打开 lib/main dart 并按如下方式修改它 import 'package\ flutter/material dart'; import 'package\ parse server sdk flutter/parse server sdk dart'; import 'screens/login screen dart'; // you'll create this file next void main() async { widgetsflutterbinding ensureinitialized(); const keyapplicationid = 'your application id'; const keyclientkey = 'your client key'; const keyparseserverurl = 'https //parseapi back4app com'; await parse() initialize( keyapplicationid, keyparseserverurl, clientkey keyclientkey, autosendsessionid true, debug true, ); runapp(myapp()); } class myapp extends statelesswidget { @override widget build(buildcontext context) { return materialapp( title 'chat app', theme themedata( primaryswatch colors blue, ), home loginscreen(), ); } } 替换 'your application id' 和 'your client key' 为您的实际 back4app 凭据。 步骤 4 – 实现用户认证 4 1 创建认证服务 在 lib 下创建一个名为 services 的新目录,并添加一个名为 auth service dart // lib/services/auth service dart import 'package\ parse server sdk flutter/parse server sdk dart'; class authservice { future\<parseuser?> signup(string username, string email, string password) async { var user = parseuser(username, password, email); var response = await user signup(); if (response success) { return user; } else { return null; } } future\<parseuser?> login(string username, string password) async { var user = parseuser(username, password, null); var response = await user login(); if (response success) { return user; } else { return null; } } future\<void> logout() async { var user = await parseuser currentuser() as parseuser?; await user? logout(); } future\<parseuser?> getcurrentuser() async { return await parseuser currentuser() as parseuser?; } } 4 2 创建登录和注册屏幕 创建一个名为 screens 的新目录,位于 lib 下,并添加名为 login screen dart 和 signup screen dart 的文件。 4 2 1 登录屏幕 // lib/screens/login screen dart import 'package\ flutter/material dart'; import ' /services/auth service dart'; import 'home screen dart'; import 'signup screen dart'; class loginscreen extends statelesswidget { final authservice authservice = authservice(); final texteditingcontroller usernamecontroller = texteditingcontroller(); final texteditingcontroller passwordcontroller = texteditingcontroller(); void login(buildcontext context) async { var user = await authservice login( usernamecontroller text trim(), passwordcontroller text trim(), ); if (user != null) { navigator pushreplacement( context, materialpageroute(builder (context) => homescreen()), ); } else { scaffoldmessenger of(context) showsnackbar(snackbar(content text('login failed'))); } } @override widget build(buildcontext context) { return scaffold( appbar appbar( title text('chat app login'), ), body padding( padding const edgeinsets all(16), child column( children \[ textfield( controller usernamecontroller, decoration inputdecoration(labeltext 'username'), ), textfield( controller passwordcontroller, decoration inputdecoration(labeltext 'password'), obscuretext true, ), sizedbox(height 20), elevatedbutton( onpressed () => login(context), child text('login'), ), textbutton( onpressed () => navigator push( context, materialpageroute(builder (context) => signupscreen())), child text('don\\'t have an account? sign up'), ), ], ), ), ); } } 4 2 2 注册界面 // lib/screens/signup screen dart import 'package\ flutter/material dart'; import ' /services/auth service dart'; import 'home screen dart'; class signupscreen extends statelesswidget { final authservice authservice = authservice(); final texteditingcontroller usernamecontroller = texteditingcontroller(); final texteditingcontroller emailcontroller = texteditingcontroller(); final texteditingcontroller passwordcontroller = texteditingcontroller(); void signup(buildcontext context) async { var user = await authservice signup( usernamecontroller text trim(), emailcontroller text trim(), passwordcontroller text trim(), ); if (user != null) { navigator pushreplacement( context, materialpageroute(builder (context) => homescreen()), ); } else { scaffoldmessenger of(context) showsnackbar(snackbar(content text('sign up failed'))); } } @override widget build(buildcontext context) { return scaffold( appbar appbar( title text('chat app sign up'), ), body padding( padding const edgeinsets all(16), child column( children \[ textfield( controller usernamecontroller, decoration inputdecoration(labeltext 'username'), ), textfield( controller emailcontroller, decoration inputdecoration(labeltext 'email'), ), textfield( controller passwordcontroller, decoration inputdecoration(labeltext 'password'), obscuretext true, ), sizedbox(height 20), elevatedbutton( onpressed () => signup(context), child text('sign up'), ), ], ), ), ); } } 步骤 5 – 实现用户在线状态 5 1 更新用户在线状态 在 authservice 中创建一个方法来更新用户的在线状态 // lib/services/auth service dart class authservice { // existing methods future\<void> updateuseronlinestatus(bool isonline) async { var user = await parseuser currentuser() as parseuser?; if (user != null) { user set('isonline', isonline); await user save(); } } } 5 2 在登录和注销时设置在线状态 更新登录和注销方法 // lib/services/auth service dart class authservice { // existing methods future\<parseuser?> login(string username, string password) async { var user = parseuser(username, password, null); var response = await user login(); if (response success) { await updateuseronlinestatus(true); return user; } else { return null; } } future\<void> logout() async { await updateuseronlinestatus(false); var user = await parseuser currentuser() as parseuser?; await user? logout(); } } 第6步 – 显示联系人列表 6 1 创建用户服务 创建 user service dart 在 services // lib/services/user service dart import 'package\ parse server sdk flutter/parse server sdk dart'; class userservice { future\<list\<parseuser>> getallusers() async { final querybuilder\<parseuser> queryusers = querybuilder\<parseuser>(parseuser forquery()); final parseresponse apiresponse = await queryusers query(); if (apiresponse success && apiresponse results != null) { return apiresponse results as list\<parseuser>; } else { return \[]; } } } 6 2 创建主页 // lib/screens/home screen dart import 'package\ flutter/material dart'; import 'package\ parse server sdk flutter/parse server sdk dart'; import ' /services/user service dart'; import ' /services/auth service dart'; import 'chat screen dart'; class homescreen extends statefulwidget { @override homescreenstate createstate() => homescreenstate(); } class homescreenstate extends state\<homescreen> { final userservice userservice = userservice(); final authservice authservice = authservice(); list\<parseuser> users = \[]; parseuser? currentuser; @override void initstate() { super initstate(); fetchusers(); getcurrentuser(); } void getcurrentuser() async { currentuser = await authservice getcurrentuser(); } void fetchusers() async { var allusers = await userservice getallusers(); setstate(() { users = allusers where((user) => user username != currentuser? username) tolist(); }); } void logout(buildcontext context) async { await authservice logout(); navigator pushreplacementnamed(context, '/'); } @override widget build(buildcontext context) { return scaffold( appbar appbar( title text('contacts'), actions \[ iconbutton(onpressed () => logout(context), icon icon(icons logout)), ], ), body listview\ builder( itemcount users length, itembuilder (context, index) { var user = users\[index]; return listtile( title text(user username ?? ''), subtitle text(user get('isonline') == true ? 'online' 'offline'), ontap () { navigator push( context, materialpageroute(builder (context) => chatscreen(receiver user)), ); }, ); }, ), ); } } 第7步 – 使用实时查询实现实时消息传递 7 1 设置实时查询客户端 在 pubspec yaml dependencies flutter sdk flutter parse server sdk flutter git url https //github com/parse community/parse sdk flutter git ref master 这确保您拥有最新版本,并支持实时查询。 7 2 在 main dart // lib/main dart void main() async { widgetsflutterbinding ensureinitialized(); const keyapplicationid = 'your application id'; const keyclientkey = 'your client key'; const keyparseserverurl = 'https //parseapi back4app com'; const livequeryurl = 'wss\ //your app back4app io'; await parse() initialize( keyapplicationid, keyparseserverurl, clientkey keyclientkey, autosendsessionid true, debug true, livequeryurl livequeryurl, ); runapp(myapp()); } 将 'your app' 替换为您的 back4app 应用程序子域名。 7 3 创建聊天界面 // lib/screens/chat screen dart import 'package\ flutter/material dart'; import 'package\ parse server sdk flutter/parse server sdk dart'; import 'package\ uuid/uuid dart'; import 'package\ image picker/image picker dart'; import ' /services/auth service dart'; import 'package\ cached network image/cached network image dart'; class chatscreen extends statefulwidget { final parseuser receiver; chatscreen({required this receiver}); @override chatscreenstate createstate() => chatscreenstate(); } class chatscreenstate extends state\<chatscreen> { final authservice authservice = authservice(); parseuser? currentuser; list\<parseobject> messages = \[]; string chatroomid = ''; final texteditingcontroller messagecontroller = texteditingcontroller(); late parselivelist\<parseobject> livemessages; @override void initstate() { super initstate(); getcurrentuser(); setupchatroom(); } void getcurrentuser() async { currentuser = await authservice getcurrentuser(); if (currentuser != null) { setuplivequery(); } } void setupchatroom() { string userid = currentuser! objectid!; string receiverid = widget receiver objectid!; chatroomid = userid compareto(receiverid) > 0 ? '$userid $receiverid' '$receiverid $userid'; } void setuplivequery() { querybuilder\<parseobject> querymessages = querybuilder\<parseobject>(parseobject('message')) whereequalto('chatroomid', chatroomid) orderbyascending('createdat'); livemessages = parselivelist\<parseobject>( querymessages, listeningincludes \['sender'], lazyloading false, ); setstate(() {}); } void sendmessage({string? content, parsefilebase? imagefile}) async { var message = parseobject('message') set('sender', currentuser) set('receiver', widget receiver) set('chatroomid', chatroomid) set('content', content ?? '') set('image', imagefile); await message save(); messagecontroller clear(); } void pickimage() async { final picker = imagepicker(); final pickedfile = await picker pickimage(source imagesource gallery, imagequality 50); if (pickedfile != null) { var file = parsefile(file(pickedfile path)); await file save(); sendmessage(imagefile file); } } @override void dispose() { livemessages dispose(); super dispose(); } @override widget build(buildcontext context) { if (currentuser == null || livemessages == null) { return scaffold( appbar appbar( title text('chat with ${widget receiver username}'), ), body center(child circularprogressindicator()), ); } return scaffold( appbar appbar( title text('chat with ${widget receiver username}'), ), body column( children \[ expanded( child parselivelistwidget\<parseobject>( query livemessages, reverse false, lazyloading false, childbuilder (context, snapshot) { if (snapshot failed) { return text('error ${snapshot error}'); } else if (snapshot hasdata) { var message = snapshot loadeddata!; var sender = message get\<parseuser>('sender')!; var isme = sender objectid == currentuser! objectid; var content = message get\<string>('content') ?? ''; var image = message get\<parsefilebase>('image'); return align( alignment isme ? alignment centerright alignment centerleft, child container( padding edgeinsets all(8), margin edgeinsets all(8), decoration boxdecoration( color isme ? colors blue\[100] colors grey\[300], borderradius borderradius circular(8), ), child column( crossaxisalignment crossaxisalignment start, children \[ if (content isnotempty) text(content), if (image != null) cachednetworkimage( imageurl image url!, placeholder (context, url) => circularprogressindicator(), errorwidget (context, url, error) => icon(icons error), ), ], ), ), ); } else { return container(); } }, ), ), divider(height 1), container( padding edgeinsets symmetric(horizontal 8), color colors white, child row( children \[ iconbutton( icon icon(icons photo), onpressed pickimage, ), expanded( child textfield( controller messagecontroller, decoration inputdecoration collapsed(hinttext 'type a message'), ), ), iconbutton( icon icon(icons send), onpressed () { if ( messagecontroller text trim() isnotempty) { sendmessage(content messagecontroller text trim()); } }, ), ], ), ), ], ), ); } } 解释 parselivelistwidget 一个监听实时查询更新并在数据变化时重建的组件。 sendmessage() 发送文本消息或图像。 pickimage() 使用 image picker 选择图像并将其作为消息发送。 setuplivequery() 设置实时查询以监听聊天室中的新消息。 第 8 步 – 测试应用程序 8 1 运行应用程序 在终端中运行 flutter run 8 2 测试消息 在两个设备或模拟器上打开应用程序。 使用不同的用户帐户注册或登录。 从一个帐户中,从联系人列表中选择另一个用户。 发送消息和图片;它们应该实时出现在两个设备上。 结论 恭喜!您已经使用 back4app 在 flutter 中构建了一个实时聊天应用程序。该应用程序支持: 用户认证 安全登录和注册。 实时消息 使用实时查询进行即时更新。 用户在线状态 在线/离线状态跟踪。 媒体共享 发送和接收图片。 消息历史 持久化聊天消息。 下一步 群聊 扩展应用程序以支持群组对话。 消息状态 实现已读回执和输入指示器。 推送通知 在应用程序后台时发送新消息的通知。 头像 允许用户上传头像。 安全增强 使用acl和基于角色的权限保护数据。 附加资源 back4app 文档 https //www back4app com/docs flutter 的 parse sdk 指南 https //docs parseplatform org/flutter/guide/ flutter 官方文档 https //flutter dev/docs parse 实时查询 https //docs parseplatform org/js/guide/#live queries 快乐编码!