2週間で筋トレ管理SNSをリリースしてみた

# 概要
10月29日（金） `筋肉の日`・`トレーニングの日`に **筋トレ管理SNS「ProTain」** をリリースしました！
ダウンロードはこちらから👇
[IOS](https://apps.apple.com/jp/app/protain-%E7%AD%8B%E3%83%88%E3%83%AC%E7%AE%A1%E7%90%86sns/id1593411486?l=en)
[Android](https://play.google.com/store/apps/details?id=net.shohi.proTain2021)

もしよければ以下のTweetをリツイートしてください。。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">筋トレ管理SNS【ProTain】<br><br>筋トレ管理を&quot;アップデート&quot;というビジョンをもとに開発しました。<br><br>iOS/Androidともにリリースされているので是非みなさん使って筋トレ、ダイエットに励んでみてください🔥<a href="https://t.co/6OYcEktHyn">https://t.co/6OYcEktHyn</a><a href="https://twitter.com/hashtag/%E7%AD%8B%E3%83%88%E3%83%AC?src=hash&amp;ref_src=twsrc%5Etfw">#筋トレ</a> <a href="https://twitter.com/hashtag/SNS?src=hash&amp;ref_src=twsrc%5Etfw">#SNS</a> <a href="https://twitter.com/hashtag/ProTain?src=hash&amp;ref_src=twsrc%5Etfw">#ProTain</a> <a href="https://twitter.com/hashtag/%E3%83%95%E3%82%A3%E3%83%83%E3%83%88%E3%83%8D%E3%82%B9?src=hash&amp;ref_src=twsrc%5Etfw">#フィットネス</a> <a href="https://twitter.com/hashtag/%E7%AD%8B%E3%83%88%E3%83%AC%E5%A5%BD%E3%81%8D%E3%81%A8%E7%B9%8B%E3%81%8C%E3%82%8A%E3%81%9F%E3%81%84?src=hash&amp;ref_src=twsrc%5Etfw">#筋トレ好きと繋がりたい</a></p>&mdash; 井手翔陽🦔 (@shouhi_ide) <a href="https://twitter.com/shouhi_ide/status/1459108189866119176?ref_src=twsrc%5Etfw">November 12, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 開発チームの紹介
### 井手翔陽
関西学院大学 商学部3年
2019年 関西学院大学（商学部) に入学
2020年10月 関西学院大学内にてコンピューターサークルTech.Uniを創設
2021年9月 ヤフー株式会社 サマーインターンシップ Yahooプレミアム フロントエンド開発
2021年11月~は楽天グループ株式会社 就業型インターンシップ サーバーサイド開発 (予定)

[技術]
React, Next, Vue, Nuxtを使ったフロントエンド開発
PHP、Laravel、Python、Node.jsを使ったサーバー開発
Flutter, React Nativeを使ったモバイル開発


### テザー ナイ ウィン
関西学院大学 理工学部3年
ミャンマー出身、高校卒業後に日本へ留学
2017年 大阪日本語教育センターで日本語の勉強
2019年 関西学院大学（理工学部) に入学
2019年4月~2021年4月 株式会社六角形アルバイト フロントエンド開発
2021年5月~2021年7月 株式会社DataGustoインターン インフラ整備, UI/UX開発, 機械学習スキームなど新機能実装
2021年8月~ Line株式会社(開発３センターTチーム)就業型インターンシップ サーバーサイド開発

# 開発したアプリの概要
開発したアプリは、**筋トレ管理できるスマホアプリケーション**です。



**ProTain**とは
<details><summary>ProTain(プロテイン)は、筋トレ管理SNSです。</summary>◇ 筋トレ後にその日のメニューを記録！
◇今日1日の頑張りを全世界に共有！
◇カレンダー機能で自分の成長を可視化！
■ProTainはこんな方にオススメ■
1. 今日のメニューを記録したい人
洗練された記録機能で部位を分けてその日のメニューを簡単記録。すぐに記録できるから正確なメニューを手軽に記録することを実現。
2.自分の成長を振り返りたい人
過去の自分のウェイトや体型をカレンダーに保存することで振り返る事ができます。
3. 自分の頑張りをシェアしたい人
ProTainerと繋がることで、自分の頑張り・みんなの頑張りを共有し合えます。自分の頑張りを認知してもらうことで自分に打ち勝ち、モチベーションを高く保てます。</details>

**使用技術**

| フロントエンド | バックエンド | クラウドサービス |
|:-----------:|:------------:|:------------:|
| Dart       | Node.js     | Firebase(mBaaS)         |
| Flutter    |              | FireStore Database
|    |              | Storage
|    |              | Functions
|    |              | Authentication
|    |              | Remote Config

![ProTain_previe_done_iphone12a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619648/1275b988-cf89-34ab-abfd-2ee378e8251f.png)

# アプリの機能及び実装
### ①筋トレ管理
✔その日の筋トレメニューを記録！
✔3つのカテゴリーから選択
✔鍛えた箇所をメモ！
![ProTain_previe_done_iphone12b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619648/53eff74a-fedf-5518-d0b6-e346f89d9a63.png)

### ②SNS
✔その日のがんばりやつぶやきを世界に共有
✔筋トレ仲間と共に頑張れる
✔フォローしていいねして仲間のトレーニングを刺激にしよう！
![ProTain_previe_done_iphone12c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619648/3019e8c3-af21-7367-812c-34a875058242.png)

### ③カレンダー
✔1ヶ月カレンダーでトレーニングを振り返ろう
✔毎日のトレーニングをメモ！
✔「先週どこ鍛えたっけ？」を無くします！
![ProTain_previe_done_iphone12d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619648/3352f9be-5af4-d125-2a29-afe3d5b66740.png)


# 開発について
### 開発メンバー
###### `エンジニア` 2名
### 期間 
###### ２週間
### 技術選定理由
>なぜFlutter/Firebaseなのか？

- 一番速く創れると思ったから。
- 料金も高くならずに済む
- 前回のアプリ開発の知見を活かせると感じたから

SNSなので必須機能としては以下のものが最初に挙げられました。
#### 1, ユーザー認証
- メールアドレス/パスワードでアカウントを作成
- ログイン機能
- 既にログインされてる場合はアプリ内に遷移

#### 2, ユーザー管理・プロフィール編集機能

- プロフィール編集できる
- ユーザー情報はCloud FireStoreのUsersコレクションで管理

#### 3, CRUD（投稿機能やタイムライン・フォロー/フォロワー・いいね等）
###### 投稿
- 画像投稿できる
- 筋トレ/ダイエット/食事の各ジャンル別で投稿できる
- 筋トレを行ったメニューを投稿できる

###### タイムライン
- フォローしているユーザーの投稿を表示
- ジャンル別に投稿されているタイムラインを表示（フォローしていないユーザーも表示）

###### フォロー/フォロワー
- ユーザープロフィールからフォローができる
- フォロー解除ができる
- フォロー/フォロワー数はユーザープロフィールに表示

###### いいね
- タイムライン上の投稿にいいねができる/いいねを外せる
- いいねした投稿はユーザープロフィールで確認できる
- いいね数を表示
- いいねした時/いいねを外したときにリアルタイムで数字を変更する


上記の実装のほとんどは話し合ってからおよそ6~8時間ほどで完成しました。
（とりあえず簡易的にSNSを完成させて2週間でできそうかを検証。そのためUIなどは何も考えずにTwitterと同じものを再現）

### 難しかったところ
###### FireStoreで管理するDB設計(NoSQL設計)
- 書き込み回数と読み込み回数を考慮する
    - タイムライン表示時のページング機能の実装

以下の記事を参考にしました

https://qiita.com/1amageek/items/3270b87cca8390e053ac

https://techblog.sgr-ksmt.dev/2019/12/31/160623/

https://qiita.com/1amageek/items/d606dcee9fbcf21eeec6

### ディレクトリ構造
```
lib
├── Constants // 色やURL、collection名などを簡易的に定義 ※1
│   ├── Constants.dart 
├── Models // 各モデルを作成, シングルトンなどで定義して使い回せるように ※2
│   ├── Activity.dart
│   ├── FirebaseError.dart
│   ├── Menu.dart
│   ├── Tweet.dart
│   └── UserModel.dart
├── Providers // ページング機能実装に必要になったProvider作成
│   └── TweetsProvider.dart
├── Screens //表示する各ページを作成
│   ├── Auth
│   │   ├── AuthScreen.dart
│   │   ├── SignInScreen.dart
│   │   └── SignUpScreen.dart
│   ├── Calender
│   │   └── CalenderScreen.dart
│   ├── FeedScreen.dart
│   ├── Home
│   │   ├── CreateTweetScreen.dart
│   │   └── HomeScreen.dart
│   ├── Notification
│   │   └── NotificationsScreen.dart
│   ├── Profile
│   │   ├── EditProfileScreen.dart
│   │   └── ProfileScreen.dart
│   ├── Report
│   │   └── ReportScreen.dart
│   ├── Search
│   │   └── SearchScreen.dart
│   └── Setting
│       ├── Account
│       │   ├── AccountScreen.dart
│       │   ├── ChangePass.dart
│       │   └── Info.dart
│       ├── Security
│       │   └── SecurityScreen.dart
│       └── SettingScreen.dart
├── Services //Firebaseで使うAPIをまとめたもの ※3
│   ├── DatabaseServices.dart
│   ├── StorageService.dart
│   └── AuthService.dart
├── Utils　//便利な関数などを生成
│   ├── Auth
│   │   └── FirebaseError.dart
│   └── Utils.dart
├── Widgets //共通で使えるようなWidgetを作成　
│   ├── AddMenuDialog.dart
│   ├── AppBar.dart
│   ├── Auth
│   │   ├── background_painter.dart
│   │   ├── decoration_function.dart
│   │   ├── sign_in_up_bar.dart
│   │   └── title.dart
│   ├── Drawer.dart
│   ├── FabBottomBar.dart
│   ├── FollowList.dart
│   ├── ImagePreview.dart
│   ├── TimeLine.dart
│   └── TweetContainer.dart
└── main.dart
```
##### 注釈
:::note warn
コードを一部抜粋したものになります。全てではありません
:::
※1 Constantsディレクトリ

```Constants.dart
//共通で使う色を指定
class Palette {
  static const Color red = Color.fromRGBO(221, 17, 17, 1);
  static const Color yellow = Color.fromRGBO(255, 219, 0, 1);
  static const Color darkBlue = Color(0xff092E34);
  static const Color lightBlue = Color(0xff489FB5);
  static const Color darkOrange = Color(0xffCC7700);
}

//FireStoreのコレクションは長いので省略しておく。リファレンスの参照なので命名は~Refで統一
final _fireStore = FirebaseFirestore.instance;
final _fireAuth = FirebaseAuth.instance;

final usersRef = _fireStore.collection('users');
```

※2 Modelsディレクトリ
https://dart.dev/guides/language/language-tour#constructors

```UserModel.dart
class UserModel {
  String id;
  String name;
  String profilePicture;
  String email;
  String bio;
  String coverImage;
  String uid;

  UserModel(
      {this.id,
      this.name,
      this.profilePicture,
      this.email,
      this.bio,
      this.coverImage,
      this.uid});

  // FireStoreからインスタンスを生成
  factory UserModel.fromDoc(DocumentSnapshot doc) {
    return UserModel(
      id: doc.id,
      name: doc['name'],
      email: doc['email'],
      profilePicture: doc['profilePicture'],
      bio: doc['bio'],
      coverImage: doc['coverImage'],
      uid: doc['uid'],
    );
  }
}
```

※3 Servicesディレクトリ

```DatabaseServices.dart
class DatabaseServices { 
  static Future<int> getFollowingNum(String userId) async {
    QuerySnapshot followingSnapshot =
        await followingRef.doc(userId).collection('Following').get();
    return followingSnapshot.docs.length;
  }

  static Future<List<UserModel>> getFollowingUser(String userId) async {
    QuerySnapshot followingSnapshot =
        await followingRef.doc(userId).collection('Following').get();
    List<UserModel> followingUsers = [];

    for (var followingUser in followingSnapshot.docs) {
      await usersRef
          .doc(followingUser.id)
          .get()
          .then((value) => followingUsers.add(UserModel.fromDoc(value)));
    }
    return followingUsers;
  }

  static Future<bool> isFollowingUser(
      String currentUserId, String visitedUserId) async {
    DocumentSnapshot followingDoc = await followersRef
        .doc(visitedUserId)
        .collection('Followers')
        .doc(currentUserId)
        .get();
    return followingDoc.exists;
  }
}

```

### 今後の展望
現在DL数は200/DAUは50ほどと少しずつ増えては来ているのでこの調子でユーザー獲得を目指してたくさんの人に使ってもらいたいなと思っています。
また、リプライ機能やサブスク導入とまだまだタスクは残っているので日々アップデートを重ねてよりよいSNSを創っていけたらと思います。
是非、インストールして**筋トレ管理をアップデート**してみてください！！
![スクリーンショット 2021-12-04 17.34.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619648/3d20f2e3-5146-9595-18da-725f681c9fd8.png)
![スクリーンショット 2021-12-04 17.28.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619648/ccd1ce0f-a634-94be-5c5c-4108ac43bbb3.png)