Flutter Templates
Создание приложения для чата в реальном времени на Flutter с Back4App
36 мин
введение создание чат приложения включает в себя управление данными в реальном времени, аутентификацию пользователей, обработку медиа и эффективное хранение данных в этом учебном пособии вы узнаете, как создать чат приложение в реальном времени на flutter, которое поддерживает однонаправленные и групповые беседы, статусы сообщений и обмен медиа мы будем использовать back4app — бэкенд как услугу, работающий на parse server, для обработки функциональности бэкенда к концу этого учебного пособия у вас будет полностью функциональное чат приложение со следующими функциями аутентификация пользователей безопасные процессы регистрации и входа обмен сообщениями в реальном времени мгновенная доставка сообщений с использованием live queries присутствие пользователей отслеживание статуса онлайн/офлайн пользователей хранение медиа отправка и получение изображений в чатах история сообщений сохранение истории чатов для пользователей предварительные требования чтобы следовать этому учебному пособию, вам потребуется 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 для 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 настройка классов и модели данных мы создадим следующие классы пользователь (класс 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 и ролей на основе разрешений дополнительные ресурсы документация back4app https //www back4app com/docs руководство по parse sdk для flutter https //docs parseplatform org/flutter/guide/ официальная документация flutter https //flutter dev/docs parse live query https //docs parseplatform org/js/guide/#live queries счастливого кодирования!