【Flutter入門】シンプルなメッセージアプリのUIを作る

#【はじめに】
Qiita初投稿です。学生エンジニアのKCPと申します。よろしくお願いします:smirk_cat:

2月からFlutterの学習を開始し、その一環としてこちらの記事[Build a simple message app UI in Flutter](https://fluttermaster.com/build-a-simple-message-app-ui-in-flutter/)を模写していたのですが、初心者にとっては解説が少なくわかりづらかったのでもう少し噛み砕いて説明していきます。

シンプルなメッセージアプリのUIのみを作っていきます。メッセージはできません。

#【対象読者】
- Flutterの環境構築が終わっている
- なんとなくだがチュートリアルを一通り終わらせた
- 初心者だけどいい感じのUIを作ってみたい

#【今回作るもの】
<img width="357" alt="スクリーンショット 2020-02-12 16.14.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487891/01bfbcd7-700d-4179-a2fa-5c6242f7c86b.png">

#【実装】
###1. 雛形を作る

まずは新しいプロジェクトを作ります。

`$ flutter create message_app`

カウンターアプリができると思いますが、一旦まっさらな状態にします。

```dart:main.dart

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold();
  }
}
```

###2. データの作成
まずはデータを入れるための新しいファイルを作成します。

場所はmain.dartと同じ階層のlibディレクトリの下です。

`$ touch chat_model.dart`

そしてこちらのサイトのAPIを利用してユーザーデータを作成します。


> **RANDOM USER GENERATOR**
A free, open-source API for generating random user data. Like Lorem Ipsum, but for people.
https://randomuser.me/

ChatModelクラスを作成し、この中にデータを入れていきます。

```dart:chat_model.dart
class ChatModel {
  final String avatarUrl; 
  final String name;
  final String datetime;
  final String message;

  ChatModel({this.avatarUrl, this.name, this.datetime, this.message});
}
```
上から画像、名前、日時、メッセージを定義しました。

次に使用する6人のダミーデータを作成します。

```dart:chat_model.dart
class ChatModel {
  final String avatarUrl;
  final String name;
  final String datetime;
  final String message;

  ChatModel({this.avatarUrl, this.name, this.datetime, this.message});

<!-- 追加 -->
  static final List<ChatModel> dummyData = [
    ChatModel(
      avatarUrl: "https://randomuser.me/api/portraits/women/34.jpg",
      name: "Laurent",
      datetime: "20:18",
      message: "How about meeting tomorrow?",
    ),
    ChatModel(
      avatarUrl: "https://randomuser.me/api/portraits/women/49.jpg",
      name: "Tracy",
      datetime: "19:22",
      message: "I love that idea, it's great!",
    ),
    ChatModel(
      avatarUrl: "https://randomuser.me/api/portraits/women/77.jpg",
      name: "Claire",
      datetime: "14:34",
      message: "I wasn't aware of that. Let me check",
    ),
    ChatModel(
      avatarUrl: "https://randomuser.me/api/portraits/men/81.jpg",
      name: "Joe",
      datetime: "11:05",
      message: "Flutter just release 1.0 officially. Should I go for it?",
    ),
    ChatModel(
      avatarUrl: "https://randomuser.me/api/portraits/men/83.jpg",
      name: "Mark",
      datetime: "09:46",
      message: "It totally makes sense to get some extra day-off.",
    ),
    ChatModel(
      avatarUrl: "https://randomuser.me/api/portraits/men/85.jpg",
      name: "Williams",
      datetime: "08:15",
      message: "It has been re-scheduled to next Saturday 7.30pm",
    ),
  ];
}
```

###3. UI設計
やっとUIの実装に移ります:wink:

まずは先ほど作成したデータのファイルをインポートします。
main.dartの一番上に記述してある
`import 'package:flutter/material.dart';`の下に

`import './chat_model.dart';`を記述。

そうしたらとりあえずappBar部分を完成させます。

```dart:main.dart
@override
  Widget build(BuildContext context) {
    return Scaffold(
<!-- 追加 -->
      appBar: AppBar(
        title: Text('fluttermaster.com - Message Chat'),
      ),
    );
  }
```

<img width="355" alt="スクリーンショット 2020-02-12 17.14.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487891/28595653-98d1-623b-2d4e-b34635fd0e7e.png">

body部分の作成に移ります。

メッセージしているユーザーのリストを作成するためにListViewウィジェットを作成します。

```dart:main.dart
body: Container(
        child: ListView.builder(
           itemCount: ChatModel.dummyData.length,
           itemBuilder: (context, index) {
             ChatModel _model = ChatModel.dummyData[index];
            }
          ),
        ),
```

ListView.builder 関数に要素数を itemCount として、 一個一個の要素に対しての表示は itemBuilder を適応させます。

今回、 itemCount には ChatModel.dummyData.length が入っています。 

この場合itemCountには先ほど6人分のデータを作成したので6を適応させていることになります。

```dart:main.dart
body: Container(
        child: ListView.builder(
           itemCount: ChatModel.dummyData.length,
           itemBuilder: (context, index) {
             ChatModel _model = ChatModel.dummyData[index];
　　　　　　　　//追加
             return Column(
              children: <Widget>[
                ListTile(
                  leading: CircleAvatar(
                    radius: 24.0,
                    backgroundImage: NetworkImage(_model.avatarUrl),
                  ),
                  title: Row(
                    children: <Widget>[
                      Text(_model.name),
                      SizedBox(
                        width: 16.0,
                      ),
                      Text(
                        _model.datetime,
                        style: TextStyle(fontSize: 12.0),
                      ),
                    ],
                  ),
                ),
               ],
             );
            },
          ),
        ),
```
Coliumnをbodyとして表示する値としてListTileウィジェットを入れていきます。


> ListTileは、ListViewの子ウィジェットとして使用でき、非常に整ったレイアウトのListViewを簡単に作れることとListViewの要素をタップした時などのイベントを容易に実装できるウィジェットです。

>参照: https://saharablog.com/mobile-app/flutter-app/listtile/

**leading**
左端にウィジェットを追加。Iconが表示されることが多い。

**title**
真ん中にウィジェットを追加

**subtitle**
タイトルの下に表示されるサブタイトル

**trailing**
右端にウィジェットを追加

**CircleAvatar**はアバターなどを表示するときに円形にクリップしてくれるウィジェットです。
今回の場合はユーザーのアイコンに使われていますね。



ここまで書いたらほとんど終わったも同然です:kissing:

<img width="352" alt="スクリーンショット 2020-02-12 18.05.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487891/70ec7b47-3aef-3ec5-a504-ac933f7eb9f3.png">

###4. 細かい修正
少しユーザー同士が寄りすぎているため、Dividerウィジェットを使って間隔を空けます。

```dart:main.dart
body: Container(
        child: ListView.builder(
           itemCount: ChatModel.dummyData.length,
           itemBuilder: (context, index) {
             ChatModel _model = ChatModel.dummyData[index];
             return Column(
              children: <Widget>[

              //追加
                Divider(
                  height: 12.0,
                ),
              //ここまで

                ListTile(
                  leading: CircleAvatar(
                    radius: 24.0,
                    backgroundImage: NetworkImage(_model.avatarUrl),
                  ),
                  title: Row(
                    children: <Widget>[
                      Text(_model.name),
                      SizedBox(
                        width: 16.0,
                      ),
                      Text(
                        _model.datetime,
                        style: TextStyle(fontSize: 12.0),
                      ),
                    ],
                  ),
                ),
               ],
             );
            },
          ),
        ),
```

ボックスの全体の高さはheightによって指定され、色や線の厚さを変更することもできます。

Dividerについてはこちらに詳しく記載されています。[Flutter公式ドキュメント: Divider](https://api.flutter.dev/flutter/material/Divider-class.html)

最後にメッセージとチャットしたいユーザーに飛べる矢印マーク(実際は飛べない)を追加します。

```dart:main.dart
body: Container(
        child: ListView.builder(
           itemCount: ChatModel.dummyData.length,
           itemBuilder: (context, index) {
             ChatModel _model = ChatModel.dummyData[index];
             return Column(
              children: <Widget>[
                Divider(
                  height: 12.0,
                ),
                ListTile(
                  leading: CircleAvatar(
                    radius: 24.0,
                    backgroundImage: NetworkImage(_model.avatarUrl),
                  ),
                  title: Row(
                    children: <Widget>[
                      Text(_model.name),
                      SizedBox(
                        width: 16.0,
                      ),
                      Text(
                        _model.datetime,
                        style: TextStyle(fontSize: 12.0),
                      ),
                    ],
                  ),

　　　　　　　　　　　//追加
                  subtitle: Text(_model.message),
                  trailing: Icon(
                    Icons.arrow_forward_ios,
                    size: 14.0,
                  ),
                ),
               ],
             );
            },
          ),
        ),
```
完成！よっしゃ！

<img width="345" alt="スクリーンショット 2020-02-12 18.37.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487891/ebe2e4b5-cee2-da1c-5443-fc032b76c3fb.png">

#【参照】
さはらのブログ:https://saharablog.com/mobile-app/flutter-app/listtile/
わかりやすいFlutterリファレンス:https://nzigen.com/flutter-reference/2018-04-17-list-view.html
Flutter Widget catalog:https://flutter.dev/docs/development/ui/widgets
Flutter Master:https://fluttermaster.com/build-a-simple-message-app-ui-in-flutter/

#【最後に】
マークダウン記法とかあんま知らんくてめっちゃ時間かかってしまった。

自分も始めたばかりなので何か間違いや知っておくべきことががあればご教授お願いします:pensive: