Flutter Templates
Flutter와 Back4App으로 소셜 네트워킹 앱 만들기
54 분
소개 소셜 네트워킹 앱을 만드는 것은 복잡한 작업일 수 있지만, flutter와 back4app을 사용하면 개발 프로세스를 간소화할 수 있습니다 이 튜토리얼은 사용자 인증, 프로필 관리, 뉴스 피드, 친구 연결, 메시징 및 알림을 포함한 기능이 완전한 소셜 네트워킹 앱을 구축하는 방법을 안내합니다 이 튜토리얼이 끝나면 다음과 같은 기능을 갖춘 작동하는 소셜 네트워킹 앱을 갖게 됩니다 사용자 인증 안전한 가입 및 로그인 프로세스 사용자 프로필 사용자 정보가 포함된 편집 가능한 프로필 뉴스 피드 친구와 사용자의 게시물 표시 친구 연결 친구 요청을 보내고 수락할 수 있는 기능 메시징 사용자 간의 실시간 채팅 알림 친구 요청, 메시지 및 게시물 상호작용에 대한 푸시 알림 전제 조건 이 튜토리얼을 따라 하려면 다음이 필요합니다 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/ 에서 무료 계정을 등록하세요 flutter용 parse server sdk 가 당신의 프로젝트에 추가되어야 합니다 back4app flutter sdk 가이드 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 푸시 알림을 위해 uuid 고유 id 생성을 위해 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" 클래스를 만드세요 user 유형 pointer< user> content 유형 string image 유형 file createdat 유형 date 2 2 3 friendrequest 클래스 다음 열이 있는 "friendrequest" 클래스를 만드세요 fromuser 유형 pointer< user> touser 유형 pointer< user> status 유형 string (값 "pending", "accepted", "rejected") 2 2 4 message 클래스 다음 열이 있는 "message" 클래스를 만드세요 fromuser 유형 pointer< user> touser 유형 pointer< user> content 유형 string createdat 유형 date 2 3 애플리케이션 자격 증명 얻기 다음으로 이동하세요 app settings > security & keys 당신의 application id 와 client key 를 적어두세요 3단계 – flutter 앱에서 parse 초기화하기 열기 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단계 – 친구 연결 추가 이 단계에서는 사용자 간의 친구 연결을 구현할 것입니다 사용자는 친구 요청을 보내고, 이를 수락하거나 거부하며, 친구 목록을 볼 수 있습니다 우리는 데이터 모델을 수정하고, 서비스를 생성하며, 이 기능을 지원하기 위해 ui를 업데이트할 것입니다 7 1 데이터 모델 업데이트 우리는 이미 다음 열이 있는 friendrequest 클래스를 back4app에서 생성했습니다 fromuser user touser user status 문자열 (값 "pending", "accepted", "rejected") 추가적으로, 사용자의 친구 목록을 추적해야 합니다 이를 위해 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 ui 업데이트 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단계 – 메시징 구현 이 단계에서는 실시간 메시징을 추가하여 사용자 간의 소통을 가능하게 합니다 8 1 back4app에서 실시간 쿼리 활성화하기 back4app 앱 대시보드에서 앱 설정 > 서버 설정 으로 이동합니다 서버 url 아래에서 서버 url , 서버 url을 기록해 두세요 (예 https //your app name back4app io ) back4app에서는 기본적으로 실시간 쿼리가 활성화되어 있습니다 8 2 flutter에서 실시간 쿼리 설정하기 이 parse server sdk flutter 패키지는 실시간 쿼리 지원을 포함합니다 8 3 메시징 서비스 만들기 lib/services/ 아래에 message service dart 라는 이름의 파일을 만듭니다 // 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 메시지 모델 만들기 lib/models/ 아래에 message dart 라는 이름의 파일을 추가합니다 // 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 ui 업데이트하여 채팅 시작하기 친구 목록이나 사용자 검색 화면에서 채팅을 시작할 수 있습니다 친구 목록에서 친구를 표시할 때, 채팅을 시작하는 버튼을 추가하세요 // inside the listtile for a friend trailing iconbutton( icon icon(icons chat), onpressed () { navigator push( context, materialpageroute(builder ( ) => chatscreen(otheruser friend)), ); }, ), 9단계 – 푸시 알림 추가하기 푸시 알림을 구현하려면 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 flutter에서 firebase 초기화하기 수정하기 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 메시징 구성하기 다음 위치에 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 를 업데이트하여 푸시 알림 초기화하기 에서 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 back4app에서 푸시 알림 보내기 클라우드 코드 또는 서버에서 직접 푸시 알림을 보낼 수 있습니다 9 6 1 친구 요청 시 알림 보내기 사용자가 친구 요청을 보낼 때, 수신자에게 푸시 알림을 보낼 수 있습니다 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를 사용하여 추가합니다 알림 사용자 참여를 위한 푸시 알림을 통합합니다 ui 개선 사용자 인터페이스와 사용자 경험을 개선합니다 보안 back4app에서 적절한 acl을 설정하여 데이터 보안과 개인 정보를 보장합니다 추가 자료 back4app 문서 https //www back4app com/docs flutter용 parse sdk 가이드 https //docs parseplatform org/flutter/guide/ flutter 공식 문서 https //flutter dev/docs provider 패키지 문서 https //pub dev/packages/provider 행복한 코딩!