Flutter Templates
Создание приложения социальной сети с использованием Flutter и Back4App
54 мин
введение создание приложения для социальной сети может быть сложной задачей, но с помощью flutter и back4app вы можете упростить процесс разработки этот учебник проведет вас через создание полнофункционального приложения для социальной сети, которое включает аутентификацию пользователей, управление профилями, новостные ленты, связи с друзьями, обмен сообщениями и уведомления к концу этого учебника у вас будет функциональное приложение для социальной сети со следующими функциями аутентификация пользователей безопасные процессы регистрации и входа профили пользователей редактируемые профили с информацией о пользователе новостные ленты отображение постов от друзей и пользователя связи с друзьями возможность отправлять и принимать запросы на добавление в друзья обмен сообщениями чат в реальном времени между пользователями уведомления уведомления push для запросов на добавление в друзья, сообщений и взаимодействий с постами предварительные требования чтобы следовать этому руководству, вам потребуется flutter sdk установленный на вашем компьютере следуйте официальному руководству по установке flutter https //flutter dev/docs/get started/install для вашей операционной системы базовые знания flutter и dart если вы новичок в flutter, ознакомьтесь с документацией flutter https //flutter dev/docs чтобы ознакомиться с основами ide или текстовый редактор такой как visual studio code или android studio аккаунт back4app зарегистрируйтесь для получения бесплатного аккаунта на back4app https //www back4app com/ parse server sdk для flutter добавленный в ваш проект узнайте, как его настроить, следуя руководству по sdk flutter от back4app https //www back4app com/docs/flutter/parse flutter sdk шаг 1 – настройка проекта flutter 1 1 создание нового проекта flutter откройте терминал и выполните flutter create social app перейдите в каталог проекта cd social app 1 2 добавить зависимости откройте pubspec yaml и добавьте следующие зависимости dependencies flutter sdk flutter parse server sdk flutter ^4 0 1 provider ^6 0 0 image picker ^0 8 4+6 cached network image ^3 2 0 firebase messaging ^11 2 8 uuid ^3 0 6 запустите flutter pub get для установки пакетов примечание мы используем parse server sdk flutter для интеграции с back4app provider для управления состоянием image picker для выбора изображений профиля и постов cached network image для эффективной загрузки изображений firebase messaging для push уведомлений uuid для генерации уникальных идентификаторов шаг 2 – настройка back4app 2 1 создать новое приложение back4app войдите в ваш панель управления back4app https //dashboard back4app com/ нажмите на "создать новое приложение" введите имя для вашего приложения, например, "socialapp" , и нажмите "создать" 2 2 настройка моделей данных нам нужно создать несколько классов в back4app пользователь встроенный класс для аутентификации пользователей профиль хранит информацию о профиле пользователя пост хранит посты пользователей запросдруга управляет запросами на дружбу между пользователями сообщение хранит сообщения между пользователями 2 2 1 класс профиля перейдите в раздел "база данных" нажмите на "создать класс" в модальном окне выберите "пользовательский" введите "профиль" в качестве имени класса нажмите "создать класс" добавьте следующие столбцы пользователь тип pointer< user> имя пользователя тип string полное имя тип string биография тип string фото профиля тип file 2 2 2 класс поста создайте "post" класс со следующими колонками пользователь тип pointer< user> содержимое тип string изображение тип file создано тип date 2 2 3 класс friendrequest создайте "friendrequest" класс со следующими колонками от пользователя тип pointer< user> к пользователю тип pointer< user> статус тип string (значения "ожидание", "принято", "отклонено") 2 2 4 класс message создайте "message" класс со следующими колонками от пользователя тип pointer< user> к пользователю тип pointer< user> содержимое тип string создано тип date 2 3 получить учетные данные приложения перейдите в настройки приложения > безопасность и ключи запишите ваш id приложения и ключ клиента шаг 3 – инициализация parse в вашем приложении flutter откройте lib/main dart и измените его следующим образом import 'package\ flutter/material dart'; import 'package\ parse server sdk flutter/parse server sdk dart'; import 'package\ provider/provider dart'; import 'services/auth service dart'; import 'screens/login screen dart'; 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 changenotifierprovider\<authservice>( create ( ) => authservice(), child materialapp( title 'social app', theme themedata( primaryswatch colors blue, ), home loginscreen(), ), ); } } замените 'your application id' и 'your client key' на ваши учетные данные back4app мы используем changenotifierprovider для управления состоянием аутентификации шаг 4 – реализация аутентификации пользователей 4 1 создайте сервис аутентификации создайте новый каталог с именем services в lib и добавьте файл с именем auth service dart // lib/services/auth service dart import 'package\ flutter/material dart'; import 'package\ parse server sdk flutter/parse server sdk dart'; class authservice with changenotifier { parseuser? user; future\<bool> signup(string username, string password, string email) async { user = parseuser createuser(username, password, email); final response = await user! signup(); if (response success) { notifylisteners(); return true; } else { user = null; return false; } } future\<bool> login(string username, string password) async { user = parseuser(username, password, null); final response = await user! login(); if (response success) { notifylisteners(); return true; } else { user = null; return false; } } future\<void> logout() async { if (user != null) { await user! logout(); user = null; notifylisteners(); } } bool get isauthenticated => user != null; } 4 2 создайте экраны входа и регистрации создайте каталог с именем screens в lib и добавьте login screen dart и signup screen dart 4 2 1 экран входа // lib/screens/login screen dart import 'package\ flutter/material dart'; import 'package\ provider/provider dart'; import ' /services/auth service dart'; import 'signup screen dart'; import 'home screen dart'; class loginscreen extends statefulwidget { @override loginscreenstate createstate() => loginscreenstate(); } class loginscreenstate extends state\<loginscreen> { final texteditingcontroller usernamecontroller = texteditingcontroller(); final texteditingcontroller passwordcontroller = texteditingcontroller(); void login() async { final authservice = provider of\<authservice>(context, listen false); bool success = await authservice login( usernamecontroller text trim(), passwordcontroller text trim(), ); if (success) { 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('social 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, child text('login')), textbutton( onpressed () { navigator push( context, materialpageroute(builder ( ) => signupscreen()), ); }, child text('don\\'t have an account? sign up'), ) ], ), ), ); } } 4 2 2 экран регистрации // lib/screens/signup screen dart import 'package\ flutter/material dart'; import 'package\ provider/provider dart'; import ' /services/auth service dart'; import 'home screen dart'; class signupscreen extends statefulwidget { @override signupscreenstate createstate() => signupscreenstate(); } class signupscreenstate extends state\<signupscreen> { final texteditingcontroller usernamecontroller = texteditingcontroller(); final texteditingcontroller passwordcontroller = texteditingcontroller(); final texteditingcontroller emailcontroller = texteditingcontroller(); void signup() async { final authservice = provider of\<authservice>(context, listen false); bool success = await authservice signup( usernamecontroller text trim(), passwordcontroller text trim(), emailcontroller text trim(), ); if (success) { navigator pushreplacement( context, materialpageroute(builder (context) => homescreen()), ); } else { scaffoldmessenger of(context) showsnackbar(snackbar(content text('signup failed'))); } } @override widget build(buildcontext context) { return scaffold( appbar appbar( title text('social app signup'), ), 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, child text('sign up')), ], ), ), ); } } шаг 5 – настройка профилей пользователей 5 1 создать сервис профиля добавьте файл с именем profile service dart в lib/services/ // lib/services/profile service dart import 'package\ parse server sdk flutter/parse server sdk dart'; import ' /models/profile dart'; class profileservice { future\<void> createprofile(parseuser user) async { final profile = parseobject('profile') set('user', user) set('username', user username); await profile save(); } future\<profile?> getprofile(parseuser user) async { final query = querybuilder\<parseobject>(parseobject('profile')) whereequalto('user', user); final response = await query query(); if (response success && response results != null) { final profileobject = response results! first; return profile fromparseobject(profileobject); } else { return null; } } future\<void> updateprofile(profile profile) async { final profileobject = parseobject('profile') objectid = profile id set('fullname', profile fullname) set('bio', profile bio); await profileobject save(); } } 5 2 создать модель профиля добавьте файл с именем profile dart в lib/models/ // lib/models/profile dart import 'package\ parse server sdk flutter/parse server sdk dart'; class profile { string id; string username; string? fullname; string? bio; parsefilebase? profilepicture; profile({ required this id, required this username, this fullname, this bio, this profilepicture, }); factory profile fromparseobject(parseobject object) { return profile( id object objectid!, username object get\<string>('username')!, fullname object get\<string>('fullname'), bio object get\<string>('bio'), profilepicture object get\<parsefilebase>('profilepicture'), ); } } 5 3 создание экрана профиля добавьте файл с именем profile screen dart в lib/screens/ // lib/screens/profile screen dart import 'package\ flutter/material dart'; import 'package\ provider/provider dart'; import ' /services/auth service dart'; import ' /services/profile service dart'; import ' /models/profile dart'; class profilescreen extends statefulwidget { @override profilescreenstate createstate() => profilescreenstate(); } class profilescreenstate extends state\<profilescreen> { final profileservice profileservice = profileservice(); profile? profile; final texteditingcontroller fullnamecontroller = texteditingcontroller(); final texteditingcontroller biocontroller = texteditingcontroller(); void loadprofile() async { final authservice = provider of\<authservice>(context, listen false); final user = authservice user!; profile? fetchedprofile = await profileservice getprofile(user); if (fetchedprofile == null) { await profileservice createprofile(user); fetchedprofile = await profileservice getprofile(user); } setstate(() { profile = fetchedprofile; fullnamecontroller text = profile? fullname ?? ''; biocontroller text = profile? bio ?? ''; }); } void saveprofile() async { if (profile != null) { profile! fullname = fullnamecontroller text trim(); profile! bio = biocontroller text trim(); await profileservice updateprofile(profile!); scaffoldmessenger of(context) showsnackbar(snackbar(content text('profile updated'))); } } @override void initstate() { super initstate(); loadprofile(); } @override widget build(buildcontext context) { if (profile == null) { return scaffold( appbar appbar(title text('profile')), body center(child circularprogressindicator()), ); } return scaffold( appbar appbar(title text(profile! username)), body padding( padding const edgeinsets all(16), child column( children \[ // add profile picture handling here textfield( controller fullnamecontroller, decoration inputdecoration(labeltext 'full name'), ), textfield( controller biocontroller, decoration inputdecoration(labeltext 'bio'), ), sizedbox(height 20), elevatedbutton(onpressed saveprofile, child text('save')), ], ), ), ); } } шаг 6 – реализация новостных лент 6 1 создание сервиса постов добавьте файл с именем post service dart в lib/services/ // lib/services/post service dart import 'package\ parse server sdk flutter/parse server sdk dart'; import ' /models/post dart'; class postservice { future\<void> createpost(string content, parseuser user) async { final post = parseobject('post') set('user', user) set('content', content); await post save(); } future\<list\<post>> getposts(parseuser user) async { final query = querybuilder\<parseobject>(parseobject('post')) orderbydescending('createdat') includeobject(\['user']); final response = await query query(); if (response success && response results != null) { return response results! map((e) => post fromparseobject(e)) tolist(); } else { return \[]; } } } 6 2 создание модели поста добавьте файл с именем post dart в lib/models/ // lib/models/post dart import 'package\ parse server sdk flutter/parse server sdk dart'; class post { string id; string content; parseuser user; datetime createdat; post({ required this id, required this content, required this user, required this createdat, }); factory post fromparseobject(parseobject object) { return post( id object objectid!, content object get\<string>('content')!, user object get\<parseuser>('user')!, createdat object createdat!, ); } } 6 3 создать главный экран измените home screen dart в lib/screens/ // lib/screens/home screen dart import 'package\ flutter/material dart'; import 'package\ provider/provider dart'; import ' /services/auth service dart'; import ' /services/post service dart'; import ' /models/post dart'; import 'profile screen dart'; class homescreen extends statefulwidget { @override homescreenstate createstate() => homescreenstate(); } class homescreenstate extends state\<homescreen> { final postservice postservice = postservice(); list\<post> posts = \[]; final texteditingcontroller postcontroller = texteditingcontroller(); void loadposts() async { final authservice = provider of\<authservice>(context, listen false); final user = authservice user!; list\<post> fetchedposts = await postservice getposts(user); setstate(() { posts = fetchedposts; }); } void createpost() async { final authservice = provider of\<authservice>(context, listen false); final user = authservice user!; await postservice createpost( postcontroller text trim(), user); postcontroller clear(); loadposts(); } @override void initstate() { super initstate(); loadposts(); } @override widget build(buildcontext context) { final authservice = provider of\<authservice>(context); return scaffold( appbar appbar( title text('social app'), actions \[ iconbutton( icon icon(icons person), onpressed () { navigator push( context, materialpageroute(builder ( ) => profilescreen()), ); }, ), iconbutton( icon icon(icons logout), onpressed () async { await authservice logout(); navigator pushreplacementnamed(context, '/'); }, ), ], ), body column( children \[ textfield( controller postcontroller, decoration inputdecoration( hinttext 'what\\'s on your mind?', contentpadding edgeinsets all(16), ), ), elevatedbutton(onpressed createpost, child text('post')), expanded( child listview\ builder( itemcount posts length, itembuilder (context, index) { final post = posts\[index]; return listtile( title text(post user username ?? 'unknown'), subtitle text(post content), trailing text( post createdat tolocal() tostring(), style textstyle(fontsize 12), ), ); }, ), ), ], ), ); } } шаг 7 – добавление дружеских связей на этом этапе мы реализуем связи дружбы между пользователями пользователи могут отправлять запросы на дружбу, принимать или отклонять их, а также просматривать свой список друзей мы изменим модели данных, создадим сервисы и обновим пользовательский интерфейс, чтобы поддержать эту функциональность 7 1 обновите модель данных мы уже создали класс friendrequest в back4app со следующими колонками fromuser указатель на user touser указатель на user status строка (значения "ожидание", "принято", "отклонено") кроме того, нам нужно отслеживать список друзей пользователя мы можем сделать это, добавив отношение friends в класс user 7 1 1 добавить отношение друзей в класс пользователя в back4app перейдите в класс user нажмите на кнопку "+" для добавления нового столбца назовите столбец "friends" и установите тип на relation < user> 7 2 создать сервис запросов на добавление в друзья создайте файл с именем friend service dart в lib/services/ // lib/services/friend service dart import 'package\ parse server sdk flutter/parse server sdk dart'; class friendservice { future\<bool> sendfriendrequest(parseuser fromuser, parseuser touser) async { final friendrequest = parseobject('friendrequest') set('fromuser', fromuser) set('touser', touser) set('status', 'pending'); final response = await friendrequest save(); return response success; } future\<list\<parseobject>> getpendingrequests(parseuser user) async { final query = querybuilder\<parseobject>(parseobject('friendrequest')) whereequalto('touser', user) whereequalto('status', 'pending') includeobject(\['fromuser']); final response = await query query(); if (response success && response results != null) { return response results!; } else { return \[]; } } future\<bool> acceptfriendrequest(parseobject friendrequest) async { friendrequest set('status', 'accepted'); final response = await friendrequest save(); if (response success) { // add each user to the other's friends relation final fromuser = friendrequest get\<parseuser>('fromuser')!; final touser = friendrequest get\<parseuser>('touser')!; fromuser setadd('friends', \[touser]); touser setadd('friends', \[fromuser]); await fromuser save(); await touser save(); return true; } return false; } future\<bool> rejectfriendrequest(parseobject friendrequest) async { friendrequest set('status', 'rejected'); final response = await friendrequest save(); return response success; } future\<list\<parseuser>> getfriends(parseuser user) async { final relation = user getrelation\<parseuser>('friends'); final query = relation query(); final response = await query query(); if (response success && response results != null) { return response results! cast\<parseuser>(); } else { return \[]; } } } 7 3 обновите пользовательский интерфейс 7 3 1 добавить экран поиска пользователей создайте файл с именем search users screen dart в lib/screens/ // lib/screens/search users screen dart import 'package\ flutter/material dart'; import 'package\ parse server sdk flutter/parse server sdk dart'; import 'package\ provider/provider dart'; import ' /services/auth service dart'; import ' /services/friend service dart'; class searchusersscreen extends statefulwidget { @override searchusersscreenstate createstate() => searchusersscreenstate(); } class searchusersscreenstate extends state\<searchusersscreen> { final texteditingcontroller searchcontroller = texteditingcontroller(); list\<parseuser> users = \[]; final friendservice friendservice = friendservice(); void searchusers() async { final query = querybuilder\<parseuser>(parseuser forquery()) wherecontains('username', searchcontroller text trim()) wherenotequalto('objectid', provider of\<authservice>(context, listen false) user! objectid); final response = await query query(); if (response success && response results != null) { setstate(() { users = response results! cast\<parseuser>(); }); } else { setstate(() { users = \[]; }); } } void sendfriendrequest(parseuser touser) async { final fromuser = provider of\<authservice>(context, listen false) user!; bool success = await friendservice sendfriendrequest(fromuser, touser); if (success) { scaffoldmessenger of(context) showsnackbar(snackbar(content text('friend request sent'))); } else { scaffoldmessenger of(context) showsnackbar(snackbar(content text('failed to send friend request'))); } } @override widget build(buildcontext context) { return scaffold( appbar appbar( title text('search users'), ), body column( children \[ padding( padding const edgeinsets all(16 0), child textfield( controller searchcontroller, decoration inputdecoration( hinttext 'search by username', suffixicon iconbutton( icon icon(icons search), onpressed searchusers, ), ), ), ), expanded( child listview\ builder( itemcount users length, itembuilder (context, index) { final user = users\[index]; return listtile( title text(user username ?? 'unknown'), trailing elevatedbutton( onpressed () => sendfriendrequest(user), child text('add friend'), ), ); }, ), ), ], ), ); } } 7 3 2 экран запросов на добавление в друзья создайте файл с именем friend requests screen dart в lib/screens/ // lib/screens/friend requests screen dart import 'package\ flutter/material dart'; import 'package\ parse server sdk flutter/parse server sdk dart'; import 'package\ provider/provider dart'; import ' /services/auth service dart'; import ' /services/friend service dart'; class friendrequestsscreen extends statefulwidget { @override friendrequestsscreenstate createstate() => friendrequestsscreenstate(); } class friendrequestsscreenstate extends state\<friendrequestsscreen> { final friendservice friendservice = friendservice(); list\<parseobject> friendrequests = \[]; void loadfriendrequests() async { final user = provider of\<authservice>(context, listen false) user!; final requests = await friendservice getpendingrequests(user); setstate(() { friendrequests = requests; }); } void acceptrequest(parseobject request) async { bool success = await friendservice acceptfriendrequest(request); if (success) { scaffoldmessenger of(context) showsnackbar(snackbar(content text('friend request accepted'))); loadfriendrequests(); } else { scaffoldmessenger of(context) showsnackbar(snackbar(content text('failed to accept friend request'))); } } void rejectrequest(parseobject request) async { bool success = await friendservice rejectfriendrequest(request); if (success) { scaffoldmessenger of(context) showsnackbar(snackbar(content text('friend request rejected'))); loadfriendrequests(); } else { scaffoldmessenger of(context) showsnackbar(snackbar(content text('failed to reject friend request'))); } } @override void initstate() { super initstate(); loadfriendrequests(); } @override widget build(buildcontext context) { return scaffold( appbar appbar( title text('friend requests'), ), body listview\ builder( itemcount friendrequests length, itembuilder (context, index) { final request = friendrequests\[index]; final fromuser = request get\<parseuser>('fromuser')!; return listtile( title text(fromuser username ?? 'unknown'), subtitle text('sent you a friend request'), trailing row( mainaxissize mainaxissize min, children \[ iconbutton( icon icon(icons check, color colors green), onpressed () => acceptrequest(request), ), iconbutton( icon icon(icons close, color colors red), onpressed () => rejectrequest(request), ), ], ), ); }, ), ); } } 7 3 3 обновление навигации на главном экране в home screen dart , добавьте навигацию к экранам поиска пользователей и запросов на добавление в друзья // inside appbar actions iconbutton( icon icon(icons person add), onpressed () { navigator push( context, materialpageroute(builder ( ) => searchusersscreen()), ); }, ), iconbutton( icon icon(icons notifications), onpressed () { navigator push( context, materialpageroute(builder ( ) => friendrequestsscreen()), ); }, ), 7 4 обновите новостную ленту, чтобы показать посты друзей измените метод getposts в post service dart для получения постов от пользователя и его друзей // lib/services/post service dart future\<list\<post>> getposts(parseuser user) async { // get friends final relation = user getrelation\<parseuser>('friends'); final friendsquery = relation query(); final friendsresponse = await friendsquery query(); list\<string> userids = \[user objectid!]; if (friendsresponse success && friendsresponse results != null) { final friends = friendsresponse results! cast\<parseuser>(); userids addall(friends map((friend) => friend objectid!)); } // fetch posts from user and friends final query = querybuilder\<parseobject>(parseobject('post')) wherecontainedin('user', userids map((id) => parseuser(null, null, null) objectid = id) tolist()) orderbydescending('createdat') includeobject(\['user']); final response = await query query(); if (response success && response results != null) { return response results! map((e) => post fromparseobject(e)) tolist(); } else { return \[]; } } шаг 8 – реализация обмена сообщениями на этом этапе мы добавим обмен сообщениями в реальном времени между пользователями с использованием live queries 8 1 включите live queries в back4app в вашей панели управления приложением back4app перейдите в настройки приложения > настройки сервера в разделе url сервера , запишите ваш url сервера (например, https //your app name back4app io ) live queries включены по умолчанию в back4app 8 2 настройка live queries в flutter пакет parse server sdk flutter включает поддержку live query 8 3 создание сервиса обмена сообщениями создайте файл с именем message service dart в lib/services/ // lib/services/message service dart import 'package\ parse server sdk flutter/parse server sdk dart'; import ' /models/message dart'; class messageservice { parselivelist\<message>? livemessagelist; future\<void> sendmessage(parseuser fromuser, parseuser touser, string content) async { final message = parseobject('message') set('fromuser', fromuser) set('touser', touser) set('content', content); await message save(); } future\<void> subscribetomessages(parseuser user, parseuser otheruser, function(list\<message>) onmessagesupdated) async { final querybuilder = querybuilder\<parseobject>(parseobject('message')) whereequalto('fromuser', user) whereequalto('touser', otheruser) orderbyascending('createdat') includeobject(\['fromuser', 'touser']); livemessagelist = parselivelist\<message>(querybuilder, listenonallsubitems true); livemessagelist! stream listen((event) { onmessagesupdated(livemessagelist! items); }); } future\<void> dispose() async { await livemessagelist? dispose(); } } 8 4 создание модели сообщения добавьте файл с именем message dart в lib/models/ // lib/models/message dart import 'package\ parse server sdk flutter/parse server sdk dart'; class message extends parseobject implements parsecloneable { message() super('message'); message clone() this(); @override clone(map\<string, dynamic> map) => message clone() fromjson(map); parseuser get fromuser => get\<parseuser>('fromuser')!; parseuser get touser => get\<parseuser>('touser')!; string get content => get\<string>('content')!; } 8 5 создать экран чата добавьте файл с именем chat screen dart в lib/screens/ // lib/screens/chat screen dart import 'package\ flutter/material dart'; import 'package\ parse server sdk flutter/parse server sdk dart'; import 'package\ provider/provider dart'; import ' /models/message dart'; import ' /services/auth service dart'; import ' /services/message service dart'; class chatscreen extends statefulwidget { final parseuser otheruser; chatscreen({required this otheruser}); @override chatscreenstate createstate() => chatscreenstate(); } class chatscreenstate extends state\<chatscreen> { final messageservice messageservice = messageservice(); list\<message> messages = \[]; final texteditingcontroller messagecontroller = texteditingcontroller(); void sendmessage() { final authservice = provider of\<authservice>(context, listen false); final fromuser = authservice user!; final touser = widget otheruser; messageservice sendmessage(fromuser, touser, messagecontroller text trim()); messagecontroller clear(); } void onmessagesupdated(list\<message> updatedmessages) { setstate(() { messages = updatedmessages; }); } @override void initstate() { super initstate(); final authservice = provider of\<authservice>(context, listen false); final user = authservice user!; messageservice subscribetomessages(user, widget otheruser, onmessagesupdated); } @override void dispose() { messageservice dispose(); super dispose(); } @override widget build(buildcontext context) { final authservice = provider of\<authservice>(context, listen false); final user = authservice user!; return scaffold( appbar appbar(title text(widget otheruser username ?? 'chat')), body column( children \[ expanded( child listview( children messages map((message) { bool isme = message fromuser objectid == user objectid; return listtile( title align( alignment isme ? alignment centerright alignment centerleft, child container( padding edgeinsets all(10), color isme ? colors blueaccent colors grey\[300], child text( message content, style textstyle(color isme ? colors white colors black), ), ), ), ); }) tolist(), ), ), divider(height 1), container( padding edgeinsets symmetric(horizontal 8 0), color theme of(context) cardcolor, child row( children \[ flexible( child textfield( controller messagecontroller, decoration inputdecoration collapsed(hinttext 'send a message'), ), ), iconbutton( icon icon(icons send), onpressed sendmessage, ), ], ), ) ], ), ); } } 8 6 обновить интерфейс для начала чата вы можете начать чат из списка друзей или экрана поиска пользователей в списке друзей, при отображении друзей, добавьте кнопку для начала чата // inside the listtile for a friend trailing iconbutton( icon icon(icons chat), onpressed () { navigator push( context, materialpageroute(builder ( ) => chatscreen(otheruser friend)), ); }, ), шаг 9 – добавление push уведомлений реализация push уведомлений включает в себя настройку firebase cloud messaging (fcm) и интеграцию его с back4app 9 1 настройка firebase cloud messaging 9 1 1 настройка проекта firebase перейдите на консоль firebase https //console firebase google com/ и создайте новый проект добавьте android и/или ios приложение в ваш проект для android вам нужно имя пакета (applicationid) для ios вам нужен идентификатор пакета 9 1 2 скачайте файлы конфигурации для android скачайте google services json и поместите его в android/app/ для ios скачайте googleservice info plist и добавьте его в ваш проект xcode в разделе runner 9 2 добавьте firebase messaging пакет убедитесь, что вы добавили firebase messaging в ваш pubspec yaml dependencies firebase messaging ^11 2 8 запустите flutter pub get для установки 9 3 инициализация firebase в flutter измените main dart import 'package\ flutter/material dart'; import 'package\ parse server sdk flutter/parse server sdk dart'; import 'package\ firebase core/firebase core dart'; // other imports void main() async { widgetsflutterbinding ensureinitialized(); await firebase initializeapp(); // initialize parse as before runapp(myapp()); } 9 4 настройка firebase messaging создайте файл с именем push notification service dart в lib/services/ // lib/services/push notification service dart import 'package\ firebase messaging/firebase messaging dart'; import 'package\ parse server sdk flutter/parse server sdk dart'; class pushnotificationservice { final firebasemessaging fcm = firebasemessaging instance; future\<void> init() async { // request permissions (ios) await fcm requestpermission(); // get the token string? token = await fcm gettoken(); if (token != null) { // save the token to parse installation final installation = await parseinstallation currentinstallation(); installation set('devicetoken', token); await installation save(); } // handle foreground messages firebasemessaging onmessage listen((remotemessage message) { print('received a message in the foreground!'); // handle the message }); } } 9 5 обновите main dart для инициализации push notifications в main dart , после инициализации parse, инициализируйте pushnotificationservice void main() async { widgetsflutterbinding ensureinitialized(); await firebase initializeapp(); // initialize parse as before // initialize push notifications pushnotificationservice pushnotificationservice = pushnotificationservice(); await pushnotificationservice init(); runapp(myapp()); } 9 6 отправка push notifications из back4app вы можете отправлять push уведомления, используя cloud code или напрямую с вашего сервера 9 6 1 отправка уведомления о запросе на добавление в друзья когда пользователь отправляет запрос на добавление в друзья, вы можете отправить push уведомление получателю в friend service dart , измените метод sendfriendrequest future\<bool> sendfriendrequest(parseuser fromuser, parseuser touser) async { // existing code to create friend request // send push notification final pushquery = querybuilder\<parseinstallation>(parseinstallation forquery()) whereequalto('user', touser); await parsepush() send( pushpayload( query pushquery, notification parsenotification( title 'new friend request', alert '${fromuser username} sent you a friend request', ), ), ); return response success; } примечание убедитесь, что вы связали установку с пользователем 9 7 связать установку с пользователем когда пользователь входит в систему, свяжите его установку с его учетной записью в auth service dart , после успешного входа future\<bool> login(string username, string password) async { user = parseuser(username, password, null); final response = await user! login(); if (response success) { // associate installation with user final installation = await parseinstallation currentinstallation(); installation set('user', user); await installation save(); notifylisteners(); return true; } else { user = null; return false; } } примечание уведомления требуют дополнительной настройки как на стороне клиента, так и на стороне сервера вам нужно обрабатывать различные сценарии, такие как фоновые уведомления и разрешения пользователей, которые выходят за рамки этого руководства заключение поздравляем! вы создали основу приложения для социальной сети с использованием flutter и back4app это приложение включает в себя аутентификацию пользователей, управление профилем и функцию новостной ленты хотя реализация полных соединений с друзьями, обмена сообщениями и уведомлений выходит за рамки этого руководства, у вас теперь есть необходимая структура для дальнейшего расширения вашего приложения следующие шаги соединения с друзьями реализуйте функционал запросов на добавление в друзья обмен сообщениями добавьте функции чата в реальном времени с использованием live queries уведомления интегрируйте push уведомления для вовлечения пользователей улучшения интерфейса улучшите пользовательский интерфейс и пользовательский опыт безопасность обеспечьте безопасность данных и конфиденциальность, установив соответствующие acl в back4app дополнительные ресурсы документация back4app https //www back4app com/docs руководство по parse sdk для flutter https //docs parseplatform org/flutter/guide/ официальная документация flutter https //flutter dev/docs документация пакета provider https //pub dev/packages/provider счастливого кодирования!