Flutterを使ったアプリ開発をやってみたので、よく使った処理をメモで残します。
Contents
処理完了までロード画面を表示
FutureBuilderを使う方法
ifの中のreturnを忘れるといつまでもロード画面になるので注意。
Future Builderを使ってしまうと、setState()の度にfuture部分が再実行されるので、後述のStackを使う方法の方が扱いやすかった。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Widget build(BuildContext context) { return FutureBuilder( future: 初期処理(), builder: (context, snapshot) { // エラー時に表示するWidget if (snapshot.hasError) { return Text('エラー'); } // 初期処理完了したら表示したいWidget if (snapshot.connectionState == ConnectionState.done) { return Text('中身'); } // Firebaseのinitializeが完了するのを待つ間に表示するWidget return Container( decoration: BoxDecoration(color: Colors.white), child: Center( child: CircularProgressIndicator(), ) ); }, ); } |
Stackを使う方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
class _MyFirestorePageState extends State<MyPageSettings>{ bool _isLoading = true; void initState() { getDocument(); } Future<DocumentSnapshot> getDocument() async { user_id = user!.uid; final snapshot = await FirebaseFirestore.instance .collection('users') .doc(user_id) .get(); setState(() { _isLoading = false; //処理完了したら_isLoadingを更新する }); return snapshot; } @override Widget build(BuildContext context) { return Scaffold( body:Stack( children: [ Container( ///ロード後 padding: EdgeInsets.all(5), child: ListView( children: <Widget>[ Text('中身'), ] ) ), Visibility( ///ロード中 visible: _isLoading, ///trueの時に表示 child: Container( decoration: BoxDecoration(color: Colors.white), child: Center( child: CircularProgressIndicator(), ) ) ), ] ) ); } } |
Listが完成するのを待ってからbuild
buildの中に書いた内容は、初期表示だけでなくsetStateを更新する度に何度も呼び出されてしまうので、初回処理はfutureBuilderやinitState()などを使う必要がある。
1 2 3 |
Widget build(BuildContext context){ xxxxxxxxxxxxxx //この部分が何度も呼ばれてしまう } |
firebaseから取得した情報を元にリストを作成し、bodyのListViewを完成させたい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
Future<List> makeTags() async { final List<Widget> retVal =[]; final snapshot = await FirebaseFirestore.instance .collection('コレクション名') .get(); //firebaseから取得したデータで何かしら作って、List retValに追加していく retVal.add(Text('aaa')); return retVal; } Widget build(BuildContext context) { return Scaffold( body:FutureBuilder( future: makeTags(), ///初回処理。ListView用のList<Widget>を作る builder: (context, AsyncSnapshot snapshot){ ///AsyncSnapshotをつけないと、snapshot.data.lengthがnullの長さになる可能性があるのでエラー ///if文でmakeTags()を更新するまでの表示内容を追記できる return Container( padding: EdgeInsets.all(5), child: Column( children: <Widget>[ Text('ここからListView始まり'), Flexible( child: ListView.builder( shrinkWrap: true, itemCount: (snapshot.data.length), itemBuilder: (context, int i)=>snapshot.data[i], ///snapshot.dataがmakeTags()のreturn ) ) ] ) ) } ); ) } |
並べ替え可能なListView
ReorderableListViewを使った。各List Itemに一意のキーを持たせる必要がある。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
makeItem(goal, context){ return Card( key: Key(goal.id), //一意のキーをふる。Firebaseのdocument IDを使った ~~~~~~カードの中身~~~~~~~ ); } Flexible( child: ReorderableListView.builder( shrinkWrap: true, itemCount: (documentList.length), itemBuilder: (context, index) => makeItem(documentList[index], context), onReorder: (int oldIndex, int newIndex) { if (oldIndex < newIndex) { newIndex -= 1; } final DocumentSnapshot item = documentList.removeAt(oldIndex); documentList.insert(newIndex, item); updateOrder(documentList); //documentListの順番と同じになるように、DBのインデックスを書き換える } ), ), |
Nullだった場合の処理
color変数に値が入っていたらその色を、入っていなければ指定した色で設定する
1 2 3 4 5 6 |
//定義する時に?をつける final Color? color; //??で「値がなかったら」を表現できる color: color ?? const Color(0xffA3D8F6) //Null判定ではなくIF分岐も可能 color: status=='active' ? Colors.black : Colors.white |
Null許容変数が絶対にNullにならない場合
ログイン中ユーザーを特定する時にはNullありで定義するが、プログラムでユーザー情報を使うときにはNullではないと宣言する
1 2 |
User? user = FirebaseAuth.instance.currentUser; //定義 user_id = user!.uid; //利用する時 |
For文、リスト追加
1 2 3 4 |
var list = []; for(var t=0; t<documentList.length; t++){ list.add(documentList[t]['field']); } |
リストの重複削除
1 2 |
List<String> selectedTags = <String>[]; selectedTags = selectedTags.toSet().toList(); |