Flutter Templates
FlutterとBack4Appを使用したリアルタイムチャットアプリケーションの構築
35 分
はじめに チャットアプリケーションを作成するには、リアルタイムデータの管理、ユーザー認証、メディア処理、効率的なデータストレージが必要です。このチュートリアルでは、1対1およびグループ会話、メッセージのステータス、メディア共有をサポートするflutterでのリアルタイムチャットアプリケーションの構築方法を学びます。バックエンド機能を処理するために、parse serverによって提供されるバックエンド・アズ・ア・サービスであるback4appを使用します。 このチュートリアルの終わりまでに、以下の機能を持つ完全に機能するチャットアプリを持つことができます ユーザー認証 安全なサインアップとログインプロセス。 リアルタイムメッセージング ライブクエリを使用した即時メッセージ配信。 ユーザーの存在 ユーザーのオンライン/オフラインステータスを追跡。 メディアストレージ チャットでの画像の送受信。 メッセージ履歴 ユーザーのチャット履歴を保持。 前提条件 このチュートリアルに従うには、以下が必要です flutter sdk をあなたのマシンにインストールします。 公式のflutterインストールガイド https //flutter dev/docs/get started/install を参照してください。 flutterとdartの基本知識 が必要です。 ideまたはテキストエディタ 、例えばvisual studio codeやandroid studioなど。 back4appアカウント が必要です。 back4app https //www back4app com/ で無料アカウントにサインアップしてください。 flutter用のparse server sdk をプロジェクトに追加します。 概要 以下のコンポーネントを使用してチャットアプリケーションを構築します ユーザー認証 ユーザーはサインアップおよびログインできます。 連絡先リスト チャットするユーザーのリストを表示します。 チャット画面 リアルタイムメッセージングインターフェース。 メディア共有 画像を送受信する機能。 オンラインステータス ユーザーのオンライン/オフラインステータスを表示します。 ステップ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 ユーザークラス フィールド ユーザー名 文字列 パスワード 文字列 メール 文字列 オンラインかどうか ブール値 アバター ファイル(オプション) この ユーザー クラスは組み込みであり、追加のフィールドがあることを確認する必要があります。 2 2 2 メッセージクラス フィールド 送信者 ポインタ< ユーザー> 受信者 ポインタ< ユーザー> チャットルームid 文字列 コンテンツ 文字列 画像 ファイル(オプション) 作成日時 日時(自動追加) 2 2 3 チャットルームクラス フィールド chatroomid 文字列 users pointer< user>の配列 lastmessage 文字列 updatedat datetime(自動更新) 2 3 アプリケーションの資格情報を取得する 次に進む アプリ設定 > セキュリティとキー あなたの アプリケーションid と クライアントキー をメモしてください。 ステップ 3 – flutter アプリで parse を初期化する 次のように lib/main dart を開いて修正します import 'package\ flutter/material dart'; import 'package\ parse server sdk flutter/parse server sdk dart'; import 'screens/login screen dart'; // you'll create this file next void main() async { widgetsflutterbinding ensureinitialized(); const keyapplicationid = 'your application id'; const keyclientkey = 'your client key'; const keyparseserverurl = 'https //parseapi back4app com'; await parse() initialize( keyapplicationid, keyparseserverurl, clientkey keyclientkey, autosendsessionid true, debug true, ); runapp(myapp()); } class myapp extends statelesswidget { @override widget build(buildcontext context) { return materialapp( title 'chat app', theme themedata( primaryswatch colors blue, ), home loginscreen(), ); } } 「 'your application id' 」と 'your client key' を実際のback4appの資格情報に置き換えてください。 ステップ4 – ユーザー認証の実装 4 1 認証サービスの作成 「 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 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 テストメッセージング 2つのデバイスまたはエミュレーターでアプリを開く。 異なるユーザーアカウントでサインアップまたはログインする。 1つのアカウントから、連絡先リストから他のユーザーを選択する。 メッセージと画像を送信する; 両方のデバイスにリアルタイムで表示されるはずです。 結論 おめでとうございます! back4appを使用してflutterでリアルタイムチャットアプリケーションを構築しました。このアプリは次の機能をサポートしています ユーザー認証 安全なログインとサインアップ。 リアルタイムメッセージング ライブクエリを使用した即時更新。 ユーザーの存在 オンライン/オフラインのステータストラッキング。 メディア共有 画像の送受信。 メッセージ履歴 チャットメッセージの永続化。 次のステップ グループチャット アプリを拡張してグループ会話をサポートします。 メッセージのステータス 読了通知と入力インジケーターを実装します。 プッシュ通知 アプリがバックグラウンドにあるときに新しいメッセージの通知を送信します。 プロフィール写真 ユーザーがアバターをアップロードできるようにします。 セキュリティ強化 aclとロールベースの権限でデータを保護します。 追加リソース back4app ドキュメント https //www back4app com/docs flutter 用の parse sdk ガイド https //docs parseplatform org/flutter/guide/ flutter 公式ドキュメント https //flutter dev/docs parse ライブクエリ https //docs parseplatform org/js/guide/#live queries 楽しいコーディングを!