前回、Flutterの環境作成と開発者ツールの導入をしました。
- 
  
- 
Windows + VSCodeでFlutter環境作成会社でSwiftUIがなんちゃらと言ってるんですけど、私のところにmacはない。 じゃあ自宅にmac買う? とググってみると、好きでもないのに誰が買うかぁー!!というお値段。 ほんとmacって高いです ... 続きを見る 
- 
  
- 
FlutterのDevToolsを使ってみたFlutterで開発始めようかなぁ もっと便利なプラグインないのかなぁ と思っていたら、DevToolsという開発者ツールがあるというじゃないですか。 便利そうなので導入しようとしたら若干つまずいたの ... 続きを見る 
さて、本格的に作るとして何しよう?
…特に浮かばないので、おなじみTwitterのクライアントを作ってみようと思います。
今回はプロジェクト作成直後~ヘッダーの追加です。
初期画面を作る
プロジェクトを作成した直後はサンプルコードが書かれているので、5行目以降を削除。
main.dartを新しく書き直します。
StatelessWidgetを追加する
StatelessWidgetは後から変更ができない静的なウィジェットです。
後で変えるかもしれませんが、何かしら画面が表示されてないと不便なのでとりあえずで作ります。
全部手で書いてもいいですが、「sta」くらいまで入力すると候補にstatelessと出てきます。
※statefulと間違えないように注意
候補から選択するとテンプレートが挿入されるので便利です。
以下のように変更します。
| 1 2 3 4 5 6 7 8 9 10 11 12 | void main() => runApp(MyApp()); class MyApp extends StatelessWidget {   @override   Widget build(BuildContext context) {     return MaterialApp( // MaterialAppを作るときのお約束       home: Scaffold(   // MaterialAppを作るときのお約束         body: Center(child: Text('ホーム')),       ),     );   } } | 
return MaterialAppと書いているのはマテリアルデザインのアプリを作るときのお約束です。 MaterialApp Classのリファレンスによると、homeはアプリが起動したときに最初に表示されるRouteとのこと。 ※Flutterでは要素がすべてWidget ※画面にあたるWidgetをRouteと呼びます
エミュレーターで画面を確認する
デバッグモードの場合はファイルを保存した瞬間。 通常モードの場合は以下を実行するとエミュレーターに画面が表示されます。
| 1 | >flutter run | 
通常モードの場合はr またはRを押すとHot Reloadされますが、デバッグモードは自動でReloadされます。
開発中はデバックモードおすすめ。
表示された画面がこちら。
右上のDebugの文字はDevToolsで消してます。
インスペクターで見るとこんな感じ。
ほんのちょっとしかコード書いてないのに、内部ではかなり複雑な処理がされてます。
フレームワークすごい。。。
ヘッダーを追加する
header.dartを追加
main.dartに追加するとmain.dartが複雑になってしまうので、lib/header.dartを追加してそちらに書きます。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | import 'package:flutter/material.dart'; class Header extends StatelessWidget with PreferredSizeWidget{   @override   Size get preferredSize => Size.fromHeight(kToolbarHeight);   @override   Widget build(BuildContext context) {     return AppBar(       title: Text('ホーム'),     );   } } | 
ここはちょっと理解ができてないですが、Withを使うとウィジェットに複数の属性を持たせられる?みたいです。
- PreferredSizeWidget:最適なサイズに変更するウィジェット
- preferredSize:制約がないときに設定するデフォルトサイズ
- Size.fromHeight:指定された高さでsizeを作成
- kToolbarHeight:appBarの高さの定数
| 1 2 3 | class Header extends StatelessWidget with PreferredSizeWidget{   @override   Size get preferredSize => Size.fromHeight(kToolbarHeight); | 
=>ってなに?
JavaScriptのアロー関数と同じです。
※DartはJavaScriptから派生した言語
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | // アロー関数を使う場合 Size get preferredSize => Size.fromHeight(kToolbarHeight); // アロー関数を使わない場合 Size get preferredSize {   Size.fromHeight(kToolbarHeight); }  // 2行以上の処理には使えない Size get preferredSize {   Size.fromHeight(kToolbarHeight);   print('高さ設定'); } | 
=>は使わなくても問題はないですが
- 少しコンパクトに書けてスッキリする
- 他の処理をせずに1つの結果だけを返す処理であることが分かりやすい
- 他のコードで普通に出てくるから理解はしておいたほうがいい
とのこと。
main.dartを修正
header.dartを読み込んで表示できるようにします。
※Scaffoldに設定できる要素はこちら
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import 'package:flutter/material.dart'; import 'header.dart'; // 作ったファイルを読み込み void main() => runApp(MyApp()); class MyApp extends StatelessWidget {   @override   Widget build(BuildContext context) {     return MaterialApp(        home: Scaffold(            appBar: Header(), // Header Widgetを指定する         body: Center(child: Text('ホーム')),       ),     );   } } | 
この状態でHot Reloadすると…
インスペクターにもHeaderが追加されました。
テーマを設定する
デフォルトのテーマカラーは青。
Twitterカラーといえばそうですが、チュートリアルっぽくてちょっとつまらないので変更したいと思います。
mail.dartのMaterialAppにthemeを追加します。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import 'package:flutter/material.dart'; import 'header.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget {   @override   Widget build(BuildContext context) {     return MaterialApp(        theme: ThemeData( // themeを追加         primaryColor: Colors.pink[100], // 全体の共通色を設定       ),       home: Scaffold(            appBar: Header(),         body: Center(child: Text('ホーム')),       ),     );   } } | 
Colorsクラスに用意されている色を指定。
春だからなんとなく桜色にしました。
pinkの部分にマウスを合わせると色見本が出ます。
※Colorsクラスに用意されている全色はこちら
ヘッダーの色が変わりました。
さらに、青の時は文字色が白だったのに対し、pink[100]にしたら黒になりました。
ここら辺は個別に指定することもできますが、特にこだわりがなければprimaryColorで一括指定した方が楽そうです。
ヘッダーにアイコンを表示する
Twitterアプリってヘッダーにユーザーアイコンが表示されてるんですよね。
ということで真似してみます。
画像を用意する
twitterフォルダ直下にimagesフォルダを作成して画像を入れました。
小さすぎても大きすぎても使いまわししにくそうなので、512×512pxにしてます。
Assetsに画像を登録する
アプリで画像を読み込むにはAssetsに画像ファイルを登録する必要があります。
pubspec.yamlを修正します。
| 1 2 3 4 5 6 7 8 | # コメントアウトされてる部分を # assets: #  - images/a_dot_burr.jpeg #  - images/a_dot_ham.jpeg # 解除してimages/を指定 assets:   - images/ | 
これで保存した後に出力タブにexit code 0と出てくればOK。
| 1 2 3 | [twitter] flutter packages get Running "flutter pub get" in twitter...        0.5s exit code 0 | 
Assetsから画像を読み込んで表示する
header.dartのAppBarにleadingを追加し、Image.assetで画像のパスを指定します。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import 'package:flutter/material.dart'; class Header extends StatelessWidget with PreferredSizeWidget{   @override   Size get preferredSize => Size.fromHeight(kToolbarHeight);   @override   Widget build(BuildContext context) {     return AppBar(       leading: Image.asset('images/user_icon.jpg'), // 画像のパスを指定       title: Text('ホーム'),     );   } } | 
リファレンスによると、leadingは戻るボタンやハンバーガーメニューを表示する場所のようです。
Twitterアプリの場合ユーザーアイコンタップでメニューを表示するので使い方は間違ってない、はず。
これを保存するとこんな感じ。
画像を丸くする
別に四角くてもいいんだけど、Twitterアプリ見ると丸いから丸くしたい。
ということでheader.dartをさらに修正します。
新しくStatefulWidgetを追加します。
StatelessWidgetじゃないので注意。
| 1 2 3 4 | class UserIcon extends StatefulWidget {   @override   _UserIcon createState() => _UserIcon(); } | 
この状態だと_UserIconウィジェットを作ってないのでエラーが出ます。
ということで追加します。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class _UserIcon extends State{   @override   Widget build(BuildContext context) {     return Stack(       children: <Widget>[         CircleAvatar(           backgroundImage: AssetImage('images/user_icon.jpg'),           radius: 60.0,         ),         RawMaterialButton(           child: Container(             width: 120.0,             height: 120.0,           ),           shape: CircleBorder(),           elevation: 0.0,           onPressed: () {},         ),       ],     );   } } | 
なんだかいきなり難しくなった感じがしますね。
1つずつ説明します。
まず、参考にしたのはここ。
- CircleAvatarを使うと画像を丸くしてくれる
- CircleAvatarはonTapイベントに対応していない
- Stackでウィジェットを重ねれれば両方実現できる
ということでした。
アイコンをタップしたらメニュー表示するにはonTapイベントも必要、ということでこの通りにやってみました。
Stackってなに?
ここによれば
Stack:テキスト、画像、ボタンなどを重ねて1つにできる
というウィジェットのようです。
なので、今回はCircleAvatarとRawMaterialButtonを重ねています。
| 1 2 3 4 5 6 7 8 |   Widget build(BuildContext context) {     return Stack(       children: <Widget>[         CircleAvatar(),         RawMaterialButton(),       ],     );   } | 
CircleAvatarってなに?
丸いユーザーアイコンを実現するためのウィジェット。
 リファレンスによれば
- backgroundImage:表示する画像を指定
- radius:画像の半径を指定
とのこと。
| 1 2 3 4 | CircleAvatar(   backgroundImage: AssetImage('images/user_icon.jpg'),   radius: 60.0, ), | 
画像の指定で、先ほどはImage.asset()、今回はAssetImage()を使いました。
両方assetsから画像を読み込むためのものなのですが、正直違いがよくわかりません…。
そのうち調べます。
RawMaterialButtonってなに?
自分が持っているリソースからマテリアルボタンを作るためのウィジェット。
リファレンスによれば
- child:ボタンのラベル
- shape:ボタンの形状
- elevation:ボタンが押されていない時のボタンの高さ
- onPressed:ボタンが押された時の動作
とのこと。
elevationはちょっと理解があいまいですが、elevationが1→0になるとボタンがへこむ=押されたっぽく見えるってことかなと。
| 1 2 3 4 5 6 7 8 9 | RawMaterialButton(   child: Container(     width: 120.0,     height: 120.0,   ),   shape: CircleBorder(),   elevation: 0.0,   onPressed: () {}, ), | 
childはCircleAvatarより大きい箱を作っているだけ。
shapeで丸くしています。
onPressedは今は空っぽのままにします。
UserIconをHeaderに組み込む
せっかく作ったUserIconも、Headerに組み込まなければ表示されません。
header.dartのHeaderウィジェットを修正します。
leadingでUserIcon()を指定するだけです。
ついでに、ホームという文字も中央寄せにしてみました。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Header extends StatelessWidget with PreferredSizeWidget{   @override   Size get preferredSize => Size.fromHeight(kToolbarHeight);   @override   Widget build(BuildContext context) {     return AppBar(       leading: UserIcon(), // 作ったUserIconを指定する       title: Center(         child: Text('ホーム'),       ),     );   } } | 
これを保存するとこんな感じに。
わかりにくいかもしれませんが、ちゃんとボタンとして動作してます。
ヘッダーにボタンを追加する
左側にユーザーアイコンを追加したらホームの文字が中央からずれてしまいました。
この場合のCenterはアイコン部分を除いた部分の中央ということですね。
アイコン部分のサイズを取得して中央の位置を計算…というのもできなくはないですがちょっと面倒。
AppBarには右側にもボタンを追加できるスペースがあるので、1つ追加してバランスを取ってみることにしました。
ついでにユーザーアイコンとボタンの周りにpaddingを追加してます。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @override Widget build(BuildContext context) {   return AppBar(     leading: Padding(  // Paddingを追加       padding: const EdgeInsets.all(8.0),       child: UserIcon(),     ),     title: Center(       child: Text('ホーム'),     ),     actions: <Widget>[ // actionsを追加       Padding( // Paddingを追加         padding: const EdgeInsets.all(8.0),         child: IconButton( // IconButtonを追加           icon: Icon(Icons.settings), //Iconsの設定アイコンを指定           onPressed: () {}, // 動作は空         ),       ),     ],   ); } | 
これを保存すると…。
- 設定ボタン追加
- ホームの文字が中央
- ユーザーアイコンと設定アイコンの周りに余白
となりました。
とりあえずヘッダーは完成
最終的に、ヘッダーはこんな感じになりました。
ヘッダーと言えど、結構複雑になってるんですねぇ。
長くなったのでいったんここまで。
次回はフッターを作ります。
- 
  
- 
FlutterでTwitterクライアント作成②フッター追加前回、FlutterでTwitterのクライアントアプリを作ろうということでヘッダーを追加したところまでやりました。 今回はフッターを追加して画面を切り替えられるようにしたいと思います。 目次1 フッ ... 続きを見る 
ここまでのソースをGitHubに上げたので、よろしければどうぞ。













