見出し画像

研修向け!Flutter3系【画面実装ドリル】〜基本編〜

大阪で合同会社Molasoftの代表をしてる向江です!
前回にLaravelのチュートリアル記事を書いたときはいろんな人に見てもらえてとてもありがたかったです!☺️

最近うちの会社ではiOS+Androidを同時開発できるFlutterでの開発が盛んになっており、社員教育の記事が必要だな〜と思っていたので早速書いてみることにしました!


今回は「画面側の実装」だけに特化したドリル形式の記事にしています。
新人さんが初めてFlutterに触るときや、とりあえず画面だけでも実装できるようになって欲しい!という時にお使いください!

教科書とか色々書きすぎててね。みんな挫折しまくってるからね。
正直こっちとしてはまずは画面が実装できるようになって慣れて欲しいんだよ!!なのでstateNotifierとかRiverPodとかの状態管理についての記事などはまた別途書いて行きたいと思います!


基本的な心構え

ドリル形式の問題に入っていく前にFlutterの基本的な事項や学習する上での心構えを書きます。

初心者は飛ばさずに見るように!!

まずFlutterの実装にはWidgetを使います。
Widgetは「部品」という意味を持っており、これを組み合わせることによって画面(アプリ)を成り立たせているんですね。
イメージとしてはプラモデルのようなものです。

ただこのWidgetはさらに箱のような性質も持っているため、
WidgetのなかにWidgetを入れてその中にWidgetを入れて、それでできたら二つのWidgetのグループをWidgetに入れてその二つのWidgetをWidgetで囲む。
みたいな呪いのような構図が出来上がります。
HTMLでいうdivのネスト構造みたいな。
マトリョーシカみたいな。

画像1


なのでめっちゃ紙に書いたりしてどう組み立てるのか整理する作業が大事です!
すでにFlutterをやっているスタッフKがこれをやらないで頭の中で組み立てて定期的に知能崩壊しています。
「なんか、なんか、なんか、、、」って壊れたように言い続けてます。

ぜひこうならないように訳が分からなくなったら紙になぐり書きで設計図を書いてみてください。
多分それでも分からなかった時に上司にその紙を見せればいいので一石二鳥です。

Widgetの基本構造を知る

さてこんなめんどくさそうなWidgetですが基本的な構成は一緒です。

あ、まずそもそもWidgetにはどんなのがあるか一覧を見てみましょう。
ほんの一部です。

・Text
・TextFormField
・SizedBox
・TextButton
・Padding
・Container
・AlertDialog
・BottomSheet

などなどまだまだめっちゃあります!
そう、まじでめっちゃあります!
けど使うのは一部なので安心してください。

そして気付いて欲しいんですが、
なんかもうWidgetの名前そのものがなんか実現できるやつを連想させてくれそうですよね?
Textは文章表示させるだろうし、
TextButtonはテキストだける押せるボタンを表示させそうだし、
PopupMenuButtonとか絶対ポップアップでボタンとして押せるメニューが出るでしょ。

そう、Flutterではこうやってある程度の部品の基礎部分を提供してくれてるんですね!!

FlutterはGoogle様のフレームワークですがUI/UXをめちゃくちゃ重視しています。
そのためGoogle様が提供してくれているWidgetをカスタマイズしながら使えば言い訳ですね。
この点を考えるとwebの実装みたいにHTML/CSSでゼロから実装するよりとても楽ですし、
なんならポップアップとかはwebではHTML/CSS/Javascriptで実現することが多いので、その作業量をWidget一つで実現してるの便利すぎますね。

さてこんな多種多様なWidgetですが、
ちゃんと規則性があります。

例えばText()ですが、

Text('Flutterの中の言語はDart')

これだけで画面に

'Flutterの中の言語はDart'

と表示されます。

そしてじゃあ中央寄せにしたい場合。

Text(
    'Flutterの中の言語はDart', 
    textAlign: TextAlign.center,
)

これで文章が中央寄せで配置されます。

じゃあ次にSizedBoxです。
この人は名前の通りそのサイズの空間を作り出してくれるWidgetです。
空き箱のイメージですね。

例えば

SizedBox(
   height:20,
)

これで20の高さの空間が出来ます。

さてここで、共通点が見えてきましたね、

①Widget名(
    なんかの名前:なんかの実際の値,
)

または

②Widget名(
   ':がないなんか'
    なんかの名前:なんかの実際の値,
)

ってなってます。

これ他の言語したことある方なら分かると思うんですがなんか見たことありますよね。
そう!引数の形です!☺️

引数でパッとイメージが分からない方は引数でググってください!
なんとなーくわかればOK

つまりWidget達は自分たちの役割に応じた名前の引数を求めてるってことですね。
上のケースだとSizedBoxは高さheightの値を求めたりしてます。

ちなみに上の②に該当するText()の場合の ':がないなんか'の部分は必須ってことですね。
テキスト表示させるWidgetなのにテキスト情報が無いとうまく作動出来ないので必須の引数となっています。

試しにAndroid Studioの人は
command+クリックでTextウィジェットをクリックしてみてください。
Textウィジェットを構成するファイルへ飛べます。

すると一部抜粋ですがこんなのが見れますね!

class Text extends StatelessWidget {
 /// Creates a text widget.
 ///
 /// If the [style] argument is null, the text will use the style from the
 /// closest enclosing [DefaultTextStyle].
 ///
 /// The [data] parameter must not be null.
 const Text(
   this.data, {
   Key key,
   this.style,
   this.strutStyle,
   this.textAlign,
   this.textDirection,
   this.locale,
   this.softWrap,
   this.overflow,
   this.textScaleFactor,
   this.maxLines,
   this.semanticsLabel,
   this.textWidthBasis,
   this.textHeightBehavior,
 })

ついにWidgetの本性を見るにいたりましたね!

そうWidgetはクラスだったんですね!!
そしてコンストラクターで必要な情報をもらおうとしているのが良くわかります。
textAlignとかmaxLinesとかなんかいろいろ情報を与えることが出来そうですね。

さてここでちょっと脱線しますが、
Flutterのことをネットで調べたことがある人は繋がってくると思うのですが、昔の記事だと

new Text('ddd')

ってなってませんか?
これだとTextクラスをインスタンス化しているということが分かりやすいですね。
ただ今はFlutterが進化して「もうnewいらなくね?」ってしたので不要になっています。
逆にnewがついてる記事は古いってのがすぐ分かりますね。

さて脱線しましたが、
Widgetはクラスであり、
それぞれの役割に応じた引数を求めてる存在。
これを念頭に置いていてもらって次に進みます。

実は組み合わせを考える方がムズイ

さて上記でWidgetがなんなのかが分かってきました。
ぶっちゃけた話、
Widgetを使うこと自体はそこまで難しく無いはずです。

Widgetが求めている値を適切に送ってあげるだけですから!

Flutter初心者が画面の実装で苦しむのは実際にはWidgetとWidgetの組み合わせです。
要素を重ね合わせるStackとか出てくるとめっちゃ右脳を使い始めます。

組み合わせが発生するのは、以下のようなWidgetを使うケースですね。

Row…横に要素を並べる

■■

Column
…縦に要素を並べる



Stack…要素を重ね合わせる(cssでいうposition)
🔳

そしてこの子たち同士もしっかり親子関係になれちゃうので、より複雑さを増してきます。
ちなみに親子関係は

child:
children:

例えばさっき出てきた
TextウィジェットとSizedBoxですが、 

SizedBox(
    height:20,
    child:Text('ddd'),
}

これで高さ20の空間の中にdddを表示させたりが出来ます。
SizedBoxの子供としてTextウィジェットが存在するイメージですね。

childrenはRowやColumn、Stackで利用します。
例えば後から出てくるデフォルトのページにも登場する
 Colomunだと次のような形になっています。

    Column(
         children: <Widget>[
           Text(
            'A'
           ),
           Text(
            'B'
           ),
         ],
       ),

このようにColumnの子供のウィジェットが複数になるので
そのまんまchildrenですね。

そして<Widget>[]となっているので、

Widget型の配列の形式で書いてる感じになります。

こんな感じで、
縦に並べたり重ねたり、子供にしたりを繰り返して画面を構成させます。
細かい画面になればなるほど複雑化してきます。

そしてこの複雑さを解決するシンプルな対策がさっきも言った

紙に描け!

です!
もういいから紙に書いてくれ。
なぜ頭の中でやろうとするんだ。

いやもちろん頭の良い人はそのまま書いて良いですよ。
僕もだいぶ慣れたので普通に頭の中で組み立ててますが、慣れてないうちは無理でしょ〜
暗算で122×194を計算してるくらいじゃ無いですかね?

じゃあ早速Widgetの組み立てをして見よう

Flutterの環境構築やプロジェクト作成は世にある別の記事とかで出来ている想定で行きますね。

デフォルトのmain.dartの
@override
Widget build(BuildContext context) {}の中に記述していきます。
※//のコメントは邪魔(ごめんなさい)なんで削除してます。

※Flutter3.3.10で初期立ち上げをした場合です。
違くても気にせず同じ場所に記入していきましょう!

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

実際はこんな感じになってます。
さてどれがWidgetでしょうか??

そう、、、

全部です!
Scaffoldも
Centerも
FlutterはWidgetの塊なんです。

Scaffoldは
appBarという値にAppBarウィジェットを、
bodyにはなんでも良いからウィジェットを、
floatingActionButtonに値を渡せば右下にボタンを表示させてくれます。

それでは実際にドリル形式で問題を解いて行ってみましょう!
記述はbody内だけでOKです。

body内を初期値としてこんな感じにしておきましょうか!

     body: Center(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: <Widget>[
            Text('ドリルスタート!'),
         ],
       ),
     ),

画面的にはこうなってるはずです!


それではドリル問題開始です!
答えは問題が終わった後にまとめて記述しています!
調べまくってなるべく自分でがんばって見てください!

基本的に使うWidget名+Flutterで検索しまくってください!
できるはずだ!!!

ちなみに色とか大きさとか文字、画像、アイコンはお好きなやつでOKです!

ドリル① Rowで横に並べる
(10分 難易度★)

早速組み合わせの問題です。
以下の画像と同じ実装をしてください!

使うWidgetは
ContainerとRowだけです。
色やサイズは大体で良いですよ!
この余白の部分はRowウィジェットに渡す引数の
mainAxisAlignment:が鍵を握ります。

最初の問題なので大ヒントとしては最初の黒い方はこれをRowに引数として入れるだけで良い感じの余白にしてくれます。

mainAxisAlignment: MainAxisAlignment.spaceAround,

全然何言ってるか分からない人は、Row Flutterで検索!
できれば1冊くらい書籍も見てみましょう。

スクリーンショット 2020-10-12 19.07.16


ドリル②ColumnとRowの組み合わせ
(10分 難易度★)

続いてはColumnとRowを組み合わせます。
コードは先ほどの①の続きてどんどん描いて行きましょう。以降の問題も同じようにしてください!
余白はマージンでもSizedBoxでもご自由に!

では以下を実装してください。
謎のしましまが出るかと思いますが、実装してください!
(画面サイズなどで出ない人もいるかもですがドリル続けてたらいつから出るので出た想定で続けて読んでください)

スクリーンショット 2020-10-12 19.36.10

このしましまは、そのWidgetを描画する際に領域が足りてない場合が出るエラーです。
よく見たら文字で52px足りないと言ってくれてますね。

さてこのシマシマを消すためには今回はスマホ特有の画面のスクロールを許可して上げないといけないです。

なんと初期設定ではスクロールできないんですね!!
初期設定でして欲しい!
でも簡単にスクロールは実装できます。 
SingleChildScrollView()でbody全体を囲ってみてください!

body: SingleChildScrollView(
       child: Center(
         child: Column(
〜略〜

これだけで解決するはずです!

最終的にこうなっていたらこの問題は正解です!

スクリーンショット 2020-10-12 19.36.35

ドリル③角丸ContainerとColumn
(15分 難易度★★)

基本的な部分はここまでにして、ちょっとなんかありそうなやつを実装してみましょう。



コンタクトフォームとかでありそうなやつ。ヒントとしては
Containerを角丸にしたりボーダーを表示するために
BoxDecorationというのを使います!

また、フォームのように入力する部分はTextFormFieldというWidgetを使ってください!

ドリル④丸とStack
(15分 難易度★★)

先ほどのドリルの問題に追加で丸い×マークを重ねてくっ付けてみましょう!
重ねる場合はStackというウィジェットを使います!


ヒントとしては
丸はContainerで作ることが可能です!
調べましょう!

また中のバツ印はIconウィジェットで作成可能です!
Icons.closeでバツが出るのでIconウィジェットを調べましょう!

ちなみにStackで普通に重ねるだけだとこのデザインだとバツ印の領域が足りなくて削れてしまうと思います!
どうするかはContainerにとある引数を渡せば解決できます!

ドリル⑤画像表示とあるあるリスト
(20分 難易度★★★)

いよいよラストの実装となります!
あるある〜なリスト表示をしたいと思います!
しかしこれがいろいろな要素の詰め合わせです!!

まずは実装してみるやつを確認してみましょう!

スクリーンショット 2020-10-13 12.38.30

僕ですね。
誰かI am PHPer シャツ買ってください。

文字とか画像、Iconとかはどうでも良いです。好きなものに変更してください!

今回の実装の肝です!

・角丸ボーターの外枠
・外枠にシャドウをかけてる
・画像を表示させてる
・実は画像も角丸にさせてる
・ColumnとRowをめっちゃ組み合わせてる

画像表示の部分は初挑戦ですね!
新規プロジェクトだとpubspec.yamlってファイルをいじって画像を保存するフォルダを指定してあげないといけません!
Flutter画像表示で検索!

問題は終了!

これで全てのドリルが終わりました!
どうだったでしょうか??
まだまだflutterの一部しか使ってないし画面だけの実装なのですが、
上司からとりあえず画面作れ!って言われた時になんとなく対応できるようになるんじゃないかと思います!

続きはドリルの答えと解説です!

他にも状態管理やWidgetの切り出しとか色々ありますのでぼちぼち記事化していけたらと思っています!☺️

是非是非フォロー&いいねして帰ってください!!

Twiiterもフォローしてね!

答え合わせ

※Flutterはいろいろな方法でUIを実装できるのでもちろん以下と違うコードでも問題はないはずです!1つの答えとして利用してください!

※constなどは付けないように実装してます。実際はつけれるところはつけてね。(constとは宣言しててあげると少しパフォーマンスが良くなるやつです。)

ドリル① Rowで横に並べる
(10分 難易度★)

             Text('問題① Rowで横に並べる'),
             Row(
               mainAxisAlignment: MainAxisAlignment.spaceAround,
               children: [
                 Container(
                   width: 120,
                   height: 120,
                   color: Colors.black,
                 ),
                 Container(
                   width: 120,
                   height: 120,
                   color: Colors.black,
                 ),
               ],
             ),
             Row(
               mainAxisAlignment: MainAxisAlignment.spaceBetween,
               children: [
                 Container(
                   width: 120,
                   height: 120,
                   color: Colors.red,
                 ),
                 Container(
                   width: 120,
                   height: 120,
                   color: Colors.red,
                 ),
               ],
             ),
             
            

これは流石に簡単だったかもしれません!
Rowは横に要素を並べるWidgetです!
そして並び方も自由に変えることができるんですね。
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
の部分です。

どんなものになるかは自分でやってみるのが一番ですが、startとかcenterとか色々あります!
paddingとかで頑張って余白を作らなくてもRowの初期設定で対応してくれる場合があるので覚えておきましょうね!

ドリル②ColumnとRowの組み合わせ
(10分 難易度★)

             Text('問題② RowとColumnの組み合わせ'),
             Row(
               mainAxisAlignment: MainAxisAlignment.spaceBetween,
               children: [
                 Container(
                   padding: EdgeInsets.all(20),
                   color: Colors.amber,
                   child: Column(
                     children: [
                       Container(
                         width: 120,
                         height: 120,
                         color: Colors.red,
                       ),
                       SizedBox(
                         height: 20,
                       ),
                       Container(
                         width: 120,
                         height: 120,
                         color: Colors.red,
                       ),
                     ],
                   ),
                 ),
                 Container(
                   padding: EdgeInsets.all(20),
                   color: Colors.amber,
                   child: Column(
                     children: [
                       Container(
                         width: 120,
                         height: 120,
                         color: Colors.red,
                       ),
                       SizedBox(
                         height: 20,
                       ),
                       Container(
                         width: 120,
                         height: 120,
                         color: Colors.red,
                       ),
                     ],
                   ),
                 )
               ],
             ),
  
 
 

少し組み合わせを考える必要がある問題でした!
まずはオレンジ色の外枠がRowで二つ横並びしています。
端っこにぴったりくっついてるのでspaceBetweenで実装しました。

そのRowの中で赤い四角が二つ縦に並んでいます。
縦と言えばColumnですね。
この構成さえわかればRowの中にColumnを入れ込むだけの非常に簡単な問題です。

ドリル③角丸ContainerとColumn
(15分 難易度★★)

             Text('問題③ 角丸ContainerとColumn'),
             Container(
                width: 300,
                padding: EdgeInsets.all(20),
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.black),
                  borderRadius: BorderRadius.circular(10),
                ),
                child: Column(
                  children: [
                    Text('お名前'),
                    SizedBox(
                      width: 200,
                      child: TextFormField(
                        decoration: InputDecoration(
                          border: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                            borderSide:
                                BorderSide(color: Colors.black, width: 0.8),
                          ),
                        ),
                      ),
                    ),
                    SizedBox(
                      height: 20,
                    ),
                    ElevatedButton(
                      onPressed: () {},
                      child: Text(
                        '送信',
                        textAlign: TextAlign.center,
                        style: TextStyle(color: Colors.white),
                      ),
                    )
                  ],
                ),
              ),

少し実践的な感じになってきた問題でした。
まずは外枠の角丸ボーダーが必要で、最初のContainerの

decoration: BoxDecoration(
 border: Border.all(color: Colors.black),
 borderRadius: BorderRadius.circular(10),
),

この部分で実装しています。
BoxDecorationは今後もいろんな場面でこんにちはするんで慣れていてくださいね。
ちなみにこのdecorationの危険ポイントですが、

まず

Container(
   height: 100,
   width: 300,
   color: Colors.black,
   padding: EdgeInsets.all(20),
)

これはきれいに黒い四角が表示されます。

じゃあこれを角丸にしようと思って

Container(
   height: 100,
   width: 300,
   color: Colors.black,
   padding: EdgeInsets.all(20),
   decoration: BoxDecoration(
     border: Border.all(color: Colors.black),
     borderRadius: BorderRadius.circular(10),
   ),
)

こうすると、
流れ的には黒背景の角丸ができるかと思ったら
エラーが発生します!

正解のコードは

Container(
   height: 100,
   width: 300,
   padding: EdgeInsets.all(20),
   decoration: BoxDecoration(
     color: Colors.black,
     border: Border.all(color: Colors.black),
     borderRadius: BorderRadius.circular(10),
   ),
)

です!

color:の位置がdecorationの中に入ってるんですね。
decorationを使うとcolorは絶対にdecorationの中に書かないとFlutterはブチ切れきます。覚えててください。

ちなみにClipRRectってWidgetでも角丸を簡単に作れます!が、なんか少し重たいとか、、、。
使いやすい方を使うと良いと思います!

ドリル④丸とStack
(15分 難易度★★)

丸と重ね合わせの問題でした!
まず正解はこちら!

Text('問題④ 丸とStack'),
              Stack(
                children: [
                  Container(
                    width: 300,
                    margin: EdgeInsets.all(24),
                    padding: EdgeInsets.all(20),
                    decoration: BoxDecoration(
                      border: Border.all(color: Colors.black),
                      borderRadius: BorderRadius.circular(10),
                    ),
                    child: Column(
                      children: [
                        Text('お名前'),
                        SizedBox(
                          width: 200,
                          child: TextFormField(
                            decoration: InputDecoration(
                              border: OutlineInputBorder(
                                borderRadius: BorderRadius.circular(10),
                                borderSide:
                                    BorderSide(color: Colors.black, width: 0.8),
                              ),
                            ),
                          ),
                        ),
                        SizedBox(
                          height: 20,
                        ),
                        ElevatedButton(
                          onPressed: () {},
                          child: Text(
                            '送信',
                            textAlign: TextAlign.center,
                            style: TextStyle(color: Colors.white),
                          ),
                        )
                      ],
                    ),
                  ),
                  Positioned(
                    right: 4,
                    top: 4,
                    child: Container(
                      width: 35,
                      height: 35,
                      decoration: BoxDecoration(
                        color: Colors.blue,
                        shape: BoxShape.circle,
                      ),
                      child: Icon(
                        Icons.close,
                        color: Colors.white,
                      ),
                    ),
                  ),
                ],
              ),

結構長いコードになってきましたね。
まずは丸を実装するには width/heightを指定しつつ

decoration: BoxDecoration(
   color: Colors.blue,
   shape: BoxShape.circle,
 ),

このまた登場のBoxDecorationのshapeを使っています。
そしてこのコンテナーの子供として

 child: Icon(
   Icons.close,
   color: Colors.white,
 ),

Iconウィジェットでcloseボタンを表示させることで青い丸のバツボタンを実装しています。
このIconウィジェットは便利すぎますよね。
Flutterで使えるアイコンの一覧はこちらです!

ちなみにこれでも丸いボタンが作れますがStackの関係で少しややこしくなります。

 ElevatedButton(
   style: ElevatedButton.styleFrom(
     shape: CircleBorder(),
   ),
 )

Flutterは同じUIでもいろいろな実装の仕方ができるので面白いですね。

次に必要なのが重ね合わせるStackウィジェットです。

Stack(
  children: []
)

一個覚えておきたいのがなんでもかんでもStackさせちゃダメってことです。
例えばまさに今の青い丸の上に白いバツがあるのを作ろうとした時に重なってる!って判断しちゃってStackを使うと面倒です。
childで子供の要素にしたらさっきのように簡単でしたもんね。

例えば今回のような微妙な位置に場所を指定して重ねたい時や、画像の上に重ねたい時に活用すべきです。

ちなみにStackは[]のなかの最初のWidgetが親と判断します。
なのでこの先頭のWidgetの領域をベースにどんどん上に重ねていきます。
なのでコード上では下にあるウィジェットほど重なるのは上になりますね。

そして先頭のWidgetの領域というのが味噌で、これを位置指定で例えばright: -10って感じではみ出してしまうようにしちゃうとその親の領域を超える部分が削れてしまいます。
Stackはその範囲は認識していないですからね。

簡単な方法としては回答みたいにmarginで領域を確保してあげたり、先頭のベースとなるWidgetを別に準備したり色々あるかと思います。

ドリル⑤画像表示とあるあるリスト
(20分 難易度★★★)

さて、最後の問題はどこかのアプリでありそうな画面実装でしたね。
まずは答えから!

 Text('ドリル⑤画像表示とあるあるリスト'),
 Container(
   margin: EdgeInsets.symmetric(horizontal: 12),
   decoration: BoxDecoration(
     color: Colors.white,
     boxShadow: [
       BoxShadow(
         color: Colors.black,
         spreadRadius: 1,
         blurRadius: 10,
         offset: Offset(10, 10),
       ),
     ],
     border: Border.all(color: Colors.black),
     borderRadius: BorderRadius.circular(10),
   ),
   child: Row(
     children: [
       Container(
         width: 120,
         height: 140,
         decoration: BoxDecoration(
           borderRadius: BorderRadius.circular(10),
           image: DecorationImage(
             fit: BoxFit.cover,
             image: AssetImage('images/mukae.jpg'),
           ),
         ),
       ),
       const SizedBox(
         width: 10,
       ),
       Expanded(
         child: Column(
           mainAxisAlignment: MainAxisAlignment.start,
           crossAxisAlignment: CrossAxisAlignment.start,
           children: [
             Text(
               'Mukae',
             ),
             Text(
               '合同会社Molasoft',
               style: const TextStyle(
                 fontWeight: FontWeight.bold,
                 fontSize: 16,
               ),
             ),
             Padding(
               padding: const EdgeInsets.symmetric(vertical: 4),
               child: Row(
                 children: [
                   SizedBox(
                       width: 24,
                       child: Icon(Icons.network_check)),
                   Text(
                     '大阪でシステム会社をやってます。',
                     style: const TextStyle(
                       fontSize: 12,
                     ),
                   ),
                 ],
               ),
             ),
             Row(
               children: [
                 SizedBox(
                   width: 24,
                   child: Icon(Icons.account_circle),
                 ),
                 Text(
                   'キックボクシングハマってる',
                   style: const TextStyle(
                     fontSize: 12,
                   ),
                 ),
               ],
             ),
           ],
         ),
       ),
     ],
   ),
 ),

今までの角丸に加えて影の実装が必要となっています。

   decoration: BoxDecoration(
     color: Colors.white,
     boxShadow: [
       BoxShadow(
         color: Colors.black,
         spreadRadius: 1,
         blurRadius: 10,
         offset: Offset(10, 10),
       ),
     ],

この部分ですね!
またBoxDecorationです。
この通り画面実装の際はめっちゃ乱用します。
BoxShadowの使い方については見たまんまなので割愛します。
数字とか変えて見て自分で体験してみるのが一番良いと思います。

次は画像の表示です。
WEBとかだとスッとすぐに表示できているからいきなり画像を呼ぶだけですが、Flutterだとpubspec.yamlというファイルでどの画像フォルダを見に行くのか事前に指定しないといけません。

pubspec.yamlを開いておそらく48行目あたりの
assets:ってとこですね。

flutter:

 # The following line ensures that the Material Icons font is
 # included with your application, so that you can use the icons in
 # the material Icons class.
 uses-material-design: true

 # To add assets to your application, add an assets section, like this:
 assets:
   - images/

 # An image asset can refer to one or more resolution-specific "variants", see
 # https://flutter.dev/assets-and-images/#resolution-aware.

 # For details regarding adding assets from package dependencies, see
 # https://flutter.dev/assets-and-images/#from-packages

えっと、このpubspec.yamlはです。

よく覚えててください。
半角一個分でもインデントがずれているとエラーを発生させます。
上記のコードを空間とか含めて気をつけてコピペしないといけません。

その後にAndroid Studioならpub getって右上の青い文字をクリックしたら準備が完了するはずです。

その上でimageってフォルダをプロジェクト直下に置きます。
その中に画像を保存しておけばとりあえず画像が表示されるようになります。(色々あるけど今はとりあえずね。)

あとはその画像の名前で、
正解のコードのように

   Container(
     width: 120,
     height: 140,
     decoration: BoxDecoration(
       borderRadius: BorderRadius.circular(10),
       image: DecorationImage(
         fit: BoxFit.cover,
         image: AssetImage('images/mukae.jpg'),
       ),
     ),
   ),

またもBoxDecorationのimageという項目にAssetImage()というウィジェットを使うことで画像を反映させています。

ちなみにいきなりAssetImage()で画像を表示することも可能ですが、
サイズを調整したり今回のように角丸にしたりする際はBoxDecoration()の中に書いていきます。

そしてこっそりある
fit: BoxFit.cover,
これは非常に重要です。
試しに削ってみると角丸とかを無視して画像が表示されます。

ここまでが出来たらあとはColumnとRowの組み合わせですね!!
正解のコードも唯一の正解というわけではないので職場環境に合わせてよりよいコードで実装して見てください!

はい!以上で画面実装基本編のドリルが完了しました!

意外とHTML/CSSを覚えるよりも簡単!っていう人もいるくらいFlutterは簡単な実装が出来ます!(簡単な部分はね)

最後まで見てくれてありがとうございました〜!



この記事が気に入ったらサポートをしてみませんか?