「[[.NET 開発基盤部会 Wiki>http://dotnetdevelopmentinfrastructure.osscons.jp]]」は、「[[Open棟梁Project>https://github.com/OpenTouryoProject/]]」,「[[OSSコンソーシアム .NET開発基盤部会>https://www.osscons.jp/dotNetDevelopmentInfrastructure/]]」によって運営されています。 -[[戻る>Flutter]] --Flutterのファースト・ステップ --[[Flutterのセカンド・ステップ]] --[[Flutterのサード・ステップ]] --[[Flutterの4thステップ]] --[[Flutterの5thステップ]] *目次 [#l34bf167] #contents *概要 [#rde47fa2] [[Flutter]] の step by step(其の一)。 *手順1:インストールから実行まで。 [#sdd49677] [[コチラ>Flutter#a9ed99c8]]を参考にして実施 **インストール [#e5336e5a] ***ダウンロード [#r9e85d44] https://flutter.dev/docs/get-started/install/windows ***解凍&パス指定 [#pda245ef] flutter_windows_x.x.x-stable.zip -解凍 C:\prog\dev\flutter -パス指定 // ユーザーの環境変数に設定する例 $path =[System.Environment]::GetEnvironmentVariable('PATH','User') $path +=";" + "C:\prog\dev\flutter\bin"; [System.Environment]::SetEnvironmentVariable('PATH',$path,'User') ***チェック(1) [#h3bd155b] 以下のCommandを実行し、TODOを確認する。 >flutter doctor ***[[Android Studioのインストール>Android Studio#a4e75be5]] [#qc322227] -既定値でインストール(Android Virtual Device(AVD)もインストール)。 -最後に、ライセンス条項に同意する。 flutter doctor --android-licenses -[[初期設定>Android Studio#yfcbb29e]] --必要に応じて、Android Targetをインストール --日本語化したら問題が出るらしい(現時点で解決しているか不明)。 -[[PATH環境変数>Android Studio#f38ea058]] --ツール類 %USERPROFILE%\AppData\Local\Android\Sdk\tools %USERPROFILE%\AppData\Local\Android\Sdk\tools\bin %USERPROFILE%\AppData\Local\Android\Sdk\platform-tools --gradleのbin~ %USERPROFILE%\.gradle\wrapper\dists\gradle-6.7-all\cuy9mc7upwgwgeb72wkcrupxe\gradle-6.7\bin >※ 前述のPowerShellだと、%USERPROFILE%が展開されねぇんだけど...。 ***プラグインのインストール [#of1de50d] 「File」メニュー -> 「Settings..」 -> 「Plugins」 -Flutterプラグインの「Install」ボタンを押下 -Dartプラグインの「Install」ボタンを押下 ***チェック(2) [#h3bd155b] 以下のCommandを実行し、問題が無いことを確認する。 >flutter doctor Doctor summary (to see all details, run flutter doctor -v): [√] Flutter (Channel stable, 2.0.3, on Microsoft Windows [Version 10.0.19041.867], locale ja-JP) [√] Android toolchain - develop for Android devices (Android SDK version 30.0.3) [√] Chrome - develop for the web [√] Android Studio (version 4.1.0) [√] VS Code, 64-bit edition (version 1.47.2) [√] Connected device (2 available) • No issues found! > ***余談:SDKのパス変更 [#lbb2ae25] SDKのパスを変更する場合、以下の手順に従って変更する。 -Flutter SDKのPathを変えた時にやること【Flutter】 - Qiita~ https://qiita.com/umi_mori/items/46073f5bfb88348efd16 **サンプルの生成と実行 [#p6672f41] ***新規作成プロジェクト [#ab55e378] [[Android Studio>#qc322227]]で、新規作成プロジェクトする。 -「Start Flutter New Project」~ or 「File -> New -> New Flutter Project..」 -「Flutter Application」を選択 |プロジェクトタイプ|概要|h |Flutter Application|アプリケーション用| |Flutter Plugin Android|ネイティブAPI用ライブラリ用| |Flutter Package|画面部品用| |Flutter Module|Dartライブラリ用| -以下を入力する。 |入力項目|概要|h |Project Name|プロジェクト名| |Flutter SDK path Project location|Flutter SDKをインストールしたフォルダを指定。| |Description|プロジェクト、アプリの説明| |Package Name|通常、ドメインを反対にした値を入力| -[[コチラ>Flutter#zb4ac86e]]のコードが生成される。 ***デバッグ実行 [#n16190b8] -実行するデバイスでChromeなどのブラウザを選択し、 -緑三角(実行)、虫(デバッグ)ボタンを押すと実行。 -ブラウザではなく、エミュレータや実機を使う手順は[[コチラ>#re710153]]。 ***プロジェクトの開発 [#fe5f689f] 以降の手順で説明する。 **デバッグ実行 [#re710153] [[Android Studio>#ab55e378]]で1GB、[[エミュレータ>#qef3780a]]で6GB程、メモリを消費するので、~ 開発機のメモリが16GB程度では少々、スペック不足、故に[[実機でのデバッグ>#ud190db6]]を行うことになる。 ***エミュレータを登録して実行 [#qef3780a] -必要に応じて、[[Windows ハイパーバイザー プラットフォーム>Androidのエミュレータ#pfe59a21]]を有効化しておく。~ (コレにより、Hyper-V、Docker for Windows、WSL2との同居が可能に) -[[コチラ>Androidのエミュレータ#s9f7ba94]]の新し目の手順になる。 --エミュレータの登録 ---「Tools」メニュー -> 「AVD Manager」 ---(「Create Virtual Device ..」でデバイスを選択し「Next」ボタンを押下) ---(API Levelを選択(最新に近いモノを選択する)) ---「Actions」から三角ボタンを押して、エミュレータを起動 --エミュレータでデバッグ実行 ---実行するデバイスを選択し、 ---緑三角(実行)、虫(デバッグ)ボタンを押すと実行。 ***実機でデバッグ実行する場合 [#ud190db6] -[[IDEサポート>Flutter - ビルドとデプロイ#wf653b03]]~ 最近の開発環境では、IDEサポートが追加されている。 -マニュアル --「Build」メニュー -> 「Flutter」 -> 「Build APK」~ を実行して、apkファイルをプロジェクト出力。 --[[コチラ>Androidのデプロイ#o1221b8b]]の手順を参考にして、以下のフォルダのapkファイルを送り込む。 %HOMEPATH%\AndroidStudioProjects\(flutter_app)\build\app\outputs\flutter-apk\app-release.apk ※ コマンドの実行に、[[PATH環境変数の設定>#qc322227]]は必須。 ***Flutter Devtoolsを利用する。 [#i45db2b6] -「Tools」メニュー -> 「Flutter」 -> 「Open Flutter Devtools」を実行して、 -デバッグ実行中に、[Flutter Inspector]のペインを開くとUI要素のツリーが表示される。 #ref(FlutterInspector.png,left,nowrap,FlutterInspector,75%) -Flutter Devtoolsでは、その他、様々なツールを使用できる。 --Timeline --Memory --Performance --Debugger --Logging ***その他のツールを利用する。 [#m0d93c29] -外部デザイナとして、Flutter Studioがある。~ --AppBuilder 2~ https://flutterstudio.app/ -加えて、 --[[Flutter Inspector>#i45db2b6]]と、 --Widget等の追加、削除+Hot Reload >を併用すると、RAD風に開発できる。 -参考 --開発効率が上がるFlutter x Android StudioのTips集 - Qiita~ https://qiita.com/akatsuki174/items/d66fa84fe51aa2d9a1a6 *手順2:イベントを実装する。 [#jf1cdd07] **ボタンのWidget [#m8b57d15] -以下のようなボタンがある。 -「() {}」(空のラムダ式)を、~ 生成されたコード中にある、~ 「_incrementCounter」に変更してテスト可能。 ***RaisedButton [#e36807a9] -onPressedに、任意のメソッドを書く。 -deprecatedらしい→[[ElevatedButton>#f894aa5a]] RaisedButton( child: const Text('Button'), color: Colors.orange, textColor: Colors.white, onPressed: () {}, ), ***ElevatedButton [#f894aa5a] onPressedに、任意のメソッドを書く。 ElevatedButton( child: const Text('Button'), style: ElevatedButton.styleFrom( primary: Colors.orange, onPrimary: Colors.white, ), onPressed: () {}, ), ***FlatButton [#z69dde61] -onPressedに、任意のメソッドを書く。 -deprecatedらしい→[[TextButton>#wa77bfd4]] FlatButton( child: const Text('Button'), textColor: Colors.black, onPressed: () {}, ), ***TextButton [#wa77bfd4] onPressedに、任意のメソッドを書く。 TextButton( child: const Text('Button'), style: TextButton.styleFrom( primary: Colors.black, ), onPressed: () {}, ), ***OutlineButton [#vf4dde96] -onPressedに、任意のメソッドを書く。 -deprecatedらしい→[[OutlinedButton>#tdfd2905]] OutlineButton( child: const Text('Button'), onPressed: () {}, ), ***OutlinedButton [#tdfd2905] onPressedに、任意のメソッドを書く。 OutlinedButton( child: const Text('Button'), style: OutlinedButton.styleFrom( primary: Colors.black, ), onPressed: () {}, ), ***IconButton [#daa818af] ***ButtonBar [#ncee4c62] ***FloatingActionButton [#x2287a59] ***PopupMenuButton [#v437fd5e] ***DropdownButton [#mbd05014] -参考 --【Flutter】ドロップダウンボタンを表示する | スクリプちん~ https://dtpscriptin.com/flutter-dropdownbutton/ --Flutterで2つのDropdownButtonを使う - Qiita~ https://qiita.com/bigface00/items/c0fad7b0e94869d1c609 **任意のWidget [#n5395d65] [[ボタン>#m8b57d15]]でない、任意のWidgetに、イベントを実装できる。 ***InkWell [#cbab219c] 簡単に、Ripple エフェクトを付ける事が出来る。 Material( // 親にMaterialが必須 color: Colors.blue, // Material自体は青を指定 child: Center( // child: Ink( // ここをContainerにするとsplashが効かないので、Inkに変える color: Colors.yellow, // 背景色はここで指定 width: 200.0, height: 100.0, child: InkWell( onTap: () {}, child: Center(child: Text('YELLOW')) ), ), ), ) ***GestureDetector [#ta864cd4] [[InkWell>#cbab219c]]のRipple エフェクトは無いが、~ 他のイベント(ジェスチャー)にも反応する。 GestureDetector( // タッチ検出対象のWidget child: Text( 'How to use GestureDetector', textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold), ), // ダブルタップ onDoubleTap: _incrementCounter ) **参考 [#c9d61f89] -[Flutter]コピペで使える!ボタンのデザイン16種類をまとめました~ https://zenn.dev/coka01/articles/cb0b632766138e9858e7 -Flutter: InkWell vs GestureDetector: what is the difference? - Stack Overflow~ https://stackoverflow.com/questions/56725308/flutter-inkwell-vs-gesturedetector-what-is-the-difference ***Qiita [#r7419f30] -Flutter:Widget一覧 > Material Components > Buttons~ https://qiita.com/matsukatsu/items/e289e30231fffb1e4502#buttons -Flutter でよく使う(と思う)コードまとめ > イベント~ https://qiita.com/chooyan_eng/items/e7f1fcca1eeb0cf150b5 *手順3:画面遷移処理を実装する。 [#y1c65c17] -画面遷移の実装について。 -MaterialApp以下で実装。 --MaterialApp が持っている Navigator を使って画面遷移。 --Navigatorの動作イメージはStackで、一番上の Route が現在の画面。 ---無限にPushできてしまうので、実装を考える必要がある。 ---常にPoP & Pushだと、戻った時に、常に状態は保存されていない。 ---... **API [#r068b122] ***push [#ve163562] -Widget を MaterialPageRoute に渡して Route を作る。 -Navigator.pushにRouteを渡して画面遷移。 Navigator.of(context).pop(); Navigator.of(context).push( MaterialPageRoute( builder: (context) { return MyHomePage(title: 'Flutter Demo Home PageA'); }, ), ); ***pushNamed [#l7e321b0] -予め MaterialApp に Routes を登録しておく。 Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( ... ), routes: { '/': (context) => MyHomePage(title: 'Flutter Demo Home Page'), '/message': (context) => MessageView(), -Navigator.pushNamedにルート名を渡して画面遷移。 -- Navigator.of(context).pushNamed("/message"); -- Navigator.pushNamed(context, '/message', arguments: ...); **メニュー [#ab032765] ***AppBar [#uab4869a] -以下を参考に、AppBarのactions: --[[IconButton>#daa818af]]のonPressed: --[[PopupMenuButton>#v437fd5e]]のonSelected: >にNavigatorによる[[画面遷移>#y1c65c17]]を書く。 -MaterialPageRouteのbuilderが返すWidgetにMyHomePageを流用。 -参考 --AppBar | Flutter Doc JP~ https://flutter.ctrnost.com/basic/navigation/appbar/ ***Drawer [#a39c49ff] -以下を参考に、ListTileのonTap:にNavigatorによる[[画面遷移>#y1c65c17]]を書く。 -MaterialPageRouteのbuilderが返すWidgetにMyHomePageを流用。 -参考 --Drawer | Flutter Doc JP~ https://flutter.ctrnost.com/basic/navigation/drawer/ --Flutterでdrawerを利用した遷移時にdrawerを閉じる方法 - Qiita~ https://qiita.com/hiko1129/items/9d31938046bbf4dcad2e ***TabBar [#nb84e642] -以下は、TabPageが遷移先で、Navigatorは使用しない。 -_MyHomePageState.buildメソッドが~ Scaffoldではなく、DefaultTabControllerを返すのもポイント。 -参考 --TabBar | Flutter Doc JP~ https://flutter.ctrnost.com/basic/navigation/tabbar/ ***BottomNavigationBar [#md88c7f0] -以下は、PageWidgetが遷移先で、Navigatorは使用しない。 -bottomNavigationBarのonTap:にIndex切替処理を書く。 -参考 --BottomNavigationBar | Flutter Doc JP~ https://flutter.ctrnost.com/basic/navigation/bottomnavigationbar/ **参考 [#sefba61b] -ナビゲーション | Flutter Doc JP~ https://flutter.ctrnost.com/basic/navigation/ -FlutterのNavigationとRoutingを理解する~ https://itome.team/blog/2019/12/flutter-advent-calendar-day10/ ***Qiita [#p8ca044b] -Flutter でよく使う(と思う)コードまとめ > 画面遷移~ https://qiita.com/chooyan_eng/items/e7f1fcca1eeb0cf150b5 -[Flutter]画面遷移のやり方~ https://qiita.com/kono-hiroki/items/b1a8f19dfab371e7816d *手順4:レイアウトを行う。 [#gb136ec0] body, children or childと、レイアウトを行う。 **Layout系 [#k8fee006] ***Container [#r1745fe6] -どちらも原点は”左上” -デフォルトで --Columnは縦方向いっぱい --Rowは横方向いっぱい >のサイズになる。 body: Container( ... child: Column or Row ***Center [#dc1ee6cd] -中央に寄るのはColmn(Row)自体。 -内部コンテンツの位置は変わらない。 body: Center( ... child: Column or Row ***その他 [#j25f12b4] -Align -Padding -FittedBox / BoxFit -SafeArea **Multi Layout系 [#lcc20e22] ***Colmn [#f3227fa2] -列内の行は縦にスタック -行内の内部コンテンツは横にスタック body: Column( ... children: <Widget>[ Row( ***Row [#s89c6bd9] -行内の列は横にスタック -列内の内部コンテンツは縦にスタック body: Row( ... children: <Widget>[ Column( ***その他 [#ydacbb99] -Expanded -Stack -ListView / ListTile -TabBar / Tab / TabBarView -BottomNavigationBar **margin/padding [#u24eb54f] ***margin、paddingプロパティ [#u8ce80ee] -childプロパティのあるWidgetの、~ margin、paddingプロパティにEdgeInsetsを指定する。 margin: EdgeInsets.only(top: 100), padding: EdgeInsets.only(top: 50), -EdgeInsetsには以下のメソッドがある。 --only:上下左右のmargin/paddingを指定 --fromLTRB:onlyの名前無し引数版(上下左右) --symmetric:縦方向横方向のmargin/paddingを指定 --all:全方向のmargin/paddingを指定 -参考 --【Flutter】Containerのmarginの設定方法~ EdgeInsetsをざっくりまとめる | tty×Dev~ https://ttydev.com/2020/11/11/flutter-edgeinsets/ ***margin、paddingプロパティが無い場合 [#nb407d63] RowやColumnなど、childrenプロパティのあるWidgetは、~ margin、paddingプロパティを持たないが、~ Containerをネストして使用すると面倒なので、~ SizedBoxを拡張したSpaceBoxを使用すると良い。 -参考 --Padding(またはMargin)だけが目的でContainerを~ 使うのは間違ってる(というか分かりづらい) -Flutter-~ **AxisAlignment [#j3887756] 内部コンテンツの位置 ***MainAxisAlignment [#jd40a297] -前述の、...の部分に、 mainAxisAlignment: MainAxisAlignment.XXXXX, >...と書き、主軸方向の配置位置を調整する。 --start/end~ 先頭/末尾寄せ --center~ 中央寄せ --spaceEvenly~ 間隔を均等に配置 --spaceAround~ 各余白を均等に配置 --spaceBetween~ 最大限に分散配置 ***CrossAxisAlignment [#v5b164d4] -前述の、...の部分に、 crossAxisAlignment: CrossAxisAlignment.XXXXX, >...と書き、交差軸方向の配置位置を調整する。 --start/end~ 先頭/末尾寄せ --center~ 中央寄せ --stretch~ 引き伸ばして配置 --baseline~ ベースラインを揃えて配置 -SizedBox.expand~ 交差軸方向のスペースをすべて取らない場合、~ CrossAxisAlignmentが適切に機能しない事がある。~ この場合は、SizedBox.expandで、スペースを消費させる。 body: SizedBox.expand( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ ..., ], ), ), **参考 [#g0352efc] -Flutter基礎 – Column/Rowの色々な並べ方 | UKI-HOME~ https://uki-home.xyz/2020/03/30/2052/ *手順5:パッケージの追加と利用 [#wdf201cb] **english_wordsパッケージの例 [#c2f39d2c] ***パッケージの追加 [#l4895405] english_wordsパッケージを追加・利用する。 -パッケージを探す。 --サイトで、english_wordsを探す。~ https://pub.dev/flutter/packages --探したら、~ https://pub.dev/packages/english_words ---評価を確認して、~ https://pub.dev/packages/english_words/score ---インストール方法を確認する。~ https://pub.dev/packages/english_words/install -インストール方法は、 dependencies: flutter: sdk: flutter english_words: ^4.0.0 --その後、ツール・メニューかコマンドから、Pub Getを実行。 --インストールされ、pubspec.lockにも記載が追加される。 ***パッケージの利用 [#l994c979] -インポートした上で import 'package:english_words/english_words.dart'; -ライブラリを利用する([[コチラ>#ab55e378]]のコードを修正)。 class _MyHomePageState extends State<MyHomePage> { String _counter = 'hoge'; void _incrementCounter() { setState(() { // This call to setState tells the Flutter framework that something has // changed in this State, which causes it to rerun the build method below // so that the display can reflect the updated values. If we changed // _counter without calling setState(), then the build method would not be // called again, and so nothing would appear to happen. _counter = WordPair.random().asPascalCase; }); } **データの永続化(shared_preferences) [#k0d2f689] ***パッケージの追加 [#y2248706] shared_preferencesパッケージを追加する必要がある。 -shared_preferences| Flutter Package~ https://pub.dev/packages/shared_preferences/install dependencies: flutter: sdk: flutter shared_preferences: ^2.0.6 ***パッケージの利用 [#j552eb54] import 'package:shared_preferences/shared_preferences.dart'; Future<SharedPreferences> _prefs = SharedPreferences.getInstance(); late Future<int> _counter; Future<void> _incrementCounter() async { final SharedPreferences prefs = await _prefs; final int counter = (prefs.getInt('counter') ?? 0) + 1; setState(() { _counter = prefs.setInt("counter", counter).then((bool success) { return counter; }); }); } @override void initState() { super.initState(); _counter = _prefs.then((SharedPreferences prefs) { return (prefs.getInt('counter') ?? 0); }); } ***参考 [#sefa34d2] -Flutterでshared_preferencesを使う - Qiita~ https://qiita.com/fukuit/items/0f385dde916cd7668c72 **ブラウザ起動(url_launcher) [#y663789f] ***パッケージ追加 [#s4421c50] url_launcher[[パッケージを追加する必要がある。>Flutterのファースト・ステップ#wdf201cb]] url_launcherパッケージを追加する必要がある。 -url_launcher | Flutter Package~ https://pub.dev/packages/url_launcher/install dependencies: flutter: sdk: flutter url_launcher: ^6.0.4 ***パッケージの利用 [#rcd57bc6] import 'package:url_launcher/url_launcher.dart'; _launchURL() async { const url = "http://https://www.google.co.jp/"; if (await canLaunch(url)) { await launch(url); } else { throw 'Could not Launch $url'; } } ***参考 [#l6c52f19] -Flutterで外部ブラウザに遷移する - Qiita~ https://qiita.com/superman9387/items/868ce6ad60b3c177bff1 **WebAPIを呼び出す。 [#k0b7d0c5] ***パッケージ追加 [#ea0f55f3] HTTP[[パッケージを追加する必要がある。>#wdf201cb]] HTTPパッケージを追加する必要がある。 -http | Dart Package~ https://pub.dev/packages/http/install import 'package:http/http.dart'; ***GET [#r32f0dbd] -Example~ https://pub.dev/packages/http/example import 'dart:convert' as convert; import 'package:http/http.dart' as http; // This example uses the Google Books API to search for books about http. // https://developers.google.com/books/docs/overview var url = Uri.https('www.googleapis.com', '/books/v1/volumes', {'q': '{http}'}); // Await the http get response, then decode the json-formatted response. var response = await http.get(url); if (response.statusCode == 200) { var jsonResponse = convert.jsonDecode(response.body) as Map<String, dynamic>; var itemCount = jsonResponse['totalItems']; print('Number of books about http: $itemCount.'); } else { print('Request failed with status: ${response.statusCode}.'); } -作法 --Futureのメソッドチェーン void sendRequest() { http .get(url) .then((response) { print(response); }) .catchError((error) => print(error)); } --async/await Future<void> sendRequest() async { try { final response = await http.get(url); print(response); } on Exception caach (error) { print(error); } } ***POST [#sef9e1b2] -作法 --Futureのメソッドチェーン ... --async/await var response = await http.post( "https://www.example.com/path/to/api", body: { "nickname": "chooyan", "description": "A freelance mobile app developer", }, ); -コンテンツ --application/x-www-form-urlencoded~ bodyに、Map<String, String>型 の変数を指定する。 --application/json ---JSON形式のString型を指定する。 ---json.encode(Map<String, String>型)でもOK。 ***その他 [#vdc68763] -ヘッダ~ headersに、Map<String, String>型 の変数を指定する。 -... ***参考 [#q5bd47cb] -dart - Flutter http headers - Stack Overflow~ https://stackoverflow.com/questions/53299447/flutter-http-headers -Make authenticated requests - Flutter~ https://flutter.dev/docs/cookbook/networking/authenticated-requests -Google Books API からJSONデータを取得してjQueryで処理する - Qiita~ https://dishware.sakura.ne.jp/swift/archives/129 **参考 [#a16079da] -Page 1 | Top Flutter packages~ https://pub.dev/flutter/packages -外部パッケージ | Flutter Doc JP~ https://flutter.ctrnost.com/tutorial/tutorial04/ -【Flutter】パッケージ導入手順 - Qiita~ https://qiita.com/akeome/items/0a6ebf3af402fdf62c79 *[[Flutterのセカンド・ステップ]] [#r39843fc] *[[参考>Flutter#a9ed99c8]] [#i485f5b3]