【チーム作成UI】FloatingActionButtonからLINE風ボトムシートを表示する
このページでやること
このページでは、右下の + ボタンを押したときに、チーム作成用のボトムシートを表示します。
ボトムシートとは、画面の下から出てくる入力画面です。
今回作る流れはこれです。
トーク一覧画面
↓
右下の+ボタンを押す
↓
下からチーム作成画面が出る
↓
チーム名を入力する
↓
作成する
LINE風に、白背景・角丸・シンプルな入力欄で作ります。
今日のゴール
右下の FloatingActionButton を押すと、次のような画面が下から出るようにします。
新しいチームを作成
チーム名
[作成する]
FloatingActionButton とは、画面右下に表示する丸いボタンです。
今回の画面では、+ ボタンとして使います。
このページで出てくる単語
| 単語 | 一言説明 |
|---|---|
FloatingActionButton | 画面右下に出す丸いボタン |
showModalBottomSheet | 下から画面を出す命令 |
| ボトムシート | 画面下から出てくる小さな画面 |
TextEditingController | 入力欄の文字を管理する道具 |
StatefulBuilder | ボトムシート内だけ画面更新するための部品 |
MediaQuery | 画面サイズやキーボード状態を知る道具 |
viewInsets.bottom | キーボードの高さを考慮する設定 |
isScrollControlled | ボトムシートをキーボードに合わせて広げる設定 |
npmや環境変数はこのページで必要?
このページでは、npmは使いません。
環境変数も設定しません。
必要なのは、すでにあるFlutterのコードだけです。
main.dartを開く
↓
FloatingActionButtonを置く
↓
showCreateTeamSheet()を作る
↓
保存する
↓
flutter runで確認する
Step 1:main.dartを開く
次のファイルを開きます。
lib/main.dart
ターミナルから開く場合は、次を実行します。
code lib/main.dart
code が使えない場合は、VS Codeの左側から lib/main.dart を開いてください。
Step 2:TeamListPageを探す
main.dart の中で、次を検索します。
class TeamListPage
VS Codeなら、次で検索できます。
command + F
この TeamListPage が、トーク一覧画面です。
ここに右下の + ボタンと、ボトムシートを追加します。
Step 3:チーム名入力用Controllerを確認する
_TeamListPageState の中に、次があるか確認します。
final teamNameController = TextEditingController();
これは、チーム名入力欄の文字を管理する道具です。
もしまだなければ追加します。
class _TeamListPageState extends State<TeamListPage> {
final teamNameController = TextEditingController();
bool isCreating = false;
String? errorText;
@override
void dispose() {
teamNameController.dispose();
super.dispose();
}
// この下に処理を書いていきます
}
dispose() は、使い終わったControllerを片付ける処理です。
Step 4:ボトムシートを開く関数を作る
_TeamListPageState の中に、次の関数を追加します。
Future<void> showCreateTeamSheet() async {
teamNameController.clear();
errorText = null;
await showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (context) {
return StatefulBuilder(
builder: (context, setSheetState) {
return Padding(
padding: EdgeInsets.only(
left: 20,
right: 20,
top: 20,
bottom: MediaQuery.of(context).viewInsets.bottom + 20,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'新しいチームを作成',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w900,
color: AppColors.text,
),
),
const SizedBox(height: 12),
AppTextField(
controller: teamNameController,
label: 'チーム名',
),
if (errorText != null) ...[
const SizedBox(height: 12),
ErrorBox(message: errorText!),
],
const SizedBox(height: 16),
FilledButton(
onPressed: isCreating
? null
: () async {
await createTeam(setSheetState);
},
child: Text(isCreating ? '作成中...' : '作成する'),
),
],
),
);
},
);
},
);
}
これで、下からチーム作成画面を表示できます。
Step 5:showModalBottomSheetの意味
一番大事なのは、この部分です。
await showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (context) {
return ...
},
);
短く言うと、こうです。
下から出る画面を表示する
↓
背景を白にする
↓
上だけ角丸にする
↓
中身をbuilderで作る
shape は、ボトムシートの形を決める設定です。
今回は、上の角だけ丸くしています。
Step 6:キーボードに隠れないようにする
この部分を見ます。
bottom: MediaQuery.of(context).viewInsets.bottom + 20,
これは、スマホでキーボードが出たときに、入力欄が隠れないようにするための設定です。
キーボードが出る
↓
ボトムシートの下余白を増やす
↓
入力欄が見える
初心者の方は、ここはそのまま使えばOKです。
Step 7:StatefulBuilderを使う理由
この部分です。
return StatefulBuilder(
builder: (context, setSheetState) {
return ...
},
);
StatefulBuilder は、ボトムシートの中だけを更新するために使います。
たとえば、チーム名が空のまま「作成する」を押したとき、エラーを出したいです。
チーム名が空
↓
作成する
↓
エラーを表示する
このようなボトムシート内の表示変更には、setSheetState を使います。
Step 8:作成ボタンとcreateTeamをつなげる
ボトムシートの中のボタンは、この形です。
FilledButton(
onPressed: isCreating
? null
: () async {
await createTeam(setSheetState);
},
child: Text(isCreating ? '作成中...' : '作成する'),
),
意味はこうです。
作成中ではない
↓
ボタンを押せる
↓
createTeam() が動く
作成中
↓
ボタンを押せない
↓
文字が「作成中...」になる
createTeam(setSheetState) は、チームをFirestoreに保存する処理です。
Step 9:まだcreateTeamがない場合
まだ createTeam() を作っていない場合は、仮で次を入れてください。
Future<void> createTeam(StateSetter setSheetState) async {
final teamName = teamNameController.text.trim();
if (teamName.isEmpty) {
setSheetState(() {
errorText = 'チーム名を入力してください。';
});
return;
}
setSheetState(() {
errorText = '次の処理でFirestoreにチームを保存します。';
});
}
この仮コードでは、Firestoreには保存しません。
まずは、ボトムシートが表示され、入力チェックが動くことだけ確認します。
すでにチーム作成処理がある場合は、この仮コードは不要です。
Step 10:FloatingActionButtonを追加する
Scaffold の中に、次を追加します。
floatingActionButton: FloatingActionButton(
onPressed: showCreateTeamSheet,
child: const Icon(Icons.add),
),
完成イメージはこのようになります。
return Scaffold(
backgroundColor: AppColors.bg,
appBar: AppBar(
title: const Text('トーク'),
),
body: ...,
floatingActionButton: FloatingActionButton(
onPressed: showCreateTeamSheet,
child: const Icon(Icons.add),
),
);
onPressed: showCreateTeamSheet にすることで、右下の + を押したときにボトムシートが開きます。
Step 11:保存する
main.dart を保存します。
Macの場合:
command + S
Windowsの場合:
Ctrl + S
Step 12:実行する
ターミナルで実行します。
flutter run
すでに起動している場合は、ターミナルで r を押します。
r
うまく反映されない場合は、R でホットリスタートします。
R
Step 13:画面を確認する
ログイン後、トーク一覧画面を開きます。
右下に + ボタンが出ているか確認します。
右下に+ボタン
次に、+ を押します。
下から、次のような画面が出ればOKです。
新しいチームを作成
チーム名
[作成する]
Step 14:入力チェックを確認する
何も入力せずに「作成する」を押してみます。
次のエラーが表示されればOKです。
チーム名を入力してください。
次に、チーム名を入力します。
開発チーム
「作成する」を押して、ボトムシートが閉じる、または仮メッセージが表示されればOKです。
1か所だけ変更してみる
ボトムシートのタイトルを変えてみます。
この部分を探します。
'新しいチームを作成',
次のように変えてみます。
'チームを追加',
保存して、ホットリロードします。
r
+ ボタンを押して、タイトルが変わっていれば成功です。
よくあるエラーと直し方
| エラー | 原因 | 直し方 |
|---|---|---|
showCreateTeamSheet isn't defined | 関数がない | _TeamListPageState の中に作る |
teamNameController isn't defined | Controllerがない | final teamNameController = TextEditingController(); を追加 |
StateSetter isn't defined | Materialのimportがない | import 'package:flutter/material.dart'; を確認 |
createTeam isn't defined | 作成処理がない | 仮の createTeam() を作る |
| ボトムシートが開かない | FABのonPressedが空 | onPressed: showCreateTeamSheet にする |
| エラーが表示されない | setSheetState を使っていない | ボトムシート内は setSheetState を使う |
| キーボードで隠れる | bottom余白がない | viewInsets.bottom + 20 を入れる |
ボトムシートが開かないとき
まず、FloatingActionButton を確認します。
これはNGです。
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
これだと、押しても何も起きません。
正しくはこれです。
floatingActionButton: FloatingActionButton(
onPressed: showCreateTeamSheet,
child: const Icon(Icons.add),
),
エラー表示が変わらないとき
ボトムシートの中では、通常の setState ではなく、setSheetState を使います。
setSheetState(() {
errorText = 'チーム名を入力してください。';
});
これで、ボトムシート内のエラー表示が更新されます。
最短作業まとめ
読むのが大変な人は、ここだけ見てください。
1. Controllerを用意
final teamNameController = TextEditingController();
2. ボトムシートを開く関数を作る
Future<void> showCreateTeamSheet() async {
teamNameController.clear();
errorText = null;
await showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (context) {
return StatefulBuilder(
builder: (context, setSheetState) {
return Padding(
padding: EdgeInsets.only(
left: 20,
right: 20,
top: 20,
bottom: MediaQuery.of(context).viewInsets.bottom + 20,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'新しいチームを作成',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w900,
color: AppColors.text,
),
),
const SizedBox(height: 12),
AppTextField(
controller: teamNameController,
label: 'チーム名',
),
if (errorText != null) ...[
const SizedBox(height: 12),
ErrorBox(message: errorText!),
],
const SizedBox(height: 16),
FilledButton(
onPressed: isCreating
? null
: () async {
await createTeam(setSheetState);
},
child: Text(isCreating ? '作成中...' : '作成する'),
),
],
),
);
},
);
},
);
}
3. 右下の+ボタンにつなげる
floatingActionButton: FloatingActionButton(
onPressed: showCreateTeamSheet,
child: const Icon(Icons.add),
),
4. 保存して実行
flutter run
チェックリスト
□ main.dartを開いた
□ TeamListPageを探した
□ teamNameControllerを用意した
□ disposeでControllerを片付けた
□ showCreateTeamSheet()を作った
□ showModalBottomSheetを書いた
□ 上だけ角丸にした
□ AppTextFieldでチーム名入力欄を作った
□ ErrorBoxを表示できるようにした
□ FilledButtonを置いた
□ FloatingActionButtonを追加した
□ onPressedをshowCreateTeamSheetにした
□ 保存した
□ flutter runで確認した
□ +ボタンからボトムシートが出た
ミニ確認問題
Q1. FloatingActionButton は何をするWidgetですか?
回答
画面右下に丸いボタンを表示するWidgetです。
今回なら、チーム作成用の + ボタンとして使います。
Q2. showModalBottomSheet は何をする命令ですか?
回答
画面の下から、入力用の小さな画面を表示する命令です。
Q3. ボトムシート内の表示を更新するときは何を使いますか?
回答
setSheetState を使います。
エラー表示や「作成中…」の表示を更新するときに使います。
Q4. このページでnpmや環境変数は必要ですか?
回答
必要ありません。
このページでは、右下の + ボタンからボトムシートを表示するUIを作るだけです。
このページのまとめ
FloatingActionButtonは、右下の丸い+ボタン。showModalBottomSheetで、画面下から入力画面を出せる。- ボトムシートは、チーム作成UIに向いている。
isScrollControlled: trueを入れると、キーボード表示に対応しやすい。viewInsets.bottomを使うと、入力欄がキーボードに隠れにくい。- ボトムシート内の更新には
setSheetStateを使う。 - このページではnpmや環境変数は不要。
- まずは
+を押して、下から入力画面が出ることを確認する。
次のページでやること
次のページでは、チームをタップしてタスク一覧画面へ移動します。
teamId と teamName を渡して、そのチーム専用のタスク一覧画面を開きます。
