【Flutter】UserListからcurrentUserを除外する

ずっと詰まっていたUserListからログイン中のcurrentUserのみを除外する方法を理解したのでまとめておきます。

## `User`クラスを作る
```dart:user.dart
class User {
  String uid;
  String name;
  String email;
  String photoUrl;

  User({this.uid, this.name, this.email, this.photoUrl});

  Map toMap(User user) {
    var data = Map<String, dynamic>();
    data['uid'] = user.uid;
    data['name'] = user.name;
    data['email'] = user.email;
    data['photoUrl'] = user.photoUrl;
    return data;
  }

  User.fromMap(Map<String, dynamic> mapData) {
    this.uid = mapData['uid'];
    this.name = mapData['name'];
    this.email = mapData['email'];
    this.photoUrl = mapData['photoUrl'];
  }
}
```

まずは`user.dart`ファイルにUserクラスを定義します。

## `fetchAllUsers`メソッドを作る
auth関係のメソッドをまとめた`auth.dart`ファイルに`fetchAllUsers`メソッドを作成します。

```dart:auth.dart
class Auth {
  final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
  static final Firestore firestore = Firestore.instance;
  
  Future<FirebaseUser> getCurrentUser() async {
    FirebaseUser currentUser;
    currentUser = await _firebaseAuth.currentUser();
    return currentUser;
  }
    
  Future<List<User>> fetchAllUsers(FirebaseUser currentUser) async {
      List<User> userList = List<User>();

      QuerySnapshot querySnapshot =
          await firestore.collection("users").getDocuments();
      for (var i = 0; i < querySnapshot.documents.length; i++) {
        if (querySnapshot.documents[i].documentID != currentUser.uid) {
          userList.add(User.fromMap(querySnapshot.documents[i].data));
        }
      }
      return userList;
    }
}
```

これでFirestoreに登録されている全ユーザーのドキュメントを取ってきます。

他にも全ユーザー情報を取ってくる方法はあるのですが、currentUserのデータを削除するわけではなく、ただ除外する方法を思いつかなかったのでこの方法にしました。

簡単に内容を説明するとfor文で登録されているユーザーのデータの数だけユーザーデータを追加していくが、currentUserのuidと被るものは追加しない。というコードとなっています。
## `initState`する
次にUserListをレンダリングする`userlist.dart`ファイルにStateの初期化を記述します。

```dart:userlist.dart
class UserList extends StatefulWidget {
  @override
  _UserListState createState() => _UserListState();
}

class _UserListState extends State<UserList> {
  Auth _auth = Auth();
  List<User> userList;

  @override
  void initState() {
    super.initState();
    _auth.getCurrentUser().then((FirebaseUser user) {
      _auth.fetchAllUsers(user).then((List<User> list) {
        setState(() {
          userList = list;
        });
      });
    });
  }
}
```
initStateですが、下記の記事で呼び出されるタイミングがわかると思います。

[Flutter StatefulWidgetのライフサイクル](https://qiita.com/kurun_pan/items/116288b8ab2c409d2ee5)

また、公式ドキュメントにはツリーに挿入されたときに呼び出され、作成した各Stateオブジェクトに対して、このメソッドを一度だけ呼び出す。と書かれています。

>Called when this object is inserted into the tree.

>The framework will call this method exactly once for each State object it creates.

>Override this method to perform initialization that depends on the location at which this object was inserted into the tree (i.e., context) or on the widget used to configure this object (i.e., widget).

上記のコードによって`UserList`クラスが呼び出された時に`getCurrentUser`メソッドでcurrentUserを取得し、その情報を得た後に`fetchAllUsers`メソッドで全ユーザーを取得します。

## `ListView.builder`でユーザーデータを一覧として表示する
```dart:userlist.dart
buildUserList() {
    return ListView.builder(
      itemCount: userList.length,
      itemBuilder: ((context, index){
        User users = User(
          uid: userList[index].uid,
          name: userList[index].name,
          email: userList[index].email,
          photoUrl: userList[index].photoUrl
        );
        return Container(
          child: ListTile(
          leading: CircleAvatar(
            backgroundImage: NetworkImage(users.photoUrl),
            radius: 25.0,
        ),
          trailing: IconButton(
            icon: Icon(Icons.person_add),
            onPressed: () {
              addFriends(index);
            },
          ),
            title: Text(users.name),
            subtitle: Text(users.email),
          ),
        );
      }),
    );
  }
```

onPressedのaddFriends関数はまた違う機能なので気にしないでください。

`ListView.builder`でユーザーのデータをリストとして`users`に格納し、`Container`の各部分にそのデータを入れる入れ物(リストのタイル)を記述することで、各ユーザーのデータを表示したタイルを表示することができます。



## `FutureBuilder`を利用してエラーハンドリング
現状のままでも機能は成り立つと思いますが、このままだとエラー画面が一瞬出てしまうので`FutureBuilder`を利用します。

```dart:userlist.dart
buildItems() {
    return FutureBuilder(
        future: Provider.of<AuthService>(context).getCurrentUser(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            return buildUserList();
          } else {
            return Center(
              child: CircularProgressIndicator()
            );
          }
        }
    );
  }
```

これで通信が完了していない間はエラー画面ではなく、グルグル回るやつが表示されるようになりました。

## 最後に
かなり強引にこの機能を実装したので何か間違いや疑問点を見つけた方が入ればお気軽にコメントよろしくお願いします。