オサレUI作ってみる Identifying Text APP

オサレなコンセプトUIデザインを参考に、Flutterで実際に動くものを作ってみる。

なお完全なクローンになるまでは突き詰めないし、ビジネスロジックとの組み合わせ易さは考慮せず、ハードコードしまくる。
記事に載せているソースコードは一部抜粋。

# 参考デザイン

https://dribbble.com/shots/6252337-Identifying-Text-APP

# ソースコード

https://github.com/KoheiKanagu/identifying_text_app (gifあり)


# トップページ

## AppBar

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46022/b6b48eb8-8a0d-b040-dc3a-f330d3c8c2fb.png" width=400px>

- Listをスクロールすると縮みそうだったのでSliverAppBarを利用
- WelcomeとSearchはAppBarに固定して、常に表示させる
- "Search"のRowはSizedBoxで包んであげないとサイズが0になって表示されなかった
- HeaderContent()でflexibleSpaceのWidgetを作る
- TextFieldはExpandedで包んであげないと表示されなかった

```dart
SliverAppBar(
  expandedHeight: 240.0,
  pinned: true,
  title: AppBar(
      title: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          Text("Welcome"),
          SizedBox(
            width: 100,
            child: Row(
              children: <Widget>[
...
                Expanded(
                  child: TextField(
                    decoration: InputDecoration(hintText: "Search"),
                  ),
...
      ),
      elevation: 0),
  flexibleSpace: FlexibleSpaceBar(background: HeaderContent()),
  floating: true,
),
```

### HeaderContent

- MediaQueryからステータスバーのサイズを取ってきてpaddingに追加
- VerticalDividerはContainerで包んでheightを設定しないと表示されなかった

```dart
final double statusBarHeight = MediaQuery.of(context).padding.top;

return Container(
  padding: new EdgeInsets.only(
    top: statusBarHeight + 66,
...
  ),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>[
...
      Card(
        margin: EdgeInsets.symmetric(vertical: 28),
        elevation: 20,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
...
            Container(
              height: 24,
              child: VerticalDivider(
                width: 20,
                color: Colors.white,
              ),
            ),
...
```

## Body

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46022/44f2c422-39be-54a9-527c-cd3b370d9c31.png" width=400px>

### SliverList

- RowとColumnで構成
- xボタンはOutlineButtonをRoundedRectangleBorderで角を丸めつつ、Containerでサイズを調整

```
Container(
  height: 24,
  width: 48,
  child: OutlineButton(
    child: Icon(
      Icons.close,
      size: 16,
      color: Colors.grey,
    ),
    shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(30)),
  ),
)
```

### Identifyボタン

- FloatingActionButtonをFloatingActionButtonLocation.centerDockedにして設置
- Listより下にスペースがあったのでbottomNavigationBarにSizedBoxで調整

```
floatingActionButton: FloatingActionButton.extended(
  label: Padding(
    padding: EdgeInsets.symmetric(horizontal: 20),
    child: Text(
      "Identify",
      style: TextStyle(color: Colors.white, fontSize: 20),
    ),
  ),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: SizedBox(height: 100),
```


# 詳細ページ

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46022/fdc6ea70-8d8c-1472-7afa-0fda768848aa.png" width=400px>

- AppBarにRowでOutlineButtonを並べた
- English <=> JapaneseはAppBarのbottomにPreferredSizeに包んだRowで表現
- ListViewでCardを置いている
- （罫線の出し方がよく分からなかったのでスルーしてしまった... Textのunderlineでは思ってたのとは違ったので、1行分の文章を切り出してContainerに入れてBorder？）

```
return Scaffold(
    appBar: AppBar(
      title: ButtonTheme(
...
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            OutlineButton(
...
          ],
        ),
      ),
      bottom: PreferredSize(
        preferredSize: Size.fromHeight(68),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            createDropdownButton("English"),
...
            createDropdownButton("Japanese"),
          ],
        ),
      ),
    ),
    body: ListView(
      padding: EdgeInsets.symmetric(horizontal: 12),
      children: <Widget>[
        createCard(english),
        createCard(japanese),
      ],
    ));
```