Flutter Templates
Создание приложения для чата в реальном времени на Flutter с Back4App
36 мин
введение создание чат приложения включает в себя управление данными в реальном времени, аутентификацию пользователей, обработку медиа и эффективное хранение данных в этом учебном пособии вы узнаете, как создать чат приложение в реальном времени на flutter, которое поддерживает однонаправленные и групповые беседы, статусы сообщений и обмен медиа мы будем использовать back4app — бэкенд как услугу, работающий на parse server, для обработки функциональности бэкенда к концу этого учебного пособия у вас будет полностью функциональное чат приложение со следующими функциями аутентификация пользователей безопасные процессы регистрации и входа обмен сообщениями в реальном времени мгновенная доставка сообщений с использованием live queries присутствие пользователей отслеживание статуса онлайн/офлайн пользователей хранение медиа отправка и получение изображений в чатах история сообщений сохранение истории чатов для пользователей предварительные требования чтобы следовать этому учебному пособию, вам потребуется flutter sdk установлен на вашем компьютере следуйте за https //flutter dev/docs/get started/install базовые знания flutter и dart ide или текстовый редактор такой как visual studio code или android studio аккаунт back4app зарегистрируйтесь для получения бесплатного аккаунта на https //www back4app com/ parse server sdk для 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 войдите в вашу https //dashboard back4app com/ нажмите на "создать новое приложение" введите имя для вашего приложения, например, "chatapp" , и нажмите "создать" 2 2 настройка классов и модели данных мы создадим следующие классы пользователь (класс parse по умолчанию) хранит информацию о пользователе сообщение хранит сообщения чата чаткомната представляет собой чат между пользователями 2 2 1 класс пользователя поля имя пользователя string пароль string электронная почта string онлайн boolean аватар file (optional) класс пользователь встроен; нам просто нужно убедиться, что у него есть дополнительные поля 2 2 2 класс сообщение поля отправитель pointer< user> получатель pointer< user> chatroomid string содержимое string изображение file (optional) создано datetime (автоматически добавлено) 2 2 3 класс чаткомната поля chatroomid строка users массив указателей< user> lastmessage строка updatedat datetime (автоматически обновляется) 2 3 получение учетных данных приложения перейдите к настройки приложения > безопасность и ключи запишите ваш идентификатор приложения и ключ клиента шаг 3 – инициализация parse в вашем приложении flutter откройте 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 создание службы аутентификации создайте новый каталог с именем services в lib и добавьте файл с именем 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 это гарантирует, что у вас есть последняя версия с поддержкой live query 7 2 инициализация live query в 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 application id' и 'your client key' на ваши реальные ключи замените '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 тестирование сообщений откройте приложение на двух устройствах или эмуляторах зарегистрируйтесь или войдите с разными учетными записями из одной учетной записи выберите другого пользователя из списка контактов отправляйте сообщения и изображения; они должны появляться в реальном времени на обоих устройствах заключение поздравляем! вы создали приложение для чата в реальном времени на flutter с использованием back4app это приложение поддерживает аутентификация пользователя безопасный вход и регистрация сообщения в реальном времени мгновенные обновления с использованием live queries присутствие пользователя отслеживание статуса онлайн/офлайн обмен медиа отправка и получение изображений история сообщений сохранение сообщений чата следующие шаги групповые чаты расширьте приложение для поддержки групповых разговоров статусы сообщений реализуйте уведомления о прочтении и индикаторы ввода push уведомления отправляйте уведомления о новых сообщениях, когда приложение находится в фоновом режиме аватары позвольте пользователям загружать аватары улучшения безопасности защитите данные с помощью acl и ролей на основе разрешений дополнительные ресурсы https //www back4app com/docs https //docs parseplatform org/flutter/guide/ https //flutter dev/docs https //docs parseplatform org/js/guide/#live queries счастливого кодирования!