🏔️

【Flutter】大学生のためのコミュニティアプリ「Hash」をリリースしたので気づいたことをまとめてみる

2022/06/13に公開

はじめに

こんにちは!個人でFlutterアプリ開発を行っているひろたか@Hash_hirotakaです😸

そもそもどんなアプリ?

概要としては私が所属する関西大学の学生向けに学内のサークルや部活情報を一覧でまとめたサークル・部活一覧機能や、オープンチャット形式で学生同士がコミュニケーションをとれるコミュニティ機能という2つの機能を実装した関大生専用のコミュニティアプリです。

使用技術など

アプリ:Flutter(Dart)
データベース:Firebase(Firestore)
アーキテクチャ:MVVMでステート管理に ChangeNotifier を使用
開発手法:DDD

アプリの機能

サークル・部活情報掲載機能

まず一つ目は大学内のサークル・部活情報を掲載する機能です。
従来では大学の入学式に配られるサークルガイドという紙媒体の情報からでないとサークルや部活を見つけることが困難で、Twitterやinstagramで検索しようとしても団体の名前を知らないとそもそも検索できないといった問題を解決したかったのでこの機能を作りました。

コミュニティ機能

次に紹介するのはコミュニティ機能です。
いわゆるLINEオープンチャットやYahoo知恵袋みたいなオープンチャット機能なのですが、どうせならこのアプリで学生同士がコミュニケーションをはかる機会を増やしたいと思ったので作りました。
各ユーザーがコミュニティというオープンチャットルームを作成でき、そうして作成したコミュニティはHashのユーザーであれば誰でも閲覧、メッセージ送信が可能です。
また、不適切な投稿をするユーザーを報告する機能や投稿をブロックし非表示にする機能も実装しています。

プロフィール機能

最後にプロフィール機能です。
自分のプロフィールが確認できます。
ログアウトやアカウント削除も可能です。

ブロック機能意外と難しかった

もしメッセージ機能などを実装したSNSのようなアプリをAppstoreにリリースする場合、不適切な投稿や発言をするユーザを報告したりブロックしたりする機能の実装が必須となります。

自分が作ったブロック機能は以下のような処理です

あるユーザーが不適切なオープンチャットを作成していた時、チャット詳細画面にあるブロック用アイコンをタップすることでブロックするかどうかの確認ダイアログを表示する。

ここでブロックするを選択した時、オープンチャット一覧画面に遷移し該当ユーザーが投稿したオープンチャットを全て非表示にする。

ここで問題となるのが、ブロック処理を行なった後、一覧画面に遷移した時に非表示にされるオープンチャットが「該当の投稿のみ」ではなく、「ブロックしたユーザーが投稿したオープンチャット全て」でないといけない点です。

以下ではブロック機能を実装している部分のコードを一部紹介します。

community_detail_model.dart
 Future<void> addBlockList(
      String followingCommunityId, String followingCommunityCreatorUid) async {
    var db = FirebaseFirestore.instance;
    final uid = FirebaseAuth.instance.currentUser!.uid;

    final communityDocument = await FirebaseFirestore.instance
        .collection('communities')
        .doc('following_communities')
        .collection('following_community_details')
        .doc(followingCommunityId)
        .get();

    Map<String, dynamic> communityDataToUser =
        communityDocument.data() as Map<String, dynamic>;
    db
        .collection("users")
        .doc(uid)
        .collection('blocked_users')
        .doc(followingCommunityCreatorUid)
        .set({
      "creatorFaculty": communityDataToUser['creatorFaculty'],
      "creatorImage": communityDataToUser['creatorImage'],
      "creatorName": communityDataToUser['creatorName'],
      "creatorUniversity": communityDataToUser['creatorUniversity'],
      "createdAt": DateTime.now()
    });

    final userDocument =
        await FirebaseFirestore.instance.collection('users').doc(uid).get();

    Map<String, dynamic> userDataToCommunity =
        userDocument.data() as Map<String, dynamic>;
    db
        .collection("communities")
        .doc('following_communities')
        .collection('following_community_details')
        .doc(followingCommunityId)
        .collection('blocked_users')
        .doc(uid)
        .set({
      "bio": userDataToCommunity['bio'],
      "email": userDataToCommunity['email'],
      "faculty": userDataToCommunity['faculty'],
      "photoUrl": userDataToCommunity['photoUrl'],
      "university": userDataToCommunity['university'],
      "createdAt": DateTime.now()
    });

    notifyListeners();
  }

ここでは何をやっているのかを以下で説明します。
1.ここでそれぞれログイン中のユーザーのuidとブロックしようとしている該当オープンチャットのidを取得

community_detail_model.dart
final uid = FirebaseAuth.instance.currentUser!.uid;

final communityDocument = await FirebaseFirestore.instance
     .collection('communities')
     .doc('following_communities')
     .collection('following_community_details')
     .doc(followingCommunityId)
     .get();

2.1で作成したcommunityDocumentで取得したオープンチャットの作成者情報(今自分がブロックしようとしているユーザーの情報)をuserコレクション直下のblocked_usersサブコレクションに追加する。

community_detail_model.dart
 Map<String, dynamic> communityDataToUser =
     communityDocument.data() as Map<String, dynamic>;
 db
     .collection("users")
     .doc(uid)
     .collection('blocked_users')
     .doc(followingCommunityCreatorUid)
     .set({
   "creatorFaculty": communityDataToUser['creatorFaculty'],
   "creatorImage": communityDataToUser['creatorImage'],
   "creatorName": communityDataToUser['creatorName'],
   "creatorUniversity": communityDataToUser['creatorUniversity'],
   "createdAt": DateTime.now()
 });

これはログイン中のユーザーがどのユーザーをブロックしているのかを判別できるようにするためです。

3.同様に1で取得したuidを元にログイン中のユーザーの詳細情報を取得し、userDocument関数に格納する。

community_detail_model.dart
final userDocument =
       await FirebaseFirestore.instance.collection('users').doc(uid).get();

uid取得してるところとやってること一緒じゃね?と思うかもしれませんが、ここで初めて取得したuidを元にログイン中のユーザーの名前やプロフィール画像のような詳細情報を取得しているので別物です。

4.3で作成したuserDocumentで得たユーザーの詳細情報をcommunitiesコレクション直下のblocked_usersサブコレクションに追加する。

community_detail_model.dart
  Map<String, dynamic> userDataToCommunity =
      userDocument.data() as Map<String, dynamic>;
  db
      .collection("communities")
      .doc('following_communities')
      .collection('following_community_details')
      .doc(followingCommunityId)
      .collection('blocked_users')
      .doc(uid)
      .set({
    "bio": userDataToCommunity['bio'],
    "email": userDataToCommunity['email'],
    "faculty": userDataToCommunity['faculty'],
    "photoUrl": userDataToCommunity['photoUrl'],
    "university": userDataToCommunity['university'],
    "createdAt": DateTime.now()
  });

5.最後にnotifyListeners();でview側にaddBlockList関数の処理が完了したことを通知する。

community_detail_model.dart
 notifyListeners();

このように、単にブロックしたオープンチャットだけを非表示にするのではなく、オープンチャットを作ったユーザーのuidをログインしているユーザーのblocked_usersサブコレクションに追加しないと誰がどのユーザーをブロックしているのかを判別できないためオープンチャットを一括で非表示にすることができず、このロジックを考えるのにかなり苦労しました...。

最後に

自分の作った物が世に出るって最高ですね...!
これからもガンガンアップデートしていくので乞うご期待ください(?)

宣伝

最後にせっかくなので僕がリリースした大学生のためのコミュニティアプリ「Hash」とこのアプリを作るにあたって非常にお世話になったオンラインサロン「Flutter大学」の宣伝をしておこうと思います。

大学生のためのコミュニティアプリ「Hash」

以下からインストールできるのでよかったら使ってみてください(Appstoreのみでの公開です)
https://apps.apple.com/us/app/hash/id1614642150

Flutter大学

https://flutteruniv.com/

Discussion