-
-
Save umatoma/0bf9a23f28a686f4c70ec072b74d06ea to your computer and use it in GitHub Desktop.
flutter-study chat-app-firestore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:cloud_firestore/cloud_firestore.dart'; | |
import 'package:firebase_auth/firebase_auth.dart'; | |
import 'package:flutter/material.dart'; | |
void main() { | |
// 最初に表示するWidget | |
runApp(ChatApp()); | |
} | |
class ChatApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
// アプリ名 | |
title: 'ChatApp', | |
theme: ThemeData( | |
// テーマカラー | |
primarySwatch: Colors.blue, | |
), | |
// ログイン画面を表示 | |
home: LoginPage(), | |
); | |
} | |
} | |
// ログイン画面用Widget | |
class LoginPage extends StatefulWidget { | |
@override | |
_LoginPageState createState() => _LoginPageState(); | |
} | |
class _LoginPageState extends State<LoginPage> { | |
// メッセージ表示用 | |
String infoText = ''; | |
// 入力したメールアドレス・パスワード | |
String email = ''; | |
String password = ''; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: Center( | |
child: Container( | |
padding: EdgeInsets.all(24), | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
// メールアドレス入力 | |
TextFormField( | |
decoration: InputDecoration(labelText: 'メールアドレス'), | |
onChanged: (String value) { | |
setState(() { | |
email = value; | |
}); | |
}, | |
), | |
// パスワード入力 | |
TextFormField( | |
decoration: InputDecoration(labelText: 'パスワード'), | |
obscureText: true, | |
onChanged: (String value) { | |
setState(() { | |
password = value; | |
}); | |
}, | |
), | |
Container( | |
padding: EdgeInsets.all(8), | |
// メッセージ表示 | |
child: Text(infoText), | |
), | |
Container( | |
width: double.infinity, | |
// ユーザー登録ボタン | |
child: ElevatedButton( | |
child: Text('ユーザー登録'), | |
onPressed: () async { | |
try { | |
// メール/パスワードでユーザー登録 | |
final FirebaseAuth auth = FirebaseAuth.instance; | |
final result = await auth.createUserWithEmailAndPassword( | |
email: email, | |
password: password, | |
); | |
// ユーザー登録に成功した場合 | |
// チャット画面に遷移+ログイン画面を破棄 | |
await Navigator.of(context).pushReplacement( | |
MaterialPageRoute(builder: (context) { | |
return ChatPage(result.user!); | |
}), | |
); | |
} catch (e) { | |
// ユーザー登録に失敗した場合 | |
setState(() { | |
infoText = "登録に失敗しました:${e.toString()}"; | |
}); | |
} | |
}, | |
), | |
), | |
const SizedBox(height: 8), | |
Container( | |
width: double.infinity, | |
// ログイン登録ボタン | |
child: OutlinedButton( | |
child: Text('ログイン'), | |
onPressed: () async { | |
try { | |
// メール/パスワードでログイン | |
final FirebaseAuth auth = FirebaseAuth.instance; | |
final result = await auth.signInWithEmailAndPassword( | |
email: email, | |
password: password, | |
); | |
// ログインに成功した場合 | |
// チャット画面に遷移+ログイン画面を破棄 | |
await Navigator.of(context).pushReplacement( | |
MaterialPageRoute(builder: (context) { | |
return ChatPage(result.user!); | |
}), | |
); | |
} catch (e) { | |
// ログインに失敗した場合 | |
setState(() { | |
infoText = "ログインに失敗しました:${e.toString()}"; | |
}); | |
} | |
}, | |
), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
} | |
// チャット画面用Widget | |
class ChatPage extends StatelessWidget { | |
// 引数からユーザー情報を受け取れるようにする | |
ChatPage(this.user); | |
// ユーザー情報 | |
final User user; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('チャット'), | |
actions: <Widget>[ | |
IconButton( | |
icon: Icon(Icons.logout), | |
onPressed: () async { | |
// ログアウト処理 | |
// 内部で保持しているログイン情報等が初期化される | |
// (現時点ではログアウト時はこの処理を呼び出せばOKと、思うぐらいで大丈夫です) | |
await FirebaseAuth.instance.signOut(); | |
// ログイン画面に遷移+チャット画面を破棄 | |
await Navigator.of(context).pushReplacement( | |
MaterialPageRoute(builder: (context) { | |
return LoginPage(); | |
}), | |
); | |
}, | |
), | |
], | |
), | |
body: Column( | |
children: [ | |
Container( | |
padding: EdgeInsets.all(8), | |
child: Text('ログイン情報:${user.email}'), | |
), | |
Expanded( | |
// FutureBuilder | |
// 非同期処理の結果を元にWidgetを作れる | |
child: FutureBuilder<QuerySnapshot>( | |
// 投稿メッセージ一覧を取得(非同期処理) | |
// 投稿日時でソート | |
future: FirebaseFirestore.instance | |
.collection('posts') | |
.orderBy('date') | |
.get(), | |
builder: (context, snapshot) { | |
// データが取得できた場合 | |
if (snapshot.hasData) { | |
final List<DocumentSnapshot> documents = snapshot.data!.docs; | |
// 取得した投稿メッセージ一覧を元にリスト表示 | |
return ListView( | |
children: documents.map((document) { | |
return Card( | |
child: ListTile( | |
title: Text(document['text']), | |
subtitle: Text(document['email']), | |
// 自分の投稿メッセージの場合は削除ボタンを表示 | |
trailing: document['email'] == user.email | |
? IconButton( | |
icon: Icon(Icons.delete), | |
onPressed: () async { | |
// 投稿メッセージのドキュメントを削除 | |
await FirebaseFirestore.instance | |
.collection('posts') | |
.doc(document.id) | |
.delete(); | |
}, | |
) | |
: null, | |
), | |
); | |
}).toList(), | |
); | |
} | |
// データが読込中の場合 | |
return Center( | |
child: Text('読込中...'), | |
); | |
}, | |
), | |
), | |
], | |
), | |
floatingActionButton: FloatingActionButton( | |
child: Icon(Icons.add), | |
onPressed: () async { | |
// 投稿画面に遷移 | |
await Navigator.of(context).push( | |
MaterialPageRoute(builder: (context) { | |
return AddPostPage(this.user); | |
}), | |
); | |
}, | |
), | |
); | |
} | |
} | |
// 投稿画面用Widget | |
class AddPostPage extends StatefulWidget { | |
// 引数からユーザー情報を受け取る | |
AddPostPage(this.user); | |
// ユーザー情報 | |
final User user; | |
@override | |
_AddPostPageState createState() => _AddPostPageState(); | |
} | |
class _AddPostPageState extends State<AddPostPage> { | |
// 入力した投稿メッセージ | |
String messageText = ''; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('チャット投稿'), | |
), | |
body: Center( | |
child: Container( | |
padding: EdgeInsets.all(32), | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
// 投稿メッセージ入力 | |
TextFormField( | |
decoration: InputDecoration(labelText: '投稿メッセージ'), | |
// 複数行のテキスト入力 | |
keyboardType: TextInputType.multiline, | |
// 最大3行 | |
maxLines: 3, | |
onChanged: (String value) { | |
setState(() { | |
messageText = value; | |
}); | |
}, | |
), | |
const SizedBox(height: 8), | |
Container( | |
width: double.infinity, | |
child: ElevatedButton( | |
child: Text('投稿'), | |
onPressed: () async { | |
final date = | |
DateTime.now().toLocal().toIso8601String(); // 現在の日時 | |
final email = widget.user.email; // AddPostPage のデータを参照 | |
// 投稿メッセージ用ドキュメント作成 | |
await FirebaseFirestore.instance | |
.collection('posts') // コレクションID指定 | |
.doc() // ドキュメントID自動生成 | |
.set({ | |
'text': messageText, | |
'email': email, | |
'date': date | |
}); | |
// 1つ前の画面に戻る | |
Navigator.of(context).pop(); | |
}, | |
), | |
) | |
], | |
), | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment