（前編・文法やビルトインの機能中心）数ヵ月くらいDartとFlutterを触ったので個人の所感と学んだことを全体的ににまとめてみる

お仕事で数か月程度DartとFlutterでのスマホアプリ開発をしていたので触ってみて感じた所感や学んだことなど、追加で調べたことなどを雑多に全体的にまとめておきます。
（個人の復習や知識の漏れの補完・その他チームメンバーへの共有も兼ねて）

※DartやFlutterの基礎であったり、ビルドインやサードパーティのライブラリ含め利用しているものも全体的に触れていきます。

※色々まとめていたら長くなったので前編後編で分けます。本記事では所感やDart周りなどを中心に触れ、後編はFlutterの細かい点も触れていきます（考えなしに書いていたらとても長くなったのでほぼ自分の勉強用の記事となってきています・・・w）。

# 前置き

:::note warn
専任のアプリクライアントエンジニアやフロントエンジニアなどをやってきているわけではないので専門の方々からすると色々知識が荒い点はご了承ください（仕事でサーバーやクラウドなどを触っている時間なども多めです）。

もし専門の方から見て間違いなど気づかれましたらこっそりコメントなどで優し目にマサカリ投げていただけますと幸いです。
:::

# そもそもFlutterって？

- Googleによって開発されたOSSのクロスプラットフォーム向けのエンジン（フレームワーク）です。
  - Windows、iOS、Android、web・・・と様々なプラットフォーム向けのアプリなどを開発することができます。
- 言語は同じくGoogleによって開発されたDartという静的型付け言語を使います。
- 本記事執筆時点でGitHubのスター数が16万を超えており、世界のGitHubのパブリックリポジトリの中で19位くらいに位置しており、大分人気を博している印象です。
  - ※参考 : [Repositories Ranking](https://gitstar-ranking.com/repositories)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1d0053c5-fa71-fb37-a1c5-fd5134824f2d.png)

- ネイティブほどでは無いですが必要十分には感じるくらいの良いパフォーマンスを出してくれます。
  - ※参考 : [Flutter vs React Native vs Native: Deep Performance Comparison](https://inveritasoft.com/blog/flutter-vs-react-native-vs-native-deep-performance-comparison)
- Google製なだけあってマテリアルデザイン関係のウィジェットなどが非常に充実しており、マテリアルデザインなどを採用する場合用意されているものを活用して効率的にアプリを作成することができます。

# 雑な所感

DartとFlutterの全体的な個人の所感について以降の節で触れていきます。

## Dartで書いてみての所感

Dart言語自体はすんなりと書けるようになった（学習コストは低めに感じる）印象です。少なくともRustとかよりも遥かに楽に手に馴染んだ印象です。

雰囲気的にはTypeScriptとかUnityとかでのC#とかSwiftとか型アノテーション付きのPythonとか、もしくは古の時代に滅んだActionsScript3とかの静的型付け言語を触っていた経験のある方ならスムーズに手に馴染みそうな気がしています。

Dart3系になってからはnull安全とかもしっかりしていますし、Rustとかほどきっちりしている・・・という感じでもないですがサクサク書けて且つ必要十分に感じる程度には安全に書ける・・・形でバランスは良さそうに感じます。

世の中的にはDartの好き嫌いは結構分かれていそうな印象ですが個人的には割と気にいってきています。

## Flutterを使ってみての所感

Dartの方はすぐに手に馴染んだのですが割とFlutter側は序盤は苦戦しました。

非常に長いことスマホアプリとかは開発しておらず、長期間サーバーサイドとかのお仕事がメインだったので浦島状態だったためReactなどもキャッチアップしていなかった勢なのでその辺も影響が大きかったのかもしれません（逆に言うとReactとかに慣れている方はすんなりと使えるようになるのかもしれません）。

状態管理や様々なウィジェットとかも色々迷ったりしましたし、マテリアルデザインの知見とかもかなり浅かったといった面もFlutterキャッチアップの大変さに絡んでいたかもしれません。

Dart + Flutter本を1冊消化した時点ではDartは抵抗感は大分無くなっていましたが、Flutter側はそれくらいでは全然分からないことだらけで、その後しばらく実際に無理やり手を動かし続けていたらやっとこ馴染んできた感じがあります。

まだまだ日々新しく知る発見とかも多いですし全然使いこなせている感じはしませんが、ただし人気なだけあって凄い便利と感動することもありますしサードパーティのライブラリなども含めるとウィジェットやアニメーションなどの充実っぷりも凄いなと思うので慣れてきたら結構効率的に作業できそうとは思っています。

一方でifやforとかでネストしていくわけではないので認知的複雑度がやばくなる・・・という感じでもないのですが、一方でchild引数とかでどんどんインデントが深くなる書き方が多くなる・・・というのは最初ちょっと違和感を感じました（慣れてきたら気にならなくなってきました）。

この辺はまあHTMLとかでもぼちぼちインデントする形で書く感じですしまあ慣れの問題か・・・と思っています。

また、用意されているウィジェットの調整が効くパラメーター等は非常に充実していますが、それらのパラメーターで対応できる範囲を超えたカスタマイズとかが要件的に必要になってくると自前で色々書かないといけなくなるのでFlutterの効率的な良さはぐっと減るとは思います。要件的に特殊なUIが多い・・・とかの場合には本当にFlutterが最適なのかなどを考える必要があります。

私は気にならないレベルなのですがアプリサイズなどに関してはネイティブと比べるとある程度増えたり、パフォーマンスに関してもネイティブと比べると若干劣るのは確かです。また、ネイティブで導入された最新機能なども必ずしもすぐにFlutter側で実装されるという感じではなく、ネイティブと比べると利用可能になるまで若干待つ必要などもあります。

その辺よりもクロスプラットフォーム対応による工数削減やホットリロードとかの便利機能の面で私は魅力を感じていますが、一方で要件的にその辺のアプリサイズ削減などが優先といった場合にもFlutterの採用が正解なのか等は考える必要があります。

### Flutterのホットリロードは割と感動した

Flutterを触っていて個人的に一番感動したと言える点がホットリロードです。コードを保存した瞬間にデバッグビルドで起動しているアプリが即時で更新されます。

ちょっとした設定値の変更やレイアウト調整などがエディタ上でCtrl + Sでコードを保存したら即時で反映されます。

特定の画面とかで作業している場合コード変更 -> ビルド -> その画面まで手作業で遷移・・・という対応がぐっと減りますし、その画面を開いたまま何度もコード調整と即時反映を繰り返すことができます（インクリメンタルビルドとかでビルドがさくっと終わるような場合でも画面遷移などが手作業で何度も発生すると地味に時間が取られるのでホットリロードでさくっと調整分などが確認できるのはとても快適に感じます）。

ただ、コードの修正全てを反映できるわけではなくもっと大元から再実行しないといけなくなるケースがあります（メソッドとかに引数を加えた影響で一度そのメソッドを通さないといけないもののホットリロードだけだとその辺を通らないケースなど）。

そういった場合にはホットリスタートと呼ばれるものでデバッグビルドのアプリは起動したまま通常の起動と同じプロセスを通すことができ、こちらもホットリロードのようにごく短時間で処理がされます（ただし画面は起動直後の画面に移ってしまいます）。

ホットリロードとホットリスタートを使うことで通常のビルドをする回数がかなり少なくなったように感じます（VS Codeを再起動した際などには通常のビルドを再度通すのが必要です）。

なお、ホットリロードとホットリスタートはスマホ端末にデバッグビルドでアプリを入れた時にもデスクトップ上でプレビューしている時と同じように使用することができます。こちらもアプリを入れ直してアプリを起動しなおす・・・といったフローを踏まずにコードを保存した瞬間などに即時でスマホ上に反映されます。スマホ上で特有の挙動をするもの等の効率的な確認や調整にとても便利です。

### GitHub Actions上でのFlutterのCI/CDの整備がとてもシンプルで良かった

ゲームなどに絡んだ会社に所属しているのでアプリクライアントに関してはクラウド上のCI/CDの整備とかが結構面倒で（ゲームエンジンとかの感覚だと）インストール時間なども考えるとJenkins上で組むケースが多い・・・という印象だったのですが、Flutterをクラウド版のGitHub Actions上で扱う際には設定がとてもシンプルで動かせるようになるまでもかなり短時間で終わる形でこの点もとても良いなと思いました（クライアント側のCI/CDの整備がもっと大変なのかと思っていました）。

それこそFlutterを設定して依存関係のライブラリを入れてとりあえずFlutterをUbuntuのrunner上で動かせるようにする・・・とかであれば数行程度YAMLを書く程度で完了しますし、FlutterやXcodeなども含めた環境設定からiOSのビルドとDeployGateなどへのアプリのデプロイなどの諸々を含めても50行くらいのYAMLの記述で完了するというのは中々感動しました（GitHub ActionsとFlutter関係無く、iOSのInHouseの証明書設定とかXcode関係とかは久々過ぎて記憶が曖昧で結構苦戦しましたが・・・w）。

自前でCI/CD用の環境のOSや使うもの（Jenkinsなど）のアップデートなども対応しなくてOKになりますし、環境構築用の設定とかも全部YAML上で残る形になりIaC的な対応もできる点（将来見直した際や複数人で作業したりする際などに環境構築に使ったコマンドなどが全部YAMLに残るのは保守面で良いなと思います）、その他サーバーサイドと同様にGitHub Actionsで統一できる点なども良いと思います。

### 国内でのFlutter人材の採用のしやすさは何とも言えない

世界的には盛り上がっていそうですしGitHubのスター数やらサードパーティのライブラリの充実具合などやら大分しっかりしている・・・という印象でいます。

一方で国内だと段々とFlutter人材は増えてきたり日本語の記事とかも増えては来ていそうですが、技術者の総数としてはどうなんだろう・・・？というのは何とも言えない感覚でいます（Flutter人材の採用のしやすさ含め。FlutterというかiOS・Androidアプリエンジニアも足りていないとかもありそうですが・・・）。

参考 :

https://otihateten.hatenablog.com/entry/2023/05/16/233508

（※なんとなくDartを書きたくなく感じている方も一定数いそうな気配も感じています・・・w）

弊社も採用などかけていますがそんなにバンバンと良い人の応募が来る・・・という雰囲気はあまり感じられていません（採用ブランディング的な面はさておき）。

そのため人材の確保的な面ではなんとも言えないという所感を個人的には持っています。

### 個々の要素になると割と情報が少ないことがある

英語圏まで調べれば結構色々な記事が見つかったりするのと、ChatGPTやCopilot Chatとかに聞けば割と解決することも多い・・・ところではありますが、一方で機能やウィジェットの数は膨大だったりするためマイナーなところやアップデートで変わったばかりのタイミングで引っかかったりすると調べものをしても解決策が中々見つからない・・・といったケースは何度か遭遇しています。

また、日本語圏で情報が見つからないことも結構あります。

この辺は用意されているウィジェットの豊富さなどの面でしょうがないところもあると思いますがある程度は覚悟しておく必要はあるかもしれません（他の技術でも似たような面はあるとは思いますしFlutterだけの問題という感じでも無いですが・・・）。

※日本語圏の資料の充実とかは今後の年経過で改善するかもしれません。

### ウィジェットテストなどの機能はとても良くできていると思った

Flutterだと主に単体テスト・ウィジェットテスト・インテグレーションテストと3種類あります。右に行くほど大がかりになって書いたり保守したりするのが大変になったり処理時間も伸びるイメージです。

単体テストは普通に書くとして、インテグレーションテストは結構書くのも保守するのもきつい、でも単体テストだけだとテストでカバーできる範囲的に不安・・・といった時にウィジェットテストは個人的には良い塩梅に思います。結構数を書いてもまあテスト時間も許容できる範囲に思います。

ウィジェットテストでも結構インテグレーションテストに近いこともできるので境界線は少し曖昧な所もありますが・・・。

挙動的にはヘッドレスでSeleniumやPlaywrightとかを動かすのに近いイメージで、特定のウィジェットを対象としてテストすることもできますしある程度複数のウィジェットが配置されているものに対してテストを行うこともできます（特定のページとかにも実施できます）。

Seleniumとかにもfind_element_by_idとかの関数を始めとして様々な要素検索の機能がありますが、Flutterのウィジェットテストにも特定のウィジェットの検索用の仕組みが色々用意されています。タップ操作やテキスト入力などもできますし、個々のウィジェットの座標を取ったりアニメーションが完了するまで待機する制御だったりとテストをしやすくするための便利機能がたくさん用意されています。

ただしウィジェットテストはエミュレーター上とかで実施するインテグレーションテストと比べると実際のビルドされたアプリとのズレが気になることもあります。そのため手間がかかっても厳密にテストしたい機能とか要件に関しては必要に応じてインテグレーションテストも混ぜるとかも良い選択肢のケースもあると思います。

### フォーマッタやLintのチェックなどに関して

フォーマッタやLintチェックなどに関しては公式が提供してくれているもので満足できています。スタイルガイドとなるEffective Dartにも結構沿う形に整形してくれますし、書き方が好ましくない時に警告とかでしっかり教えてくれるのは助かっています。

公式で完成度の高いものをしっかりと提供してくれているのは迷わなくて良いなと思います。

# 本記事で使うもの

以降の節では実際にFlutter環境を整えたり各機能について触れて行こうと思います。その際には以下のものを使っていきます。

※自宅のPCがWinなのでWin前提で書きますがMacなどの方はインストール周りは別の記事などをご参照ください。

- OS: Windows 11 (デスクトップ環境)
- Flutter: v3.19.1（2024-02-22リリース）
- Dart: v3.3.0
- VS Code
  - Flutter公式の拡張機能含む

# 環境設定

## FlutterのダウンロードとVS Codeでの設定

※VS Codeに関しては既にインストール済みの前提で進めます（VS Code自体のインストールなどは本記事では割愛します）。

※必要要件とかは以下のドキュメントのSystem requirementsとかをご参照ください。

https://docs.flutter.dev/get-started/install/windows/mobile

まずはVS CodeでFlutter公式の拡張機能をインストールします。VS Codeの拡張機能のUIでFlutterと検索して青バッジの付いている公式のものをインストールします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b6656122-94a4-d6ff-f939-52e4d9b8da5e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c5bfdc85-a7c9-c8de-ebee-4690b36787d7.png)

続いてFlutter SDKを以下のページの「flutter_windows_x.xx.xx-stable.zip」ボタンを押してダウンロードします。zipファイルは1GBくらいあるのでそれなりにダウンロードに時間がかかります。

https://docs.flutter.dev/get-started/install/windows/mobile?tab=download

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/8db5b65c-7b92-dbaa-2126-7a16e67c180f.png)

zipの展開先は任意ですがそれなりにディスク容量が必要になるので余裕のあるディスクドライブとかを選択しておきます。今回は雑に`D:\workspace\`というフォルダに展開しました。flutterというフォルダ名で展開されるので私の環境の場合`D:\workspace\flutter\`というパスにFlutter SDKが展開されています。

※特殊文字やスペースが含まれるパスは避けるように、とドキュメントに書かれているのでその辺りは避けておきます。

今度はVS Code上でCtrl + Shift + Pを押してコマンドパレットを開き、flutterと検索します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/14a43bda-565c-aac2-c4b5-3b787b526b53.png)

検索結果に表示される「flutter: New Project」を選択します。恐らくVS CodeからSDKが見つからないと以下のようなエラーが右下に表示されると思います。SDKのダウンロード自体は完了しているのでLocate SDKボタンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d5ffc916-9d67-2fd9-b1bd-cb7097dc5b5d.png)

Flutter SDKが含まれているフォルダを選択するためのダイアログが開かれるためzipを展開したフォルダ（私の場合は`D:\workspace\flutter\`）を選択して「Set Flutter SDK folder」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/82421aef-6e37-32bd-5ad7-910e83ea18e4.png)

## Flutterのパス設定を行う

Flutterコマンドを使えるようにするためFlutterのバイナリが配置されているパスを環境変数のPATHへと追加していきます。

対象のパスは展開したFlutter SDK直下のbinフォルダのパスが該当します。私の場合は`D:\workspace\flutter\bin\`となります。これをPATHへ追加します。

Windowsの検索窓で「環境変数」などと検索すると「システムの環境変数の編集」という検索結果が表示されるのでクリックします。

システムのプロパティというダイアログが表示されるので右下の方にある「環境変数(N)」というボタンを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0715fe83-e307-7a7f-dbce-b8e5c4f00e03.png)

「<ユーザー名>のユーザー環境変数」という方で変数がPATHとなっている行を選択して編集ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/483a237c-1e47-b029-e51c-e13fdbc79c8f.png)

「新規」ボタンをクリックするとパスの入力ができるのでFlutter SDK内のbinフォルダのパス（私の場合`D:\workspace\flutter\bin\`）を入力してOKをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/48360798-5e04-8aee-6c86-157b2c07d5c9.png)

これで新しく開いたPowerShellなどでFlutterのコマンドが実行できるようになります。試しにPowerShellやコマンドプロンプトなどを新しく開いて`$ flutter --help`とコマンドを打つと色々メッセージが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d9c12d64-5f53-4a8a-6111-eabd44b28a54.png)

## 環境設定が正しくできているかを確認してくれるflutter doctorコマンドを活用していく

Flutterには環境設定が正しくできているかを確認してくれる`$ flutter doctor`というコマンドが存在します。

実行してみると各設定が出来ているか出来ていないかを表示してくれます。対応が足りていないものなどはどのような対応が必要なのかの説明なども表示してくれます。

以下のコマンド結果のスクショではまだAndroid関係の整備をしていないのでAndroid Studio周りなどで引っかかっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f6ce2a2b-057b-5c38-7a71-57c594cb6954.png)

※同僚の方にご共有いただいたのですが、もしflutter doctorのコマンドを最初に実行してみても結果が返ってこない・・・といった場合には再起動したらちゃんと帰ってくるようになったそうです。

## Android Studioなどの対応

flutter doctorコマンドでAndroid Studioなどの箇所が引っかかったのでその辺を対応していきます。

まずはAndroid Studioをインストールしていきます。以下のページからインストーラーのダウンロードボタンをクリックします。ダウンロードファイルは約1GBくらいあります。

https://developer.android.com/studio?hl=ja

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b6104916-5c08-6ffc-5538-ab670859bdf6.png)

ダウンロードが終わったらインストーラーを起動してインストールしていきます。特に理由が無ければ設定そのままでインストールを進めて問題ないと思います。

インストールされたAndroid Studioを起動します。最初は設定をインポートするかが問われますが、Android Studioのインストールが今回使う環境では初なので「Do not import settings」を選択してインポートせずに進めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/881fb079-9d20-f701-dc2b-2d29811e22f1.png)

その後はログ送信を行うかどうかなどのダイアログが出ますがその辺は各々の判断で良いと思います（今回は送信しない設定で進めました）。Nextボタンを押したりしているとインストールタイプの選択画面となりますがStandardを今回は選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b1f29c50-28f8-297c-6298-d353dde81941.png)

あとは利用規約などに同意していればインストールインストールが進むのでしばらく待ちます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6122e904-c76c-fa30-d164-d01156124f87.png)

インストールが終わると以下のような画面になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/49477e6c-ebb6-d554-bc3f-8e66265d5d42.png)

左のメニューの「Plugins」を選択し、検索窓でFlutterと入力してFlutterのプラグインを検索しInstallボタンでインストールを行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7527efb1-72ba-91be-5034-d9febe35c0dc.png)

インストール時に警告が出ますが特に問題ないのでそのまま進めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4118598a-912f-dbcd-17ed-d7aea3b4d6b3.png)

Dart関係もインストールが必要と言われるのでそちらもInstallボタンをクリックしてインストールします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ac380d9d-f86a-cfbf-045a-9b5d9e7adf57.png)

インストール後「Restart IDE」というボタンが表示されるので一応押してAndroid Studioを再起動しておきます（この辺はVS Codeを基本的に使っていくので要らないかもしれません）。

### Android SDK Command-line Toolsのインストール

続いてAndroid SDK Command-line Tools周りの対応をしていきます。Android StudioのProjectsのメニューの画面の下の方にある「More Actions」メニューをクリックして「SDK Manager」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/df1bdd69-674c-6e93-daa1-8e93463a60f0.png)

設定画面が開かれます。左のメニューでAndroid SDKの部分が選択されている状態でSDK Toolsのタブを選択します。Android SDK Command-line Toolesという項目でチェックが外れているのでチェックを入れて右下のApplyをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/49cad8f9-6b5d-c637-410a-1c1ab2da3ce5.png)

この時点で再度`$ flutter doctor`コマンドを実行してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/80ff4a5b-df8e-25d1-0648-409b621b18a1.png)

赤文字のエラーは無くなりました。ただ1か所以下のようにAndroidのライセンス関係を承諾する必要がある旨の警告が出ています。

```
[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    ! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses
```

### Androidのライセンスの同意を行う

flutter doctorの警告で表示されていく通り、`$ flutter doctor --android-licenses`というコマンドを実行することでライセンスの同意を行うことができます。

コマンドを実行するとライセンス内容が表示されるので確認して問題がなければyを入力してEnterを押していけば同意作業が進んでいきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b38e5b2f-2f21-f418-350b-6a3e62ac8938.png)

最後まで同意が終わったら再度flutter doctorコマンドを実行してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/8444195a-b59a-ae06-cdf4-33a4c3def643.png)

全てのチェックを通過し、「• No issues found!」と表示されたことを確認できました。これで必要な基本的なインストール周りなどは対応が完了となります。

## その他環境によってはインストールが必要になるかもしれないものなど

私の場合昔に入れたと思われるVisual Studio関係が最初からチェックを通過していましたが人によってはこの辺もインストールが必要になってくると思われます。その場合はflutter doctorコマンド上に表示されるメッセージもしくはネット上の記事などを参照してご対応ください。

# プロジェクトの作成

新規のFlutterプロジェクトを作成するには`$ flutter create`コマンドやVS Code上での操作などいくつかの方法があります。

普段はコマンドで作成していたのですが本記事ではお試しでVS Codeを使う形でプロジェクトを作成してみます。

VS Code上でCtrl + Shift + Pを押してコマンドパレットを開き、flutterと入力してflutter関係のコマンドを検索します。`Flutter: New Project`というメニューが表示されるのでそちらを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b2d330f7-fc7b-b684-30ff-1cab5e80eecb.png)

いくつか候補が出てきます。どのみち生成される初期コードとかを編集していけば問題ないのでApplication、Empty Application、Skeleton Applicationどれでも問題なさそうですが今回は一番上のApplicationを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/457c02eb-e10a-8af3-0d23-8c4e1bee87c3.png)

プロジェクトを生成するフォルダを選択するためのダイアログが表示されるので任意のフォルダを選択します。今回は`D:\workspace`フォルダとしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1f595c92-9982-de7b-bffd-b7406d4fc710.png)

コマンドパレットの位置にプロジェクト名の入力用のフォームが表示されます。今回は雑に`test_flutter_project`と設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/cbbec2cc-7315-9e28-cf71-70d04c22e8c9.png)

名前を入力してEnterを押すとVS Codeが再起動しつつFlutterの作成したプロジェクトがワークスペースに設定された状態で起動します。
※必要に応じて切り替わる前のVS Codeのワークスペース設定の保存などはご対応ください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b145b170-4501-0737-db21-c7e9542d6df3.png)

生成されたmain.dartファイルが開かれた状態となりますが、このファイルのmain関数部分がこのプロジェクトのエントリーポイントとなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c078204f-b259-ab75-9a92-0f83da47f63b.png)

# デバッグビルドでアプリの起動を行う

VS Code上からのFlutterのデバッグビルドなどによるアプリ起動はサイドメニューのRun and Debugメニューから行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e1e332bb-0663-af0a-8e6e-98e4d9da3d06.png)

対象のメニューを選択すると以下のスクショのようなUIが表示されます。青いRun and Debugボタンをクリックするとデスクトップアプリのデバッグビルドとしてアプリのビルドがスタートします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a509a032-66a6-de5c-5e6e-ffba2812c4d0.png)

※Flutterのアプリ起動自体はマシンスペックにもよりますが結構時間がかかります。ただし一度起動してしまえば後は再度最初からビルドしなくともホットリロードやホットリスタートでコード変更分を反映できるのでぐっとビルド回数は少なくて済むため起動した後はビルド時間は気にならなくなります。

ビルドが終わると以下のスクショのようにサンプルプロジェクトのアプリが起動します。内容としては右下のボタンを押すと真ん中のカウントが1ずつ加算されていくだけのシンプルな内容になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a071d2e1-2d48-f490-d385-14085a94b817.png)

また、起動時にプロファイラー関係について開くかどうかなどの確認がVS Codeの右下に表示されたと思います。ChromeのDevToolsのようにVS Code上でFlutter用に様々なツールが用意されています。この辺りについては後々の節で詳しく触れます。

# 外部ライブラリパッケージについて

プロジェクトをVS Codeやコマンドなどで作成した場合、プロジェクトフォルダの直下に`pubspec.yaml`というファイルが作成されていると思います。このファイルはプロジェクトの設定を管理するためのファイルで、アプリ名やFlutterなどのバージョン、その他外部のライブラリパッケージの設定や画像などの埋め込むアセットの定義を行うことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b348d1e7-9cc0-92ca-7475-6f962fe96608.png)

以降の節では外部ライブラリパッケージについて色々触れていきます。

## pub.devについて

外部ライブラリパッケージに関してはDartではpub.devというサイトで情報が公開されており、様々なライブラリの情報を確認することができます。Pythonで言うところのPyPIのようなサイトになります。

https://pub.dev/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5effea86-864c-b195-8ee9-890b44164963.png)

サンプルとしてflutter_svgというライブラリで説明をしていきます。

https://pub.dev/packages/flutter_svg

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f484a94a-4aca-7325-5b25-bd93b6ca9879.png)

まずページ上部について見てみます。上部にはパッケージ名と現在公開されている最新バージョンの文字列が表示されます（スクショの2.0.10+1の部分が最新バージョンの文字列になります。）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c5d8195a-30d0-5c06-063e-de4f34a14da0.png)

また、「Published 8 days ago」といった部分は最後にパッケージのアップデートが公開された日が何日前かといった表示になります。この表示がかなり前のパッケージに関しては保守がされなくなりアップデートが止まっている可能性が高いので使用を検討する際には注意が必要になってきます。

「Dart 3 compatible」というチップの表示もありますが、これはDart3系に対応している場合に表示されます。利用の多いライブラリパッケージに関してはほぼほぼ付いている気もしますが付いていない場合には最近は（少なくとも新規のプロジェクトでは）Dart3系を利用されているプロジェクトが大半だと思いますのでそのライブラリパッケージは互換性的に利用できない可能性が高くなります。また、Dart3系になってから結構期間が経っているのでまだDart3系のサポートがされていない場合、そのライブラリパッケージはメンテナンスされているかの面で使用は警戒した方が無難かもしれません。

以下のスクショのように、Versionsタブを表示すると各バージョンとリリース日が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/020e8de2-7f19-6146-22d9-d2c6a33ec83a.png)

各バージョンのリリース日を確認することで、どのくらいの頻度でバージョンアップがされているのかを確認できます。各バージョン間でかなり期間が空いているかどうかもそのライブラリの利用を決めるかどうかの指標の一つとして使えるかもしれません。

右の方にあるLIKESやPOPULARITYは対象のライブラリパッケージがどのくらい多くのユーザーに使われている人気のパッケージなのかの判断基準の一つとして使うことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/3cfdf3ce-e6ca-bda0-4ca7-7cd1586e37af.png)

人気のパッケージであればLIKESがたくさん付いて、POPULATIONも100％付近になっています。人気パッケージでもアップデートが日々されて保守されているとは必ずしも限りませんが、指標の一部として個人的にはLIKESが数百、POPULATIONが90％強だと割と使用を考えてもいいのでは・・・という感覚でいます（他の指標なども確認はしたりしますが）。

## pubspec.yamlへの外部ライブラリパッケージの記述方法

pubspec.yamlの内容を更新して外部ライブラリパッケージのプロジェクトへの追加を試してみます。

外部ライブラリパッケージを指定する箇所はpubspec.yamlの中でdependenciesとdev_dependenciesの2つがあります。

本記事の手順でプロジェクトを作成した場合dependenciesのセクションとdev_dependenciesのセクションの初期設定は以下のようになっています。

```yaml
dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.6
```

```yaml
dev_dependencies:
  flutter_test:
    sdk: flutter

  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^3.0.0
```

dependenciesとdev_dependenciesで何が違うのか？というところですが、dev_dependencies側で設定したものは開発環境（ローカルなど）でのみ反映されます。一方でアプリストアなどで配布するためのリリースビルドなどをした際にはdev_dependenciesで指定したものはビルド結果に含まれなくなります。

そのためdev_dependenciesの方にはリリースビルドなどで含まなくても問題ないもの（使わないもの）を指定していきます。例えばテスト用やLint用などのライブラリパッケージが該当します（リリースビルドでは単体テストやLintなどを基本的に動かさないため）。これによってリリースビルドで使わないパッケージのコードの分が含まれなくなりビルド後のアプリサイズで無駄が減るようになっています。

リリースビルドなどにも含めたいものに関してはdependenciesの方で指定します（通常のウィジェットライブラリなど）。

記法に関しては`<ライブラリパッケージ名>: <バージョン>`といった形でコロンで繋げて記述します。例えば`flutter_svg`パッケージでバージョンが`2.0.10`の場合には`flutter_svg: 2.0.10`といった記述になります。

バージョンに`^`の記号が先頭に付いていた場合には最低バージョンの指定となり、インストールがされていない場合その最低バージョン以降で可能な最新バージョンがインストールされます。たとえば`flutter_lints: ^3.0.0`となっていれば3.0.0以降のバージョンのflutter_lintsパッケージがインストールされる・・・という挙動をします。

試しに`flutter_svg`パッケージで`^2.0.0`というバージョンをpubspec.yamlのdependenciesセクションに追加してみましょう（本記事執筆時点で`flutter_svg`の最新バージョンは`2.0.10+1`）。

```yaml:pubspec.yaml
dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.6

  flutter_svg: ^2.0.0
```

前節までで触れたFlutterのVS Codeの拡張子がインストールされていればVS Code上でCtrl + Sを押してpubspec.yamlを保存するとライブラリパッケージの同期が開始されます。VS Codeの右下に同期中を示す表示が以下のスクショのように表示されます。表示されている`flutter pub get`という記述はライブラリパッケージの同期を行うためのコマンドとなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ca54d22c-670f-d3db-c50c-d35cb91e10e2.png)

また、VS CodeのOUTPUTタブには同期処理のコマンドの出力が表示されます（このUIが表示されていない場合にはCtrl + @などで表示することができます）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d871e87c-e742-eefb-3023-28e2332595d4.png)

先頭に`+`記号が付いているものが追加されたライブラリパッケージとなります。`flutter_svg`も含まれており、且つバージョンが最新の`2.0.10+1`がインストールされたことが確認できました。

## 追加した外部ライブラリパッケージの削除方法

追加したライブラリパッケージが不要になった時などに削除するには単純にpubspec.yamlから記述を削除してVS Code上でCtrl + Sで保存するだけです。

試しに`flutter_svg`パッケージの記述を削除して保存してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6a435a89-f61a-8565-3deb-358df75ec8d7.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6a435a89-f61a-8565-3deb-358df75ec8d7.png)

少し待つと「These packages are no longer being depended on:」というメッセージと共に不要になったライブラリパッケージが一覧表示されます。

また、対象のプロジェクトのリポジトリフォルダでTERMINALもしくはPowerShellなどから`$ flutter pub deps`というコマンドを打つことでも現在インストールされているパッケージを一覧表示することができ、一覧の中に`flutter_svg`が含まれなくなっていることが確認てきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/853e9843-64d7-d61d-35dd-44c4aa9aae33.png)

# VS Codeのプロファイラーなどについて

前節までで少し擦れましたが、VS CodeのプロファイラーはChromeのDevToolsのように色々用意されています（実務だとこの辺りまだしっかり使いこなせていません・・・）。

初回のビルド時に開くかどうかの確認ダイアログが右下に表示されたり、他にもコマンドパレットからも開くことができます。

デスクトップ上でデバッグビルドを起動した状態でコマンドパレットをCtrl + Shift + Pで開きます（起動していない場合は起動時にプロファイラー側で接続されるようになります）。

コマンドパレットで`flutter devtools`などと検索すると対象のものがヒットするので選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0e570bf1-c826-9e87-3dd1-4576adf368c0.png)

DevToolsの中でも色々ありますがまずはWidget Inspector Pageを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f3191f7a-ebf6-3f29-45a7-982cd05511ed.png)

するとChromeのDevToolsの要素タブのようなUIがVS Code上で表示されます。Flutterではウィジェットをchild引数で結構入れ子にしたりして記述していきますが、その親子関係だったりウィジェットのサイズなどを確認することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/3f70c052-82b6-d265-608c-9c3d6979f113.png)

親子関係を辿っていって特定のウィジェットを選択してWidget Details Treeのタブを選択したりすると対象ウィジェットの各属性の詳細とかも確認することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/37c47501-0379-6250-e41b-370f58571b26.png)

ChromeのDevToolsだと要素をクリックして選択する機能がありますが、FlutterのこのDevToolsでも似たような機能があります。

DevToolsの左上の方にあるSelect Widget Modeというボタンを押して有効化すると、それをウィジェットをクリックしてデバッグビルドのアプリ上でウィジェットが選択できるようになり、且つ選択しているウィジェットのサイズなどの情報が表示されるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1bb7c13c-c0bd-9fd9-016d-13cd3babc187.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/cce8a63d-7a0c-136e-0d4c-bb1fb0149970.png)

※一度クリックした後に別のウィジェットをクリックしたい場合には左下の虫メガネのアイコンが乗っているボタンをクリックすると別のウィジェットを選択できるようになるようです。

## ウィジェットの領域をガイドライン表示する

DevToolsの右上の方に|↔|といった表示のアイコンがありますが、これを選択すると各ウィジェットごとにサイズ領域のガイドラインを表示してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d0ded89e-629a-dbea-ec5e-37c4fba67998.png)

この機能で境界線とかの無い透明なコンテナとかでどのくらいの領域が設定されているのか等の可視化を行うことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f9f9d82f-a91e-7a96-4478-9213c8d6d04a.png)

## その他のDevToolsについて

まだ使いこなせていないというのもあり本記事ではDevToolsに関しては深くは触れないため軽く紹介する程度に留めておきますが、Widget InspectorのDevTools以外にも様々なDevToolsがVS Code上で用意されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/745a50af-8866-50d0-cb78-4ac1a8484561.png)

例えばNetwork Pageでは外部のAPIへのリクエストや外部アセットの読み込みなどのURLや結果のステータスコード、レスポンスのサイズの確認などを行うことができます。

# Dartの文法

以降の節ではDartの文法について色々復習しながら触れていきます。

## Dartの実行環境について

Dartの文法を学んでいくにあたってVS Code上でFlutterのプロジェクトを使って色々動かしていく・・・というのもいいのですが、本記事ではDartの文法について触れていく際には気軽に試せるようにDartPadというブラウザ上でインストール対応など無しにDartを書いて動かすことの出来るサービスを使っていきます（※Flutterを動かす時にはVS Codeの方を使っていきます）。

https://dartpad.dev/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/46249d61-a1c2-9024-9503-7156015b7885.png)

DartPadの代わりにVS Code上で動かして・・・とかでも（若干の調整とかは調べていただく必要は出てくるかもしれませんが）大丈夫かと思いますので利用されるものはお任せします。

DartPadではコードを書いてRunボタンを押すと、少し待っているとコンソール出力の結果が右側に表示されます（スクショのhello 1といった各表示がコンソール出力になります）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/796e35dd-f8c2-571d-d045-ece3ad20ab95.png)

DartPadはお手軽な実行環境としてとても便利ですが、一方で入力補完とかドキュメンテーションコメントの表示とかはVS Codeとかの方が便利だったり、あとは本記事執筆時点ではランタイムエラーが一括で`Script error.`と表示されてエラー詳細が表示されない・・・といった面もあるので一長一短な感じではあります（ランタイムエラーの調査とかはDartPadだと中々辛いものがあります）。

## Dartのエントリーポイントの書き方

DartPad上の初期コードが以下のようになっているため、まずはエントリーポイントから説明していきます。

```dart
void main() {
  for (int i = 0; i < 10; i++) {
    print('hello ${i + 1}');
  }
}
```

Dartではエントリーポイント（プログラムの開始地点のとなる最初の関数）はmainという関数で扱います。Flutterアプリのエントリーポイントでもファイルことのテストコードでもエントリーポイントはそれぞれmainとなります（後々の節でも触れますがテスト側でもエントリーポイントが必要になります）。

また、Dartでは関数やメソッド定義時には`<返却値の型> 関数名(<引数内容>) {<関数の処理>}`といったように書きます。前述のコードで指定されている`void`は返却値を返さない関数の返却値の型となります。

エントリーポイントは引数を必要としないため`main()`の括弧の中の引数内容は空になっています。

また、関数の処理のスコープを表すにはjsなどと同じように`{}`の括弧を使います。

## コンソール出力

Dartでコンソールになんらかの値を出力したい時にはprint関数を使います。引数に出力したい値や変数などを指定します。

たとえば以下のように書くとコンソールに100が表示されます。

```dart
void main() {
  print(100);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fc94183b-7a85-5ac6-1a9a-23d878b1e228.png)

※後々の節で詳しく触れますが、関数を呼び出すには`<関数名>(引数の指定);`といったように書きます。また、関数だけではないのですが処理の最後にはセミコロンが必要になります。

前述のコードでは100という引数を指定しているので`print(100);`という記述になっています。

また、DartではインデントはHTMLとかで良くあるように半角スペース2個が基本となります（公式のフォーマッタが半角スペース2個になっています）。

なお、VS Code上だったり公式のLintでチェックしたりするとprint関数が残っていると警告で引っかかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c2ed0aed-aede-c8f2-0252-a20a11c0b3bc.png)

あくまでprint関数は一時的な確認用などのために使用して、不要になったら削除するのが基本的な運用方針となります。

永続的にコンソール出力を開発中に表示したい場合は別途ロギングライブラリを使います。その辺りを使うとリリースビルドの際にはロギングの処理が無効化されて無駄が無くなったり見やすくなったりといったメリットがあります。

ロギング関係のライブラリパッケージに関して後々の節で触れていきます。

## 変数宣言

Dartで変数を宣言する場合には型を明示する方法と`var`を使って型推論で定義する方法の2つがあります。

型を明示する場合には`<型名> <変数名>;`といった形で書きます（例 : `int myIntValue;`）。変数宣言と同時に初期値を与える場合にはイコールで繋いで値を指定します（例 : `int myIntValue = 200;`）。

```dart
void main() {
  int myIntValue = 200;
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/82c6c7a1-4772-9e3b-2c71-ae60972f56d8.png)

`var`を使って型推論を使う場合には`var <変数名> = <初期値>;`といった形で設定します。型推論で判定する性質上基本的には初期値を与える形で定義します（例 : `var myIntValue = 300;`）。

```dart
void main() {
  var myIntValue = 300;
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/65e1ced2-03bc-8bf6-e3bb-9a79d68d0c6e.png)

型推論を使う形で初期値を与えない場合は警告が出ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/cb9453a7-32c0-2fc3-46ff-6b37c715a3d0.png)

初期値を与えない形で変数宣言したい場合には`var`ではなく型を明示する形で変数を宣言します。

※この辺りのルールなどはコーディングスタイルガイドの資料などが書かれているEffective Dartなどで触れられていたり、公式のLintチェックなどでもチェックすることができます（Flutterの公式のVS Codeの拡張機能を入れていればVS Code上でもリアルタイムに表示されます）。

https://dart.dev/effective-dart

以前まとめた記事 :

https://qiita.com/simonritchie/items/daa7c58778660696417e

## 定数宣言

Dartには定数が`const`で定義するものと`final`を使って定義するものの2つがあります。

書き方としては`<constもしくはfinalキーワード> <型の指定> <変数名> = <設定する値>;`という形で記述します（例 : `const String userName = 'myName';`）。

値の変更が効かない点を除けば扱いは変数と似た感じになります。

```dart
void main() {
  const String userName = 'myName';
  print(userName);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/dfd0c33c-f1ca-c17b-7efd-8a8f325d8d3a.png)

型推論を使って定義したい場合には`const`や`final`などのキーワードは残しつつ型の指定を省略すれば型推論を使いつつ定数を定義することができます。

```dart
void main() {
  const userName = 'myName';
  print(userName);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2e2cf004-3175-28aa-543d-db1e5cac3183.png)

定数なので当たり前ですが再代入とかは効きません。コンパイルエラーとなります。

```dart
void main() {
  const userName = 'myName';
  userName = 'yourName';
  print(userName);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/9886653b-5b97-b409-0e65-50f0dabd9282.png)

`const`と`final`で何が違うのか？という点ですが、`const`の方はコンパイル時に既に既知の値である必要があります。つまり`const`の初期値は`10`や`'Hello'`といった直接的な値もしくは他の`const`の定数になっている必要があります。他の変数を設定したり、もしくは関数の返却値などを`const`の値に設定することはできません。

例えば以下のように別の変数が`const`の値として設定されているとコンパイルエラーになります。

```dart
void main() {
  var userName = 'myName';
  const otherName = userName;
  print(otherName);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/9c0f4c81-fbb6-61af-6ec8-8946dc9d2f9f.png)

関数の返却値が分岐などなく固定値を返すといった場合でも関数を経由する場合はその値は`const`として定義することはできません。

```dart
void main() {
  const userName = getUserName();
  print(userName);
}

String getUserName() {
  return 'myName';
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/31d9504a-7f34-4182-5119-efb280a55459.png)

一方で`const`で設定する値が別の`const`の値であればコンパイルが通ります。

```dart
void main() {
  const userName = 'myName';
  const otherName = userName;
  print(otherName);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/aabddac5-e824-52e0-2fa5-5e3a2dd4cd3b.png)

`final`の方は`const`とは異なり、設定される値は1回まで計算されてその後は変更されない定数値として扱われます。つまり定数の初期値には変数を設定したり関数の返却値を設定してもコンパイルが通ります。

```dart
void main() {
  final userName = getUserName();
  print(userName);
}

String getUserName() {
  return 'myName';
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b521e712-84eb-45da-0e7a-a17182489ba0.png)

定義した後は`const`と同じように再代入しようとずるとコンパイルエラーとなります。

```dart
void main() {
  final userName = getUserName();
  userName = 'otherName';
  print(userName);
}

String getUserName() {
  return 'myName';
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/dae175e9-f112-107a-e7a2-935dd9b9216c.png)

慣れてくるまでは`const`と`final`どちらを使うのか一瞬迷ってしまう・・・といったケースも出てきそうですが、その辺りはVS Code上でFlutterの拡張機能を入れていれば`const`として定義すべきケースなのに`final`で定義している場合には警告が表示されます。その辺りをエディタで設定しておいたり、後はCI/CDとかで自動チェックするようにしておけば引っかかったら直すくらいの感覚で大丈夫だと思います。

```dart
void main() {
  final userName = 'myName';
  print(userName);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6cd9de86-d4a0-2868-0278-6a02d7bc7302.png)

注意点として、（少なくとも本記事執筆時点では）リスト（配列）などを扱う場合、`final`で定義していても要素の追加などの更新処理が通ってしまいます（割と初見の時この挙動は違和感が強かったですがJavaとかに慣れている方は恐らく違和感が少ない・・・仕様ですかね・・・？）。

```dart
void main() {
  final List<int> myList = [100, 200];
  myList.add(300);
  print(myList);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6b03d240-27d6-a96c-d441-840545e99d19.png)

`const`を使う場合にはこういったケースはコンパイルが通らなくなります（`const`の方が厳密な定数といった感じになります）。そのためチェック時の警告を抜きにしても基本的には`const`を使えるケースでは`const`を使う方が好ましいといった形になります。

### lateを使った定数の遅延設定

`final`を使った定数定義では別途`late`を使って値の設定を定数定義よりも遅らせることができます。

例えば以下のように定数定義と値の設定の行をずらしたりすることができます。

```dart
void main() {
  late final int myIntFinalValue;
  myIntFinalValue = 20;
  print(myIntFinalValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ff947960-ab22-19d4-de5c-cef30db408fe.png)

何に使うのか？という感じですが、Flutterで扱う際にクラスの属性などを`late final`で定義しておいて、初期化用のメソッドなど（例 : `initState`メソッドなど）で初期値を与えたりなど、定義と値の設定タイミングをずらしたい一方でなるべく定数として扱いたい・・・といった場合に便利です（初期化を確実に1回だけにしたいといった場合などに）。

なお、`late final`で定数を定義した場合、定数定義と値の設定タイミングがずれる都合定数定義時点で型を明示しないと警告が出るようになります（`late`を付けない場合には警告が出ません）。

```dart:lateを使っている一方で型を明示していないため警告が出る例
void main() {
  late final myIntFinalValue;
  myIntFinalValue = 20;
  print(myIntFinalValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ba733fdc-6db9-32fa-a430-7b10c88e68d8.png)

また、通常の`final`定義時と同じように複数回値を設定しようとずるとエラーになります。

```dart
void main() {
  late final int myIntFinalValue;
  myIntFinalValue = 20;
  myIntFinalValue = 30;
  print(myIntFinalValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0439ef8f-7f9a-faad-b8b6-eecc07c6dded.png)

## 型について

以降の節では型の基本について触れていきます。

### 型の基本的な指定方法

変数や定数、引数などへの型の指定はそれぞれの名前の左にスペースを空けて設定します（例 : `int myIntValue;`）。

変数であれば型の指定だけ行っておいて値の設定は後で行う・・・といった書き方も行うことができます（if文で分岐するケースなど）。

```dart
void main() {
  int myIntValue;
  bool isTarget = true;
  if (isTarget) {
    myIntValue = 100;
  } else {
    myIntValue = 200;
  }
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/48842ed6-841d-9b2e-48c9-62ba8bdc290e.png)

### ジェネリックの型の指定方法

ジェネリックな型、例えば文字列の値のみを格納するリスト（配列）といった型を定義するには`型名<ジェネリックの型>`といった形で書きます（例 : `List<String>`）。

ジェネリックの型の指定を使うことで「整数のみを格納するリストだったのにうっかり文字列を入れてしまった」とか「辞書のキーは文字列だけの想定だったのにうっかり整数のキーが紛れ込んでしまった」みたいなことを弾くことができ堅牢にコーディングを行うことができます。

また、特定の型だけでなく様々な型に対応できるといった使い方もできるので実装の重複を減らしたり利用時の柔軟性を上げたりといったメリットもあります。

リスト（配列）で値の型を指定するには`List<値の型>`と書きます。

```dart
void main() {
  List<int> myIds = [10, 20, 30];
  print(myIds);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4f0c0571-397d-540b-ea1f-5b3534306e7e.png)

試しに`List<int>`と指定したリストの変数に文字列を含めてみるとコンパイルエラーになることを確認できます。

```dart
void main() {
  List<int> myIds = [10, 'Hello', 30];
  print(myIds);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/55b3493e-4933-0c86-04fd-d4b61dd7a64b.png)


マップ（辞書）でキーと値の型を指定するには`Map<キーの型, 値の型>`と書きます。

```dart
void main() {
  Map<String, bool> myMap = {
    'key1': true,
    'key2': false,
  };
  print(myMap);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c6ca578a-7407-851f-ff92-c59d69cbeb5e.png)

試しにキーに不正な型を指定してみてエラーになることを確認してみます。

```dart
void main() {
  Map<String, bool> myMap = {
    10: true,
    'key2': false,
  };
  print(myMap);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/526fd96f-e535-de8a-fe02-cf7188c113bf.png)

同様に値に対して不正な型を指定してみてエラーになることを確認してみます。

```dart
void main() {
  Map<String, bool> myMap = {
    'key1': true,
    'key2': 20,
  };
  print(myMap);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fd8cb202-7483-7b45-0352-84c87c75be56.png)

### 整数型

整数の型は`int`となります。`500`や`-1000`といった値を設定することができます。

```dart
void main() {
  int myIntValue1 = 500;
  print(myIntValue1);

  int myIntValue2 = -1000;
  print(myIntValue2);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/81b971e1-6c23-8bf5-0e22-aa8e7733d1a5.png)

加算処理は`+`の記号や`+=`の記号で行うことができます。

```dart
void main() {
  int myIntValue = 500;
  myIntValue = myIntValue + 300;
  print(myIntValue);

  myIntValue += 200;
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/746ce6b8-5826-54d8-d6f2-dfee253d71d8.png)

1だけ加算したい場合には`++`の記号を使うことで対応することができます。

```dart:++記号で1だけ加算する例
void main() {
  int myIntValue = 500;
  myIntValue++;
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a5c9bd75-2c7d-df94-8693-1497f2de6f60.png)

減算処理は`-`の記号や`-=`の記号で行うことができます。

```dart
void main() {
  int myIntValue = 1000;
  myIntValue = myIntValue - 300;
  print(myIntValue);

  myIntValue -= 200;
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ba3b4471-fa18-1d05-7d28-7f0cf7c6ba02.png)

1だけ減算したい場合には`--`の記号を使うことで対応することができます。

```dart:--記号で1だけ減算する例
void main() {
  int myIntValue = 500;
  myIntValue--;
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c3fe2b3d-0d57-d1ca-284a-d0255c7b4a56.png)

除算処理は`/`の記号や`/=`の記号で行うことができます。ただし結果の値は浮動小数点数（`double`型）となるため、結果は他の変数に格納するなどの調整が必要になります（`double`型については後の節で触れます）。

```dart
void main() {
  int myIntValue = 10;
  double myDoubleValue = myIntValue / 4;
  print(myDoubleValue);

  myDoubleValue /= 5;
  print(myDoubleValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fb9ee4b6-241d-522d-d280-e04821c14921.png)

切り捨て除算は`~/`の記号や`~/=`の記号で行うことができます。Pythonとかに慣れ親しんだ身からすると`//`の方に慣れていますが`~/`となります。

```dart
void main() {
  int myIntValue1 = 10;
  myIntValue1 = myIntValue1 ~/ 4;
  print(myIntValue1);

  int myIntValue2 = 10;
  myIntValue2 ~/= 4;
  print(myIntValue2);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/87b45e64-f94f-4dc0-d421-41ad112f4e91.png)

剰余計算は`%`の記号や`%=`の記号で行うことができます。

```dart
void main() {
  int myIntValue = 10;
  int remainder = myIntValue % 4;
  print(remainder);

  myIntValue %= 3;
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/030c6e61-4fb7-0edd-0328-fea8c9e5c861.png)

なお、Dartの`int`型の値の上限は64bitもしくは環境に応じてそれ以下の値となります。たとえばWebターゲットで書きだしてjsに変換された場合には64bitよりも小さな値までしか扱うことができません。

> 注意：JavaScriptに変換されたDartコードの場合は、整数は倍精度浮動小数点値で表現されうる値に制限される。従って使える整数は -2^53 と 2^53 の範囲及びより大きな値の一部の整数（これには一部の2^63より大きな数が含まれる）である。従ってintクラスの演算子とメソッドの振る舞いはDart VMとJavaScriptにコンパイルされたDartコードとでは差が生じることがある。

https://www.cresc.co.jp/tech/java/Google_Dart2/language/built-in_types/built-in_types.html

それよりも大きな整数を扱いたい時のために`BigInt`という型がDartにはあるようです（何となくMySQLとかの感覚で`BigInt`と聞くと64bitな印象を受けますがDartではもっと大きな値まで扱えます）。

使い方としては初期化などするには`BigInt.from`メソッドを使います。

```dart
void main() {
  BigInt myBigIntValue = BigInt.from(1000);
  print(myBigIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/3c46e893-3f02-d110-c61f-95285f4d8185.png)

大きな値を使いたい場合、そのまま`int`の値を設定する感覚で設定しようとすると`int`の上限値を超えているということでエラーになります。

```dart
void main() {
  BigInt myBigIntValue = BigInt.from(123456789012345678901234567890);
  print(myBigIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e37cc90f-9a9f-a445-f115-b229195a16ec.png)

大きな数値を使いたい場合には指数表記などを使います。

```dart
void main() {
  BigInt myBigIntValue = BigInt.from(1e+30);
  print(myBigIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/739d9785-655b-4b27-55c7-714a224b9b48.png)

もしくは文字列に対してparseメソッドを使う形でも対応ができます。

```dart
void main() {
  BigInt myBigIntValue = BigInt.parse('123456789012345678901234567890');
  print(myBigIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ee260a01-bca1-1939-05b8-0b0ec48ccfc4.png)

とはいえ、Flutterがアプリ開発用となっているのと各ライブラリパッケージなどでも通常の`int`型を使うのが大半でしょうから基本的には`int`型で扱う形となります（扱いが煩雑だったり制限が多い点やパフォーマンス面などを加味しても使う機会はあまりないかなという印象です）。

#### int型のisEven属性

※他の型でもそうですが、全てではありませんが使うことがありそうな属性やメソッドなどに関しても各節で触れていこうと思います。

`int`型の`isEven`属性は整数値が偶数かどうかの真偽値の属性となります。

```dart
void main() {
  int myIntValue = 10;
  print(myIntValue.isEven);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7fc02468-25c8-8d0b-2679-fb6f320606f8.png)

#### int型のisOdd属性

`int`型の`isOdd`属性は整数値が奇数かどうかの真偽値の属性となります。

```dart
void main() {
  int myIntValue = 10;
  print(myIntValue.isOdd);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b6ba200c-d24e-e360-2595-037317aa2198.png)

#### int型のisNegative属性

`int`型のisNegative属性は整数が負の値かどうかの真偽値の属性となります。負の値で荒れは`true`になります。

```dart
void main() {
  int myIntValue = -10;
  print(myIntValue.isNegative);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/20ce2a94-3af7-7b22-d2d1-f28582373f00.png)

#### int型のabsメソッド

`int`型の`abs`メソッドでは整数の絶対値を取得します。引数は必要としません。

```dart
void main() {
  int myIntValue = -10;
  print(myIntValue.abs());
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/bbb3b987-c9d6-4bbb-f949-dfef6b7e640e.png)

※このメソッドは後々の節で触れる浮動小数点数の`double`型などにも存在します。

#### int型のtoDoubleメソッド

`int`型の`toDouble`メソッドでは整数の値を浮動小数点数の`double`型に変換した値を返します。

```dart
void main() {
  int myIntValue = 10;
  double myDoubleValue = myIntValue.toDouble();
  myDoubleValue += 0.5;
  print(myDoubleValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d9b95060-fefc-2ad0-14fe-1ebdb172101c.png)

#### int型のtoStringメソッド

`int`型の`toString`メソッドでは整数の値を文字列の`String`型に変換した値を返します。

```dart
void main() {
  int myIntValue = 1000;
  String myStringValue = myIntValue.toString();
  myStringValue += '円';
  print(myStringValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/49b71a52-2448-726d-2370-6919f8ae6a26.png)

#### int型のparseメソッド

`int`型自体が持っているstaticメソッドとして`parse`メソッドがあります（後の節で触れる`double`型など他の型でも存在しています）。

このメソッドでは引数に指定された文字列を整数に変換します。

```dart
void main() {
  String intFormatString = '12345';
  int myIntValue = int.parse(intFormatString);
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6fcbd862-642b-89c5-4874-b73c236bf6d4.png)

整数に変換できない値を指定するとエラーになります。0になったりとかはしません。

```dart
void main() {
  String myStringValue = 'abcdef';
  int myIntValue = int.parse(myStringValue);
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/371ec473-e2d6-4f9c-c4aa-dbb133d846d9.png)

浮動小数点数の形式の文字列だった場合などでもエラーとなります。

```dart
void main() {
  String doubleFormatString = '1.234';
  int myIntValue = int.parse(doubleFormatString);
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/dbeabb83-0a70-995d-13d8-92602fd43219.png)

### 浮動小数点数の型

浮動小数点数の型は`double`となります（64bitの値となります）。基本的な使い方は`int`と大体一緒です。

```dart
void main() {
  double myDoubleValue = 20.5;
  myDoubleValue += 10.2;
  print(myDoubleValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/02342565-fc4e-9887-23e9-b20afcfb95b5.png)

#### double型のceilメソッドとceilToDoubleメソッド

`double`型の`ceil`メソッドは小数点以下を切り上げます。例えば元の値が1.1であれば結果は2となり、元の値が1.0であれば結果は1となります。

また、返却値は`double`型ではなく`int`型になります。

```dart
void main() {
  double myDoubleValue = 1.1;
  int myIntValue = myDoubleValue.ceil();
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/88e8d2e2-884e-31bc-a6f1-771177890961.png)

似たようなメソッドとして`ceilToDouble`というメソッドも存在します。これも挙動は`ceil`とほぼ同じなのですが結果の型が`int`ではなく`double`となります（2とか1といった値の`double`になります）。元の変数をそのまま使いたい場合などに便利です。

```dart
void main() {
  double myDoubleValue = 1.1;
  myDoubleValue = myDoubleValue.ceilToDouble();
  print(myDoubleValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c2fbdbe8-4a6c-409a-f853-3649be93df2a.png)

#### double型のfloorメソッドとfloorToDoubleメソッド

`ceil`と同じような形で`double`型には`floor`と`floorToDouble`というメソッドがあります。

こちらは小数点以下を切り捨てます。例えば1.1や1.9という値であれば結果の値は1となります。`floor`と`floorToDouble`の違いは`ceil`と同じように結果の値の型が`int`になるか`double`になるかの違いだけです。

```dart
void main() {
  double myDoubleValue = 1.1;
  int myIntValue = myDoubleValue.floor();
  print(myIntValue);

  myDoubleValue = 1.9;
  myDoubleValue = myDoubleValue.floorToDouble();
  print(myDoubleValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/cf88d810-0c06-d507-8954-d605a14d2241.png)

#### double型のroundメソッドとroundToDoubleメソッド

`double`型の`round`メソッドは四捨五入の挙動をします（偶数丸めかと思ったら四捨五入の挙動をするようです）。`round`と`roundToDouble`の違いは他のメソッドと同様に結果が`int`型になるか`double`型になるかだけです。

```dart
void main() {
  double myDoubleValue = 1.4;
  print(myDoubleValue.round());

  myDoubleValue = 1.5;
  print(myDoubleValue.round());

  myDoubleValue = 1.6;
  print(myDoubleValue.round());

  myDoubleValue = 2.5;
  print(myDoubleValue.round());
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/13ed11e1-3237-5870-bce0-dd5ecdc6e0c0.png)

#### double型のtoStringAsFixedメソッド

`double`型の`toStringAsFixed`メソッドでは特定の小数点の桁数で四捨五入をすることができます。引数に対象の小数点数の桁数を指定します（引数に2を指定すれば3桁目が四捨五入されて2桁目までが残るようになります）。また、結果の値は文字列になります。

`round`メソッドは特定の桁数での四捨五入とかではなく整数になる形での小数点以下の四捨五入となるため、特定の桁数で四捨五入したい場合などに便利です。

ただし、結果の値を`double`型などで扱いたい場合には結果が文字列になっているので一旦`parse`メソッドで変換する必要があります。

```dart
void main() {
  double myDoubleValue = 1.245;
  myDoubleValue = double.parse(myDoubleValue.toStringAsFixed(2));
  print(myDoubleValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/eef297de-eaa9-41cd-69b1-4e5cbd2de63f.png)

### 文字列の型

文字列の型は`String`となります。文字列の値に関してはシングルクォーテーション（`''`）もしくはダブルクォーテーション（`""`）で囲んで扱います。どちらでも動きますが理由がなければフォーマッタとかでどちらかに統一しておくといいかもしれません。

```dart
void main() {
  String myStringValue = "Hello";
  print(myStringValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/00f2392a-c934-4164-e0be-eb228db37eee.png)

文字列の連結は`+`や`+=`の記号で行えます。

```dart
void main() {
  String myStringValue = "Hello";
  myStringValue += ' World!';
  print(myStringValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/022080e8-d7f4-0fb4-db35-01e54029467a.png)

また、良く使う文字列の機能として文字列中で直前に`$`記号を設定することで変数を文字列の中で展開することができます（Pythonのf-stringsのような機能）。文字列中に変数の値を含めたい場合に文字列を連結して書くよりも記述がシンプルになります。

```dart
void main() {
  String myStringValue = "太郎";
  print('私の名前は$myStringValueです。');
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ab8ae1d3-20f2-8565-3d2b-d6d3ba24e1a1.png)

`${対象の変数}`といったように`{}`の括弧で囲むと変数の属性やメソッドにアクセスしたり呼び出したりしても動きます。

```dart
void main() {
  final myString = 'abcdef';
  print('文字列を大文字に変換すると${myString.toUpperCase()}になります。');
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/156d1835-96a9-8767-f0c8-0c5848f1def9.png)

この機能は文字列以外の変数も含めることができます。例えば以下のように整数の変数を文字列中に含めたりすることができます。

```dart
void main() {
  int age = 17;
  print('年齢は$age歳です。');
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/3c70cd87-71b7-5a64-04db-2673faef041c.png)

#### String型のisEmpty属性とisNotEmpty属性

`String`型の`isEmpty`属性は文字列が空文字列かどうかの真偽値となります。もし文字列が空であればtrueとなります。逆に`isNotEmpty`属性は空でなければtrueとなります。

```dart
void main() {
  String myStringValue = '';
  print(myStringValue.isEmpty);
  print(myStringValue.isNotEmpty);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a69bd470-7063-0a6c-cca4-8df72d69e673.png)

#### String型のlength属性

`String`型の`length`属性は文字列の文字数の整数となります。

```dart
void main() {
  String myStringValue = 'Hello';
  print(myStringValue.length);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7f35c3f7-3369-3031-64bb-5ce6d32f1dce.png)

日本語なども想定通りに文字数をカウントしてくれます。

```dart
void main() {
  String myStringValue = 'あいうえお安以宇衣於';
  print(myStringValue.length);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/949b9c0c-25d0-0c5a-b9c6-00dfa0fac84e.png)

ただし特殊な漢字や絵文字などの特殊文字は正確に取れないようです（この辺は他の言語でも想定通りの値が取れたり取れなかったりなのでDartだけの問題ではありませんが要注意な感じではあります）。

```dart
void main() {
  String myStringValue = '👀🎉🙇‍♂️';
  print(myStringValue.length);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/44d7ed1c-7c53-81da-0a32-c6928a2064d8.png)

#### String型のcontainsメソッド

`String`型の`contains`メソッドでは引数に指定された文字列を含んでいるかどうかの真偽値を取得することができます。含んでいれば`true`となります。

```dart
void main() {
  String myStringValue = 'Hello World!';
  print(myStringValue.contains('lo'));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/80c1111f-adde-4e39-d14c-9960c99b6f85.png)

#### String型のstartsWithメソッドとendsWithメソッド

`String`型の`startsWith`メソッドでは文字列が引数で指定した特定の文字列でスタートしているかの真偽値を取得することができます。スタートしていれば`true`となります。

```dart
void main() {
  String myStringValue = 'Hello World!';
  print(myStringValue.startsWith('He'));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7c8d1429-6ef3-c778-6be5-bf49a251e055.png)

逆に`endsWith`メソッドでは文字列の最後が引数で指定した文字列で終了しているかの真偽値を取得することができます。終了していれば`true`となります。

```dart
void main() {
  String myStringValue = 'Hello World!';
  print(myStringValue.endsWith('ld!'));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b2d3b82e-9518-6d66-53e5-3d1a182d0f8d.png)

#### String型のindexOfメソッドとlastIndexOfメソッド

`String`型の`indexOf`メソッドでは文字列中で引数に指定した文字列がどの位置に最初に出現するかどうかのインデックスの整数を取得することができます。複数該当する文字列がある場合は最初の位置となります。また、最初の文字がインデックス0となります。

```dart
void main() {
  String myStringValue = 'Hello World!';
  print(myStringValue.indexOf('l'));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/8ecd118a-aef4-8ed4-0ad4-fa190a4b1945.png)

`lastIndexOf`メソッドの方は文字列の右端から該当する文字列を検索します。ただし結果のインデックスは左端からカウントされます（右側からカウントされたりはしません）。

```dart
void main() {
  String myStringValue = 'Hello World!';
  print(myStringValue.lastIndexOf('l'));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/88868120-99ae-c4a0-0170-08ca850b94f5.png)

#### String型のpadLeftメソッドとpadRightメソッド

`String`型の`padLeft`メソッドは文字列の左端を特定の文字で引数で指定した文字数になるまで埋めます。第一引数に最終的な文字数、第二引数に埋める文字を指定します。第二引数は省略可で、省略した場合は半角スペースで埋められます。

```dart
void main() {
  String myStringValue = 'Hello';
  print(myStringValue.padLeft(20));
}
```

※スクショでは分かりづらいですが合計で20文字になるまで左端に半角スペースが追加になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/15f57a56-8389-005e-8a1a-46b728d17b0e.png)

第二引数に文字を指定すると半角スペースの代わりにその文字で文字列の左端が埋められます。

```dart
void main() {
  String myStringValue = 'Hello';
  print(myStringValue.padLeft(20, 'A'));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/38765109-a6d2-8b07-0f8e-ccedc2a5ce69.png)

`padRight`メソッドは`padLeft`メソッドとほぼ使い方は変わりませんが、文字を埋める位置が左端ではなく右端となります。

```dart
void main() {
  String myStringValue = 'Hello';
  print(myStringValue.padRight(20, 'A'));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/01a4ac5c-135a-0a00-cc5f-d37ddda19097.png)

#### String型のreplaceAllメソッド

`String`型の`replaceAll`メソッドは任意の文字列を別の文字列で全て置換した文字列を返却します。第一引数に置換したい文字列、第二引数に置換後の文字列を指定します。

以下の例では小文字の`l`を大文字の`L`で置換しています。

```dart
void main() {
  String myStringValue = 'Hello World!';
  print(myStringValue.replaceAll('l', 'L'));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1f413eb7-a429-e18a-d892-a0a7c9f1350b.png)

#### String型のsplitメソッド

`String`型の`split`メソッドでは文字列を引数で指定された文字で分割されたリスト（配列）を返却します。例えばコンマ区切りで分割したりスペース区切りで分割したりといった具合です。

```dart
void main() {
  String myStringValue = 'AA BBB C DD EEE';
  List<String> splittedList = myStringValue.split(' ');
  print(splittedList);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7bbb9174-2c3d-3243-40ad-20bb62bdde0b.png)

#### String型のsubstringメソッド

`String`型の`substring`メソッドは文字列の特定のインデックス範囲の文字列を返却します（文字列の一部を抽出することができます）。

第一引数には抽出範囲の開始インデックス（最初の文字のインデックスは0となります）、第二引数には抽出範囲の終了インデックス（このインデックス自体は含みません）を指定して使います（つまり第一引数のインデックス～第二引数のインデックス - 1の範囲の各文字が対象となります）。

第二引数は省略可で、その場合は文字列の最後までが抽出対象となります。

以下の例では第一引数に2を指定指定しているのでインデックス2（3文字目）～最後の文字列までが抽出対象となります。

```dart
void main() {
  String myStringValue = 'HelloWorld!';
  print(myStringValue.substring(2));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4bf4a2db-3da7-908a-b910-88be40cb0731.png)

以下の例では第一引数に2、第二引数に5を指定しているので2～4のインデックス範囲が抽出対象となります。

```dart
void main() {
  String myStringValue = 'HelloWorld!';
  print(myStringValue.substring(2, 5));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4b4047d9-6397-f998-68a3-8374e53e2017.png)

#### String型のtoLowerCaseメソッドとtoUpperCaseメソッド

`String`型の`toLowerCase`メソッドは文字列を小文字に変換した値を返却します。

```dart
void main() {
  String myStringValue = 'HELLO WORLD!';
  print(myStringValue.toLowerCase());
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c8e692b2-755f-d30f-4e3b-f0fa91209def.png)

逆に`toUpperCase`メソッドでは大文字に変換した文字列のあたぽを返却します。

```dart
void main() {
  String myStringValue = 'hello world!';
  print(myStringValue.toUpperCase());
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b1432840-500e-f794-e380-6f28c44d9e23.png)

#### String型のtrimメソッド、trimLeftメソッド、trimRightメソッド

`String`型の`trim`メソッドは文字列の左右の両端の空白文字（スペースや改行など）を取り除いた文字列を返却します。

```dart
void main() {
  String myStringValue = '  \n  Hello world!   ';
  print(myStringValue.trim());
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f7beb78f-f375-4add-f6ed-5f30c3a29e99.png)

`trimLeft`メソッドは`trim`メソッドと似た挙動をしますが文字列の左端だけ空白文字が取り除かれます。

```dart
void main() {
  String myStringValue = '  \n  Hello world! 　 \n\n     ';
  print(myStringValue.trimLeft());
}
```

※以下のスクショは分かりづらいですが、出力の文字列を選択してみるとスペースなどが残っていることが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/85abc036-6679-af1d-ef4a-20b607a09355.png)

逆に`trimRight`メソッドは文字列の右端からのみ空白文字が取り除かれます。

```dart
void main() {
  String myStringValue = '  \n\n  Hello world! 　 \n\n     ';
  print(myStringValue.trimRight());
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/25966a4d-d241-237e-c70a-00376190c496.png)

### 真偽値の型

真偽値の型は`bool`となります。値は`true`もしくは`false`のどちらかとなります。

```dart
void main() {
  bool isCompleted = true;
  print(isCompleted);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b20cfa90-47c6-5205-f10c-c8c8e1385664.png)

真偽値の値の直前に`!`記号を配置すると逆に値になります（`true`であれば`false`に、`false`であれば`true`になります）。

```dart
void main() {
  bool isCompleted = true;
  print(!isCompleted);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/879e78de-5a98-cecf-b816-8e23b11f3505.png)

### リスト（配列）の型

リスト（配列）は`List`型を使います。また、値を設定する際には`[]`の括弧を使いコンマ区切りで複数の値を指定します。

※ループ用のfor文などに関しては後々の節で触れます。

```dart
void main() {
  List myList = [10, 20, 'Hello'];
  for (final value in myList) {
    print(value);
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/cb68d503-fcc6-6870-c24c-2f6418e0ee3e.png)

前節まででも触れましたがリストの要素の型を固定したい場合には`<対象の型>`といった指定を`List`の後に記述します。

```dart:整数のリストとして明示するために<int>という型の指定をしている例
void main() {
  List<int> myList = [10, 20, 30];
  print(myList);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/33a65925-1e3d-eded-4e3a-de60d246b1e6.png)

※以降の節で`List`型の各属性やメソッドについて触れていきますがDartはかなりその辺多いな・・・！と思いました。充実していると思ったり中々初見の時「これ何のメソッドなんだろう・・・？」といったものも割とあって調べていて面白かったです。

#### List型のlength属性

`List`型の`length`属性はリスト内の要素の件数となります。3件要素が格納されていれば3となります。

```dart
void main() {
  List<int> myListValue = [10, 20, 30];
  print(myListValue.length);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/92f41aa9-b7e1-2306-2684-e1c93e329c81.png)

#### List型のfirst属性とlast属性

`List`型の`first`属性はリストの先頭の要素となります。

```dart
void main() {
  List<int> myListValue = [10, 20, 30];
  print(myListValue.first);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/cfc5acf9-a697-6932-5f68-84ebb77a77e6.png)

`last`属性では逆にリストの最後の要素となります。

```dart
void main() {
  List<int> myListValue = [10, 20, 30];
  print(myListValue.last);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e6efaae6-2639-5a21-000f-956584325df6.png)

対象が空のリストの場合には`first`属性・`last`属性共にエラーとなります。

```dart
void main() {
  List<int> myListValue = [];
  print(myListValue.first);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/dda36bd2-b2ec-26fc-4baa-e653c4230558.png)

#### List型のfirstOrNull属性とlastOrNull属性

`List`型の`firstOrNull`属性と`lastOrNull`属性は`first`と`last`属性のようにリストの先頭の要素もしくは最後の要素となります。違いとしては`first`属性などと異なりリストが空の場合でもランタイムエラーにならず、代わりに`null`となります。

```dart:先頭の要素を参照している例
void main() {
  List<int> myListValue = [10, 20, 30];
  print(myListValue.firstOrNull);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f33e6e71-4630-0411-d921-cf6424407daa.png)

```dart:最後の要素を参照している例
void main() {
  List<int> myListValue = [10, 20, 30];
  print(myListValue.lastOrNull);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/8802b3b4-4c5e-9476-c8da-ae63737a7fe3.png)

```dart:空のリストではエラーではなくnullとなっている例
void main() {
  List<int> myListValue = [];
  print(myListValue.firstOrNull);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c1595f60-17ad-a8bf-8d5b-e171caa45a5f.png)

#### List型のsingle属性とsingleOrNull属性

`List`型の`single`属性はリストの要素の件数が1件になっていることを確認し、もし1件になっていればその要素となり、もし1件ではない場合はエラーとなります。

```dart:要素が1件のみのリストの場合の例
void main() {
  List<int> myListValue = [100];
  print(myListValue.single);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4273ae48-73f4-8937-9631-296102549972.png)

```dart:要素の件数が1件ではないためエラーになる例
void main() {
  List<int> myListValue = [100, 200, 300];
  print(myListValue.single);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f53d0923-f190-8f29-e768-dc1a06a4b825.png)

#### List型のisEmpty属性とisNotEmpty属性

`List`型の`isEmpty`属性はリストが空かどうかの真偽値となります。空であれば`true`、そうでなければ`false`となります。

```dart:空の場合の例
void main() {
  List<int> myListValue = [];
  print(myListValue.isEmpty);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/bff138ae-d2ef-93af-2202-3f85b64132a8.png)

```dart:空ではない場合の例
void main() {
  List<int> myListValue = [100, 200];
  print(myListValue.isEmpty);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7a3420d5-b951-ed06-7fbc-0eea66a12757.png)

逆に`isNotEmpty`属性では空では無ければ`true`となります。`!`記号での否定とかを使うよりもこちらを使った方がぱっと見で分かりやすいかもしれません。

```dart
void main() {
  List<int> myListValue = [100, 200];
  print(myListValue.isNotEmpty);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7dfeb655-493a-b432-5c6f-aab88d4d895e.png)

#### List型のindexed属性

`List`型の`indexed`属性はリストのインデックスとリストの要素の組み合わせの`Iterable`型の値となります。

これを使うことでループ中にインデックスと要素の両方に同時にアクセスすることができます。

※`Iterable`型やループについては後々の節で触れます。

ループと一緒に使うと以下のような記述になります。

```dart
void main() {
  List<String> myListValue = ['A', 'B', 'C'];
  for (final (index, value) in myListValue.indexed) {
    print('index: $index, value: $value');
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0d8b6477-c72c-43de-dcb7-0d4d23df7e66.png)

#### Iterable型についての補足

`List`型の範疇から少し逸脱しますが`List`関係の操作で良く`Index`型が出てくるので軽く触れておきます。

`Iterable`型は`List`型のように複数の要素を順番に格納する型となります。ただし利用の際にはfor文によるループや後々触れる`map`や`where`などのメソッドと一緒に使う形になります。リストのように添え字用の括弧（`[]`）を使って要素にアクセスすることは出来ずエラーとなります。

```dart
void main() {
  List<String> myListValue = ['A', 'B', 'C'];
  print(myListValue.indexed[0]);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/88b02832-f2d3-67c7-5e2a-88bcc1dc1509.png)

##### Iterable型からList型に戻したい場合

`List`型の操作で`Iterable`型の値となった場合に再び`List`型に変換したい場合のために`toList`というメソッドが用意されているためそちらを使うと`List`の型で再び扱うことができます。

```dart
void main() {
  List<String> myListValue = ['A', 'B', 'C'];
  List indexedListValue = myListValue.indexed.toList();
  print(indexedListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/29491db9-fa40-0f2d-8adc-af0906243a06.png)

#### List型のreversed属性

`List`型の`reversed`属性はリストの各要素を逆順にした`Iterable`型の値となります。

```dart:逆順でC,B,Aという順番で出力される例
void main() {
  List<String> myListValue = ['A', 'B', 'C'];
  for (final value in myListValue.reversed) {
    print(value);
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1a6d5980-aa20-541e-85db-a4ca5148ff80.png)

#### List型のnonNulls属性

`List`型の`nonNulls`属性はリストの要素内で`null`ではない要素のみに絞った`Iterable`型の値となります。

※`null`に関しては後々の節で触れます。以下のコード例で出てくる`?`記号も`null`関係となります。

```dart:null以外の要素が出力される例
void main() {
  List<String?> myListValue = ['A', null, 'B', null, 'C'];
  for (final value in myListValue.nonNulls) {
    print(value);
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/232b9257-93b8-5ed5-115b-c5e6990de295.png)

#### List型のaddメソッドとaddAllメソッド

`List`型の`add`メソッドはリストに新たな要素を追加します。引数に追加したい要素を指定します。追加される位置はリストの末尾となります。

```dart:末尾にDという文字列をaddメソッドで追加する例
void main() {
  List<String?> myListValue = ['A', 'B', 'C'];
  myListValue.add('D');
  for (final value in myListValue.nonNulls) {
    print(value);
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0f74c368-2f0f-c0ce-22b2-e6165b8b66de.png)

また、`addAll`メソッドでは引数に別のリスト（もしくは他の`Iterable`型の値）を追加することで複数の要素を一気に追加することができます。

```dart:末尾にD,E,Fの3要素を一括でリストに追加する例
void main() {
  List<String?> myListValue = ['A', 'B', 'C'];
  myListValue.addAll(['D', 'E', 'F']);
  for (final value in myListValue.nonNulls) {
    print(value);
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b42f7065-c468-a921-ce57-05896197ef73.png)

#### List型のinsertメソッドとinsertAllメソッド

`List`型の`insert`メソッドは`add`メソッドのようにリストに要素を追加します。ただし`add`メソッドと異なり要素を追加する位置を引数で指定することができます。

第一引数に要素を追加したい位置のインデックス（先頭に追加したい場合には0、その次は1...といった値になります）、第二引数に追加したい要素を指定します。

```dart:インデックス2の位置に200という要素を追加している例
void main() {
  List<int> myListValue = [100, 100, 100, 100];
  myListValue.insert(2, 200);
  print(myListValue);
}
```

また、`insertAll`メソッドでは指定するインデックスの位置に複数の要素をまとめて追加することができます。第二引数は別のリスト（もしくは他の`Iterable`型の値）を指定します。

```dart:インデックス2の位置に200,300,400という複数の要素を一気に追加している例
void main() {
  List<int> myListValue = [100, 100, 100, 100];
  myListValue.insertAll(2, [200, 300, 400]);
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f1cb2335-5a1d-20e3-45d2-fdd59e014e59.png)

#### List型のremoveメソッド

`List`型の`remove`メソッドは引数で指定された値をリスト内から検索し、見つかった要素を1件リストから取り除きます。該当の要素が全て取り除かれるわけでは無く、一致した先頭の要素が取り除かれます。

また、返却値は要素が取り除かれたかどうかの真偽値となります。もし該当する要素がなければ`false`が返却されます。

```dart:先頭の200の要素が1件取り除かれ、返却値もtrueとなる例
void main() {
  List<int> myListValue = [100, 200, 200, 100];
  bool isRemoved = myListValue.remove(200);
  print(myListValue);
  print(isRemoved);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e9a8dd81-4d5c-a6cb-db8d-cba7155afe19.png)

```dart:取り除く対象が存在しないためリストの要素は変動せず返却値がfalseとなる例
void main() {
  List<int> myListValue = [100, 200, 200, 100];
  bool isRemoved = myListValue.remove(300);
  print(myListValue);
  print(isRemoved);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f6315b7d-691b-a690-dabc-982b77e320b3.png)

#### List型のremoveAtメソッド

`List`型の`removeAt`関数は第一引数に指定したインデックス位置の要素をリストから取り除きます。返却値は取り除いた値となります。

```dart:インデックス1の位置の要素200を取り除く例
void main() {
  List<int> myListValue = [100, 200, 300];
  int removedValue = myListValue.removeAt(1);
  print(myListValue);
  print(removedValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0c1478ef-556b-0bf7-a470-743d04227002.png)

指定したインデックスの位置に要素が無い場合にはランタイムエラーとなります。

```dart:存在しないインデックス5の位置を指定してエラーになる例
void main() {
  List<int> myListValue = [100, 200, 300];
  int removedValue = myListValue.removeAt(5);
  print(myListValue);
  print(removedValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e0fdb232-5c60-7b1c-f030-d350cf820b9a.png)

#### List型のremoveLastメソッド

`List`型の`removeLast`メソッドはリストの最後の要素を取り除きます。引数は必要とせず、返却値は取り除かれた値となります。

```dart:最後の要素300を取り除いている例
void main() {
  List<int> myListValue = [100, 200, 300];
  int removedValue = myListValue.removeLast();
  print(myListValue);
  print(removedValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f371d1e8-072f-22bd-8f0b-f442997bf594.png)

もしリストが空の場合にはランタイムエラーになります。

```dart:対象が空のリストなのでエラーになる例
void main() {
  List<int> myListValue = [];
  int removedValue = myListValue.removeLast();
  print(myListValue);
  print(removedValue);
}
```

#### List型のremoveRangeメソッド

`List`型の`removeRange`メソッドはリストから指定した範囲の要素を取り除きます。第一引数は取り除く範囲の開始インデックス、第二引数は取り除く範囲の終了インデックス（ただしこのインデックスは含まず、第二引数 - 1の範囲が取り除く対象となります）となります。例えば第一引数に2、第二引数に4を指定した場合にはインデックスが2～3の範囲の2要素が取り除かれます。

```dart:インデックス2～3の範囲（300と400）の要素を取り除く例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  myListValue.removeRange(2, 4);
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2319cd2d-9aa8-5f8e-08f7-7b5ad6e1006c.png)

指定したインデックス範囲がリストの範囲外になっているとランタイムエラーとなります。

```dart:リストの最後のインデックスが4の一方で除外範囲を5のインデックスにまで指定していてエラーになる例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  myListValue.removeRange(2, 6);
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/27f01519-2911-76a7-b56e-2908cc44093c.png)

#### List型のremoveWhereメソッド

`List`型の`removeWhere`メソッドでは条件を満たした要素をリストから取り除きます。引数には判定用の関数を指定します。判定用の関数には引数に要素、返却値に真偽値を設定する必要があります。

条件を満たす要素が複数ある場合には一通り取り除かれます。

```dart:値が300以上の要素を一通り取り除く例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  myListValue.removeWhere((element) => element >= 300);
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fe513463-8b86-21d6-a30d-95c78f1db559.png)

#### List型のretainWhereメソッド

`List`型の`retainWhere`メソッドは`removeWhere`メソッドの判定用の関数が逆の動作をします。つまり判定用の関数で条件を満たした要素をリストに残す（他は取り除く）挙動になります。

引数は`removeWhere`メソッドと同様に要素を引数に受け取って真偽値を返す判定用の関数となります。

```dart:値が300以上の要素のみを残す例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  myListValue.retainWhere((element) => element >= 300);
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/cf19c7be-e9e9-3e87-0a35-816916f5c2d7.png)

#### List型のclearメソッド

`List`型の`clear`メソッドではリストを空にします（要素をまったく含んでいない状態にします）。

```dart:clearメソッドでリストの要素が空になる例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  myListValue.clear();
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1752fb47-fb11-1a6c-d583-8fc397cdae25.png)

#### List型のelementAtメソッドとelementAtOrNullメソッド

`List`型の`elementAt`メソッドは引数で指定されたインデックス位置の要素を返却します。他のメソッドなどと同様にインデックスは0からスタートします。

```dart:インデックスが2の位置の要素を取得する例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  print(myListValue.elementAt(2));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/29cbc8ab-ab17-6f13-70ed-4c8265de8dbd.png)

リストの範囲外のインデックスを引数に指定した場合にはランタイムエラーとなります。

```dart:リストの最後の要素のインデックスが4の一方で5のインデックスを指定したためエラーになる例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  print(myListValue.elementAt(5));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/07b17952-9839-f320-4c43-e09247e13100.png)


`elementAtOrNull`メソッドは`elementAt`メソッドと似た挙動をしますが、もし引数に指定したインデックス範囲がリストの範囲を超えている場合でもエラーにはならずに代わりに`null`を返却します。

```dart:インデックスがリストの範囲内なのでelementAtと同じ挙動をする例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  print(myListValue.elementAtOrNull(4));
}
```

```dart:インデックスがリストの範囲外なのでnullが返却される例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  print(myListValue.elementAtOrNull(5));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2d13437c-f8b6-b1a4-148f-a42d23592119.png)

#### List型のwhereメソッド

`List`型の`where`メソッドは引数の関数で条件を満たした`Iterable`型の値を返却します。各要素を参照して判定を行い、条件を満たせば返却値に含まれるようになるといった挙動になります。

引数の関数の第一引数には対象の要素が渡され、返却値には真偽値を設定する必要があります（`true`を返却した要素が結果の`Iterable`内に含まれる形になります）。

`retainWhere`と似たような挙動となりますが、`retainWhere`の方はリスト自体の内容が変更されるのに対してこちらは`Iterable`型の値を返却する形になっています（元のリストは変化しません）。

```dart:値が300以上の要素のみのIterableの値を作成する例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  Iterable<int> myIterableValue =
      myListValue.where((element) => element >= 300);
  print(myIterableValue);
}
```

#### List型のsingleWhereメソッド

`List`型の`singleWhere`メソッドは`where`メソッドのように引数に判定用の関数を受け付けますが、条件を満たす要素が1件であることも確認します。もし条件を満たす要素が1件のみであればその要素を返却し、条件を満たす要素が1件ではない場合にはエラーとなります。

```dart:値が300の要素は1件のみなのでエラーにならずに対象の要素を返却出来ている例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  int myValue = myListValue.singleWhere((element) => element == 300);
  print(myValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5e870e84-4892-7208-bd5a-09402de2f317.png)

```dart:条件を満たす要素が複数あるためエラーになる例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  int myValue = myListValue.singleWhere((element) => element >= 300);
  print(myValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0fc421c8-0ee3-3153-fbe6-d66ed877252e.png)

#### List型のfirstWhereメソッドとlastWhereメソッド

`List`型の`firstWhere`は引数で判定用の関数を受け付け、その関数で条件を満たす要素の中で先頭の要素を返却します。引数の関数には他の`where`などのメソッドと同様に引数に対象の要素を受け付け返却値には判定結果の真偽値が必要になります。条件を満たす場合には返却値に`true`が設定されるようにします。

```dart:リストの中で250以上という条件を満たす先頭の要素を取得する例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  int firstValue = myListValue.firstWhere((element) => element >= 250);
  print(firstValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fe95bc01-394f-d7ef-36b4-6c89b02ed6a7.png)

第二引数を指定しない条件且つ該当する要素が1件も存在しない場合にはエラーになります（第二引数に関しては後で説明します）。

```dart:条件を満たす要素が1件も存在しないためエラーになく例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  int firstValue = myListValue.firstWhere((element) => element >= 550);
  print(firstValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/07a675a0-6023-e7d1-d743-0dbc4c1fe19d.png)

第二引数は`orElse`という名前の引数となり、こちらも関数を引数に取りますが、要素は引数に渡されたりはせず単純に特定の返却値を設定するのみの関数となります。もし`firstWhere`メソッドで該当する要素が無かった場合にはこの関数の返却値がデフォルト値のように設定されます（エラーにもならなくなります）。

```dart:第二引数にorElse引数を指定しているため該当する要素が無くてもエラーにならない例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  int firstValue = myListValue.firstWhere(
    (element) => element >= 550,
    orElse: () => -1,
  );
  print(firstValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2d810faa-948c-eb1b-aefc-135407ce30b1.png)

`lastWhere`メソッドは`firstWhere`メソッドと使い方は同じですが、条件のチェックがリストの最後の要素から逆順に実行されていきます。

```dart:300以上という条件を満たす要素をリストの末尾からチェック・取得する例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  int lastValue = myListValue.lastWhere((element) => element >= 300);
  print(lastValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1ec3f8ad-efca-4a6f-051b-8eaabd3905da.png)

第二引数の`orElse`メソッドも`firstWhere`メソッドと同じです。

```dart
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  int lastValue = myListValue.lastWhere(
    (element) => element <= 50,
    orElse: () => -1,
  );
  print(lastValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/01f5ffa4-89a4-66e9-dfaf-f48a40df978d.png)

#### List型のgetRangeメソッド

`List`型の`getRange`メソッドは引数で指定したインデックス範囲の`Iterable`型の値を取得します。第一引数は開始インデックス、第二引数は最終インデックスとなります（ただし第二引数の指定のインデックス自体は含みません。最終インデックス - 1のインデックス範囲となります）。

```dart:インデックス1～3の要素を格納したIterableの値を取得する例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  print(myListValue.getRange(1, 4));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/42cdb7f5-c420-8b54-f1b5-07a4057651dd.png)

#### List型のindexOfメソッドとlastIndexOfメソッド

`List`型の`indexOf`メソッドは引数で指定された値の要素をリスト内で左から検索し、最初に該当するインデックスを返却します。

```dart:200の要素はインデックス1の位置にあるので1が返却される例
void main() {
  List<int> myListValue = [100, 200, 300, 400, 500];
  print(myListValue.indexOf(200));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/8d5f2624-1a96-b3bb-7631-e7c4ace6f8db.png)

対象の要素が見つからない場合には`-1`が返却されます。エラーなどにはなりません。

```dart:存在しない500の要素を検索しているので-1が返却される例
void main() {
  List<int> myListValue = [100, 200, 300, 100, 200, 300];
  print(myListValue.indexOf(500));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/92d66da7-af63-eab0-0997-ef99b27f45ce.png)

`lastIndexOf`メソッドは`indexOf`と似たような挙動をしますが検索がリストの右端から実行されます（ただし返却されるインデックスは右端からの値ではなく左端からの位置となります）。

```dart:右端から検索しているので真ん中付近にある100の要素のインデックス3が返却される例
void main() {
  List<int> myListValue = [100, 200, 300, 100, 200, 300];
  print(myListValue.lastIndexOf(100));
}
```

要素が見つからない場合に`-1`が返却される点も同様です。

```dart
void main() {
  List<int> myListValue = [100, 200, 300, 100, 200, 300];
  print(myListValue.lastIndexOf(500));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1a82de18-787e-6ef0-e21a-bb2c9b590e28.png)

#### List型のindexWhereメソッドとlastIndexWhereメソッド

`List`型の`indexWhere`は条件を満たす要素のインデックスを返却します。第一引数には条件を満たすかどうかの判定用の関数を指定します。他のメソッドと同様この関数には第一引数に要素が渡され、返却値で真偽値を返す必要があります。返却値が`true`になる最初の要素のインデックスが対象となります。

```dart:250以上という条件を満たす要素は300なのでインデックス2が返却される例
void main() {
  List<int> myListValue = [100, 200, 300, 100, 200, 300];
  print(myListValue.indexWhere((element) => element >= 250));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/71acc082-4da5-2abe-8ad6-25c9eab3290f.png)

該当するものが存在しない場合には`indexOf`メソッドなどと同じように`-1`が返却されます。

```dart:500以上の要素が存在しないため-1が返却される例
void main() {
  List<int> myListValue = [100, 200, 300, 100, 200, 300];
  print(myListValue.indexWhere((element) => element >= 500));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/290f61fc-d66c-944f-aa3c-5cf90782d85f.png)

`indexWhere`メソッドの第二引数は条件のチェックを開始するインデックス位置となります。省略可の引数となり、省略した場合にはリストの先頭の要素から順番にチェックされていきます。

```dart:第二引数で開始インデックスを3としているため最後の300の要素のインデックスが返却されている例
void main() {
  List<int> myListValue = [100, 200, 300, 100, 200, 300];
  print(myListValue.indexWhere((element) => element >= 250, 3));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5e2f9c21-501b-ce39-2e87-eddd2d80d1fc.png)

`lastIndexWhere`メソッドは`indexWhere`と同じような使い方となりますが、要素の判定はリストの右端から順番にチェックされる形となります。右側からチェックされはするものの、`lastIndexOf`メソッドなどと同じように返却値のインデックスは左から数えたインデックスとなります。

```dart:右側からチェックされるため右から2番目の300の要素のインデックスが返却されている例
void main() {
  List<int> myListValue = [100, 200, 300, 100, 200, 300, 200];
  print(myListValue.lastIndexWhere((element) => element >= 250));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/8fc553ea-2b15-9cf2-70d7-127b3c958153.png)

第二引数でチェックを開始するインデックスを指定できる点も`indexWhere`メソッドと同様です。ただし開始位置のインデックスは右側からカウントした値となります。判定と第二引数の開始インデックスは右側から、返却値のインデックスは左からのインデックスとなります。

```dart:第二引数の開始インデックスに2を指定しているため右から2番目の300の要素が対象になっていない例
void main() {
  List<int> myListValue = [100, 200, 300, 100, 200, 300, 200];
  print(myListValue.lastIndexWhere((element) => element >= 250, 2));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7a67b7d9-1075-d143-4d68-efd9559ad968.png)

#### List型のfoldメソッド

`List`型の`fold`メソッドは指定する初期値とリストの各要素を参照して単一の値を返却します。第一引数に初期値、第二引数に直前の要素までの計算結果（`previousValue`）と対象の要素（`element`）の引数を持ち計算結果を返却値に設定する形の関数を指定します。

```dart:初期値10とリストの各要素で10+1+2+3で16という結果が返却される例
void main() {
  List<int> myListValue = [1, 2, 3];
  print(
    myListValue.fold<int>(
      10,
      (previousValue, element) => previousValue + element,
    ),
  );
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fa15d273-3536-1943-cc10-fbf1f97fcf83.png)

なお、ジェネリックで`fold<int>`といったように計算値に対する型を明示していますが、これをやらないと値が`null`になったりで想定した計算にならないようです。無難にジェネリックの型の指定をしておいた方が無難？そうな印象です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/3339bde5-1840-43be-ed44-1833e2dc03b5.png)

#### List型のreduceメソッド

`List`型の`reduce`メソッドは`fold`メソッドと似た感じでリストの各要素に対して順番に計算を行い最終的な単一の値を返却します。

`fold`メソッドとの違いは初期値を別の値として指定するかどうかです。`reduce`メソッドでは初期値の引数はありません。また、こちらのメソッドはジェネリックの型指定をしなくとも問題無いようです。

```dart:リストの各要素を順番に加算していく計算で1+2+3で6が返却される例
void main() {
  List<int> myListValue = [1, 2, 3];
  print(
    myListValue.reduce(
      (previousValue, element) => previousValue + element,
    ),
  );
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/12f55708-f52b-31c1-7b7c-41b5f63b0898.png)

#### List型のmapメソッド

`List`型の`map`メソッドはリストの各要素に対して特定の処理を反映した結果の`Iterable`型の値を返却します。例えば全ての要素に特定の値を足したり型を変換したりといった制御に使えます。

第一引数には各要素への処理用の関数を指定します。そちらの関数の引数にはリストの要素が渡され、返却値には処理後の要素を設定する必要があります。

```dart:全ての要素に10加算したIterableを取得する例
void main() {
  List<int> myListValue = [1, 2, 3];
  print(myListValue.map((element) => element + 10));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e16b7104-6caa-c4b5-96ea-7772a18cf998.png)

#### List型のfollowedByメソッド

`List`型の`followedBy`メソッドは引数に指定した別のリストなど（`Iterable`型なども可）を末尾に連結した`Iterable`の値を返却します。例えば`1, 2, 3`という3つの値も持つリストでこのメソッドを使い、引数に`4, 5, 6`という要素を持つリストを指定した場合には`1, 2, 3, 4, 5, 6`という連結された各要素を持った`Iterable`の値が返却されます。

```dart
void main() {
  List<int> myListValue = [1, 2, 3];
  print(myListValue.followedBy([4, 5, 6]));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b664558d-9c51-5a3b-105a-29c88dcfb49f.png)

#### List型のreplaceRangeメソッド

`List`型の`replaceRange`メソッドは引数で指定されたインデックス範囲を、別途指定したリスト（もしくは他の`Iterable`型の値）で差し替えます。`Iterable`の値を返す形ではなく元のリストに対して変更が入ります。

第一引数は置換の開始インデックス、第二引数は置換の最終インデックス（ただしこのインデックスは含まず、第二引数を1減算したインデックスまでが範囲となります）、第三引数に差し替えで使うリストなどの値を指定します。

```dart:インデックス1～インデックス3の範囲を20,30,40の値で置換する例
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5, 6];
  myListValue.replaceRange(1, 4, [20, 30, 40]);
  print(myListValue);
}
```

なお、挙動としては第一引数と第二引数のインデックス範囲の要素を削除 → 第一引数のインデックス位置に第三引数のリストなどの各要素を入れるという流れの挙動になるため置換対象のインデックス範囲の件数と第三引数のリストなどの要素の件数がずれていてもエラーなどにはなりません。第三引数の要素の件数次第で結果のリストの件数が増減します。

```dart:置換範囲の要素数よりも多い要素数のリストを第三引数に指定した例
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5, 6];
  myListValue.replaceRange(1, 4, [20, 30, 40, 50, 60]);
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1c3c8144-d50c-0aad-50e2-7e040476bc76.png)

#### List型のsetAllメソッド

`List`型の`setAll`メソッドは引数で指定した位置以降の要素を別途引数で指定したリスト（もしくは他の`Iterable`の値）で置換します。`replaceRange`メソッドと近い挙動をしますが、`setAll`メソッドの方は終了インデックスの引数は無く、固定で引数で指定されたリストなどの要素数分が置換されます。また、こちらも`Iterable`の値を返却するのではなく元のリストに対して変更が入ります。

第一引数には開始位置のインデックス、第二引数には置換後として反映するリスト（もしくは他の`Iterable`の値）を指定して使います。

```dart
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5, 6];
  myListValue.setAll(1, [20, 30, 40]);
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/643d5e38-1961-0571-ba5e-e954201bbe0c.png)

また、第一引数に指定した開始インデックス位置を加味した第二引数に指定したリストなどの要素の件数が元のリストの長さを超える場合にはエラーになります（元のリストの要素の件数は変動しないようになっています）。

```dart:元のリストの長さを超える引数指定になっているためエラーになる例
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5, 6];
  myListValue.setAll(1, [20, 30, 40, 50, 60, 70]);
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2a4b597a-d570-e1f6-06b6-0d536b02123f.png)

#### List型のsetRangeメソッド

`List`型の`setRange`メソッドは`replaceRange`メソッドや`setAll`メソッドなどと同様にリスト内の複数の要素を置換します。

`replaceRange`メソッドや`setAll`などと異なり以下のような挙動をします。

- `setAll`メソッドとは異なり置換する範囲をそれぞれ引数で指定します。
- `replaceRange`メソッドのように範囲を超えて置換されたりしません。
  - ※リストの件数は置換処理後も変わらず、もし置換範囲がリストのインデックス範囲を超えている場合にはエラーになります。
- 第四引数に`skipCount`という引数があり、置換で指定したリスト（もしくは別の`Iterable`の値）の中で先頭の任意の個数の要素をスキップすることができます。

第一引数は置換範囲の開始インデックス、第二引数は置換範囲の終了インデックス（ただしこのインデックス自体は含まず-1した位置までが対象となります）、第三引数には置換で設定するリスト（もしくは他の`Iterable`の値）、第四引数は要素のスキップ数の指定（省略可）となります。

```dart:1～2のインデックス範囲の要素を第三引数で指定したリストで置換する例
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5, 6];
  myListValue.setRange(1, 3, [20, 30, 40, 50, 60, 70]);
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/38725bc6-6d88-a85f-c210-641991e026d1.png)

```dart:第四引数に2を指定しているため、置換に使われるリストの要素位置がずれる例
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5, 6];
  myListValue.setRange(1, 3, [20, 30, 40, 50, 60, 70], 2);
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5495ed0a-04bd-ff3b-20b3-6aab3617e478.png)

```dart:置換範囲指定の第二引数がリストのインデックス範囲を超えるためエラーになる例
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5, 6];
  myListValue.setRange(1, 10, [20, 30, 40, 50, 60, 70], 2);
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7ae32fdf-e6a0-f2d6-d637-99f35e011192.png)

#### List型のsublistメソッド

`List`型の`sublist`メソッドは引数で指定されたインデックス範囲のリストを返却します。第一引数には開始インデックス、第二引数には終了インデックス（ただし他と同様にこのインデックスを`-1`した位置が最後のインデックスとなります）を指定します。返却値は`Iterable`型ではなく`List`になります。

```dart:インデックス範囲2～3のリストを取得する例
void main() {
  List<int> myListValue = [0, 1, 2, 3, 4, 5, 6];
  print(myListValue.sublist(2, 4));
}
```

#### List型のshuffleメソッド

`List`型の`shuffle`メソッドはリストの要素をシャッフルします。そのため実行の度にリストの要素の順番が変動します。処理はリストを直接更新します。

```dart
void main() {
  List<int> myListValue = [0, 1, 2, 3, 4, 5, 6];
  myListValue.shuffle();
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f04a0036-4d7b-8ac8-0ff1-c20d425f4c02.png)

#### List型のsortメソッド

`List`型の`sort`メソッドはリストの要素をソートします。引数は省略可で、省略した場合には数値の昇順でソートを行います。

```dart:数値の要素を昇順でソートする例
void main() {
  List<int> myListValue = [20, -5, 15, 3, 2, 10, 35, -16];
  myListValue.sort();
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/39058a8f-4ed7-b0ca-6510-4c076fc24d65.png)

数値以外、例えば文字列ではどのような判定になるのだろう・・・と思ったのですが、どうやらUnicodeのコードポイントの順序の昇順でソートがされるようです。この辺はPythonとかと似たような挙動でしょうか。

```dart:文字列の要素をコードポイントの順番の昇順でソートする例
void main() {
  List<String> myListValue = ['CCCC', 'BBB', 'DD', 'AAAAA', 'EE'];
  myListValue.sort();
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1a0b99aa-d47e-d753-951e-5f01741db5c4.png)

第一引数は省略可能ですが、指定する場合にはソート順判定用の関数を指定します。この関数には`a`と`b`の2つの引数を取り、もし`a`を`b`よりも前に配置したい時には`-1`、`a`と`b`が同値条件なら`b`、`a`を`b`よりも後に配置させたい場合には`1`を返却する必要のある関数となります。

```dart:引数を指定して文字数の昇順でソートする例
void main() {
  List<String> myListValue = ['CCCC', 'BBB', 'DD', 'AAAAA', 'EE'];
  myListValue.sort((a, b) {
    if (a.length <= b.length) {
      return -1;
    } else if (a.length == b.length) {
      return 0;
    } else {
      return 1;
    }
  });
  print(myListValue);
}
```

なお、`int`型などの基本的な型ではこの辺の制御がしやすいように別の値と比較して小さければ`-1`、同値であれば`0`、大きければ`1`を返却する`compareTo`というメソッドがあります。こちらを使うことで`sort`メソッドなどでの記述をシンプルにすることができます。

```dart:compareToメソッドを使ってシンプルなsortの引数を設定する例
void main() {
  List<String> myListValue = ['CCCC', 'BBB', 'DD', 'AAAAA', 'EE'];
  myListValue.sort((a, b) => a.length.compareTo(b.length));
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5d6228bd-cd98-dca0-cf11-ea4a11332d8a.png)

昇順でソートしたリストを降順にしたい場合には`reversed`属性を使うか、もしくは引数の関数の条件で設定します。

```dart:reversedを使って降順でソートされたリストを取得する例
void main() {
  List<int> myListValue = [20, -5, 15, 3, 2, 10, 35, -16];
  myListValue.sort();
  myListValue = myListValue.reversed.toList();
  print(myListValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e97290f0-860a-7eca-3483-c69640a51228.png)

#### List型のskipメソッドとskipWhileメソッド

`List`型の`skip`メソッドは引数で指定した要素数分先頭の要素を取り除いた`Iterable`の値を返却します。

```dart:先頭の2つの要素をスキップして残りの要素のIterableの値を取得する例
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5];
  print(myListValue.skip(2));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6e797979-acc5-5480-36c5-d2ca605dabae.png)

`skipWhile`メソッドは`skip`メソッドと同様に先頭の方の要素を省いた`Iterable`の値を返却します。ただしこちらの引数はスキップする数ではなく判定用の関数を指定する必要があります。引数で渡された関数でリストの要素を先頭から順番にチェックしていって、条件を満たしている限りは要素がスキップされます。関数の第一引数には対象の要素が渡され、返却値には条件を満たすかどうかの真偽値が必要になります。

```dart:要素が3以下である限り先頭の方の要素をスキップする例
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5, 6];
  print(myListValue.skipWhile((element) => element <= 3));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/30ca46ff-300d-c1eb-4d2e-709ef334c6c1.png)

#### List型のtakeメソッドとtakeWhileメソッド

`List`型の`take`メソッドは引数で指定した個数の件数分の要素を格納した`Iterable`の値を返却します。例えば引数に`3`を指定すれば3件の要素を格納した`Iterable`が返却されます。対象となる要素は先頭から順番に処理されていきます。

```dart:先頭の3件の要素のIterableを取得する例
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5, 6];
  print(myListValue.take(3));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/34064044-e959-aa36-376f-7220f8396cd9.png)

もし引数で指定した件数よりもリストの要素数が少ない場合には結果の要素数は引数の値よりも少なくなります（エラーになったりはしません）。

```dart:引数の値よりもリストの要素数が少ないため結果も3要素のみのIterableとなる例
void main() {
  List<int> myListValue = [1, 2, 3];
  print(myListValue.take(5));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ab037eac-5f94-9372-543a-187160a1fce5.png)

`takeWhile`メソッドは`take`メソッドのように先頭からの要素を参照した`Iterable`の値を返却します。ただしこちらは引数で一定の件数を対象とするのではなく、引数で指定したチェック用の関数の条件を満たす限りの要素を格納した`Iterable`の値を返却します。条件を満たさなくなったらその直前の要素までが返却値の対象となります。引数の関数には引数に対象の要素、返却値に真偽値が必要になります（条件を満たす場合には`true`を返却する形にします）。

```dart:先頭から要素が3以下という条件を満たす範囲のIterableを取得する例
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5];
  print(myListValue.takeWhile((element) => element <= 3));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b544654b-c17e-62b4-7af0-cdccd9fc7ab6.png)

#### List型のanyメソッド

`List`型の`any`メソッドは引数に関数を受け付けて、リストの各要素ごとにその関数が条件を満たすかどうかを判定し、もし1つでも条件を満たす要素が存在すれば`true`を返却するメソッドです。

対象の関数は第一引数に対象の要素が指定され、返却値には真偽値を設定する必要があります。

※Dartにおける関数の書き方や無名関数の書き方については後々の節で詳しく触れますが、`(引数) => 返却値`という記述で無名関数がDartでは定義できる形になっています。

```dart:関数内で文字列の文字数が2文字かどうかをチェックし、1件2文字の要素があるためtrueが返却されている例
void main() {
  List<String> myListValue = ['AAA', 'BB', 'CCCC'];
  print(myListValue.any((element) => element.length == 2));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/69671d6c-2e01-b502-e3ee-29d4041ff1b6.png)

```dart:どの要素も条件を満たさないのでfalseが返却されている例
void main() {
  List<String> myListValue = ['A', 'B', 'CCC'];
  print(myListValue.any((element) => element.length == 2));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/bc3b4f14-d249-fa38-f4df-f3d222227623.png)

#### List型のeveryメソッド

`List`型の`every`メソッドは`any`メソッドと似た書き方と挙動になりますが、`any`メソッドとは異なり全ての要素が関数内で条件を満たした場合に`true`を返却するメソッドとなります。

```dart:リストの全ての要素が250以下を満たすためtrueが返却される例
void main() {
  List<int> myListValue = [100, 200, 150, 220];
  print(myListValue.every((element) => element <= 250));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/baf956ee-3a65-553f-0eae-7bc985c1990e.png)

```dart:要素が1つだけ条件を満たさないのでfalseが返却される例
void main() {
  List<int> myListValue = [100, 180, 150, 220];
  print(myListValue.every((element) => element <= 200));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ddc52830-d096-3598-4c67-951ef3ec9280.png)

#### List型のjoinメソッド

`List`型の`join`メソッドはリストの要素を連結した文字列を返却します。引数は区切り文字となり、省略した場合は区切り文字無しで連結されます。

```dart:区切り文字無しで連結する例
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5];
  print(myListValue.join());
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e4f18ecd-ce4d-4b13-b33b-4c2ce2efc66c.png)

```dart:コンマを区切り文字として指定した例
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5];
  print(myListValue.join(','));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/96a2c837-f9fb-ae7f-b40c-4f1f507c0280.png)

#### List型のasMapメソッド

`List`の`asMap`メソッドはリストを`Map`（辞書）に変換した値を返却します。キーにはリストのインデックス、値にリストの要素が設定されます。
※`Map`型については後々の節で触れます。

```dart
void main() {
  List<int> myListValue = [1, 2, 3, 4, 5];
  print(myListValue.asMap());
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b3d0e86d-b2c3-e6f0-6127-5b1c72bfbf95.png)

#### List型のtoSetメソッド

`List`型の`toSet`メソッドはリスト型を`Set`型（集合の型）に変換した値を返却します。リストに似た値となりますが、重複した値は`Set`からは取り除かれます（一意な要素の結果となります）。

※`Set`型については後々の節で詳しく触れます。

```dart:重複している要素が取り除かれる例
void main() {
  List<int> myListValue = [1, 2, 1, 2, 3, 4, 3];
  print(myListValue.toSet());
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7f70b09e-0de5-4d4d-096e-0b3755759670.png)

### 集合の型

集合は`Set`型を使います。また、値を設定する際には`{}`の括弧を使いコンマ区切りで複数の値を指定します。`Map`と同じ括弧となりますがこちらはキーと値のような設定ではないためコロンは使わずにコンマのみで値を区切って使います。
※`Map`型については後々の節で触れます。

```dart
void main() {
  Set mySetValue = {1, 2, 3};
  print(mySetValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/238a2d48-5ccc-9693-1fc0-9bb88968b74d.png)

集合に含める値の型を固定したい場合のジェネリックの型の指定は`List`などと同じように`<対象の型>`といったように`<>`の括弧を`Set`の後に記述します。

```dart:整数の集合と明示ずるために<int>と型を設定している例
void main() {
  Set<int> mySetValue = {1, 2, 3};
  print(mySetValue);
}
```

また、`Set`型では重複した値が存在する場合には1件のみ値が残されます。そのためリストなどを`Set`に変換した場合には重複は取り除かれて一意な値のみ残ります。`Set`に固定の重複値を設定していたりすると警告が表示されたりもします。

```dart
void main() {
  Set<int> mySetValue = {1, 2, 1, 2, 1, 2};
  print(mySetValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fb7759d2-379b-9728-613c-b739c10aa48b.png)

`List`のように`Set`の値もfor文でループを行うことができます。

```dart
void main() {
  Set<int> mySetValue = {1, 2, 3};
  for (final value in mySetValue) {
    print(value);
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/44889f8b-0b7c-610f-09a4-927d0b028149.png)

#### Setで使える属性やメソッドに関して

`Set`型では`List`型と同じ属性やメソッドの多くを利用することができます。

例えば属性で言えば`first`、`firstOrNull`、`length`、`single`、`singleOrNull`、`isEmpty`などの`List`と同様の様々な各属性が、メソッドで言えば`add`、`addAll`、`any`、`clear`、`join`などの多くのメソッドが用意されています。

それらの属性やメソッドに関しては`List`型の節で詳しく触れたため`Set`のそれらの説明は割愛します。

### マップ（辞書）の型

マップ（辞書）は`Map`型を使います。キーと値を持つ値となります。値を定義する際には`{}`の括弧を使い、キーと値をコロンで区切り、そして要素をコンマで区切ります。

```dart
void main() {
  Map myMapValue = {"myKey1": 10, "myKey2": "Hello"};
  print(myMapValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a6693c3c-9788-c008-fb35-348328f018c6.png)

前節まででも触れましたがマップでキーと値の型を固定したい場合には`<キーの型, 値の型>`といった形でコンマ区切りでそれぞれを指定します。

```dart:キーをString、値をintの型に設定する例
void main() {
  Map<String, int> myMapValue = {
    'key1': 100,
    'key2': 200,
  };
  print(myMapValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/00abe59e-7e4b-e2bb-524d-d531741ffdb0.png)

#### MapEntry型

`Map`型について詳しく触れて行く前に`MapEntry`という型について触れた方がスムーズなので先に触れておきます。

`MapEntry`型は単一のキーと値の組み合わせを扱う型となります。`Map`型が複数のキーと値を持てる形になる一方でこちらは単一のキーと値になります。

使い方はクラスのコンストラクタで初期化し、第一引数にキー、第二引数に値を設定します。

```dart
void main() {
  MapEntry myMapEntry = MapEntry('key', 100);
  print(myMapEntry);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0ad03eef-97e6-0d1d-4100-4bc4fdcdd681.png)

`MapEntry`型でキーを参照したい場合には`key`属性を使います。

```dart
void main() {
  MapEntry myMapEntry = MapEntry('myKey', 100);
  print(myMapEntry.key);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fe7b6a92-8727-12e7-7c6c-1d352b84be27.png)

値の方は`value`属性でアクセスすることができます。

```dart
void main() {
  MapEntry myMapEntry = MapEntry('myKey', 100);
  print(myMapEntry.value);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/652076b4-d6d8-463a-c49e-58454c85350d.png)

#### Map型のisEmpty属性とisNotEmpty属性

`Map`型の`isEmpty`属性は辞書が空かどうか（1つのキーが存在しないかどうか）の真偽値となります。

```dart:空の辞書なのでtrueとなっている例
void main() {
  Map<String, int> myMapValue = {};
  print(myMapValue.isEmpty);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/224b23af-51e4-5792-8484-1f59b5574bed.png)

```dart:空ではないのでfalseとなっている例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  print(myMapValue.isEmpty);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4d699b00-85e5-bfac-a38e-b93385b9f4d0.png)

`isNotEmpty`属性は`isEmpty`と逆の値となります。つまり辞書が空でなければ`true`となります。

```dart:空ではないのでtrueとなっている例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  print(myMapValue.isNotEmpty);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/809a10a5-c475-51ef-6c84-44a772d1ad0e.png)

#### Map型のkeys属性とvalues属性

`Map`型の`keys`属性は辞書の各キーを格納した`Iterable`の値となります。

```dart
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20, 'key3': 30};
  print(myMapValue.keys);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/017e7592-9cbe-407d-8cb7-6e8d368f6bed.png)

似たような形で`values`属性は辞書の値の`Iterable`の値となります。

```dart
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20, 'key3': 30};
  print(myMapValue.values);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5b92f362-ca88-1498-818c-a941085910ee.png)

#### Map型のlength属性

`Map`型の`length`属性は辞書の要素数（キーの数）となります。

```dart:3件の要素（キー）を持つため3となる例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20, 'key3': 30};
  print(myMapValue.length);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2153b5c6-baeb-1b42-dd0d-0e2896d392d9.png)

#### Map型のentries属性

`Map`型の`entries`属性は各キーと値の`MapEntry`型の値を格納した`Iterable`となります。

```dart
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20, 'key3': 30};
  print(myMapValue.entries);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7981f267-ca15-8928-5237-e5bf9631bff4.png)

#### Map型のaddAllメソッド

`Map`型の`addAll`メソッドは引数に指定された別の`Map`の各キーと値を対象の辞書に追加します（2つの辞書を統合したような結果になります）。

```dart
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  myMapValue.addAll({'key3': 30, 'key4': 40});
  print(myMapValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a27759b9-387e-54da-c2c0-99f245fa9975.png)

キーが被っている場合には引数で指定した方の値で上書きされます。

```dart:key2が30で上書きされる例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  myMapValue.addAll({'key2': 30, 'key3': 40});
  print(myMapValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d9121a23-c0ad-ca75-05a9-a93ee2697fb2.png)

#### Map型のaddEntriesメソッド

`Map`型の`addEntries`メソッドは引数に指定された`MapEntry`のリスト（もしくは別の`Iterable`）のキーと値を対象の辞書に追加します。

```dart
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  myMapValue.addEntries([
    MapEntry('key3', 30),
    MapEntry('key4', 40),
  ]);
  print(myMapValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5e5f9bc1-3537-f586-a50d-eaeeb4f41b9f.png)

こちらも`addAll`メソッドと同様にキーが被っている場合には引数で指定された方の値で上書きされます。

```dart:key2が30で上書きされる例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  myMapValue.addEntries([
    MapEntry('key2', 30),
    MapEntry('key3', 40),
  ]);
  print(myMapValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fdc0ce2f-5ded-a935-ab65-71e560ed225e.png)

#### Map型のclearメソッド

`Map`型の`clear`メソッドは辞書の内容を空にします（1つもキーが無い状態にします）。

```dart
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  myMapValue.clear();
  print(myMapValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/573f3192-ed26-631f-afd4-650baf726a33.png)

#### Map型のcontainsKeyメソッド

`Map`型の`containsKey`メソッドは引数に指定したキー名を対象の辞書が持つかどうかの真偽値を返却します。キーがあれば`true`、無ければ`false`となります。

```dart:キーがあるのでtrueとなる例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  print(myMapValue.containsKey('key2'));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b1b6d8cb-24f4-aa55-82e9-173c2fc4929c.png)

```dart:キーが無いのでfalseとなる例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  print(myMapValue.containsKey('key3'));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a2471ae5-5760-4fba-ba91-d2e5ab2bda5a.png)

#### Map型のforEachメソッド

`Map`型の`forEach`メソッドは引数に関数を受け取り、その関数では各キーと値を引数に持ち各要素数分実行されます。実質的にfor文でループを回すような挙動をします。関数の第一引数がキー、第二引数が値となります。

```dart
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  myMapValue.forEach((key, value) {
    print('キー: $key, 値: $value');
  });
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c9771555-b14a-9122-8850-46d9b454c0e0.png)

#### Map型のmapメソッド

`Map`型の`map`メソッドは引数に関数を受け付け、各キーと値に対してなんらかの処理を行った新たな`Map`の値を返却します。

関数の第一引数には辞書のキー、第二引数には辞書の値が設定されます。また、返却値には`MapEntry`の値が必要になります。

```dart:各キーの末尾に0の文字を加えて、各値に100加算した新しいMapを取得する例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  Map<String, int> newMapValue =
      myMapValue.map((key, value) => MapEntry(key + '0', value + 100));
  print(newMapValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a4986a57-8aa8-ec07-a92f-8801857da502.png)

#### Map型のputIfAbsentメソッド

`Map`型の`putIfAbsent`メソッドは第一引数で指定したキーがもし存在しなければ第二引数で指定した関数によって生成される値をそのキーに設定します。既に該当のキーが存在すれば処理をスキップします。

また、返却値にはもし対象のキーが元から存在すればそのキーの値が設定され、もし対象のキーが無ければ新たに設定された値が返却されます。

```dart:key3というキーは存在しないため30という値が設定される例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  int putValue = myMapValue.putIfAbsent('key3', () => 30);
  print(myMapValue);
  print(putValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b78c9b83-3c3e-20d9-cea8-f213d75b3b05.png)

```dart:key2というキーが元から存在するため値の設定がスキップされる例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  int putValue = myMapValue.putIfAbsent('key2', () => 30);
  print(myMapValue);
  print(putValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f6d9938e-a99a-c205-f591-2a5d6174db92.png)

#### Map型のremoveメソッド

`Map`型の`remove`メソッドは引数で指定したキーを辞書から取り除きます。返却値は取り除かれた値となります。もし削除対象のキーが存在しない場合には返却値は`null`となります（エラーにはなりません）。

```dart:辞書からkey2というキーを削除する例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  int? removedValue = myMapValue.remove('key2');
  print(myMapValue);
  print(removedValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ee9ec999-ea02-1b5e-24fe-40aaf8885929.png)

```dart:key3というキーは辞書に存在しないのでnullが返却されている例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20};
  int? removedValue = myMapValue.remove('key3');
  print(myMapValue);
  print(removedValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c22b4c0a-7a3d-079d-3252-1e1cacc4336e.png)

※余談ですがキーが存在する場合でも元から辞書の値で`null`を格納していた場合には`null`が返却されたからといって必ずしもキーが存在しないという判定にはなりません。

#### Map型のremoveWhereメソッド

`Map`型の`removeWhere`メソッドでは引数に判定用の関数を受け付け、その関数内で条件を満たすものを辞書から取り除きます。関数の第一引数には辞書キー、第二引数には辞書の値が設定されるため、キーか値（もしくは両方）を参照して判定を行うことができます。返却値には真偽値が必要となり、`true`を返却した要素が辞書から取り除かれます。

条件を満たす要素が複数存在する場合にはそれらがまとめて取り除かれます。

```dart:辞書の値が20以上の要素を辞書から取り除く例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20, 'key3': 30};
  myMapValue.removeWhere((key, value) => value >= 20);
  print(myMapValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c04df965-b6c3-0a29-3d2f-92c3768b6bc3.png)

#### Map型のupdateメソッド

`Map`型の`update`メソッドは特定のキーの値を更新することができます。

普通にキーを指定して値を設定するのと何が違うのか？という所ですが、「現在設定されている値を参照して結果の値を設定する」「キーが存在しない場合には別の値を設定する」といった制御が可能です。

第一引数には対象のキーの文字列、第二引数には対象のキーが存在する場合にそのキーの値を参照して結果の値を返却する関数、第三引数には対象のキーが存在しない場合の値の設定用の関数（省略可）を指定します。第二引数の関数の引数には対象のキーの値が渡されます。また、第二引数と第三引数の関数両方とも設定したい値を返却する必要があります。

```dart:対象のキーが存在するためkey2に現在の値に対して100加算した値を設定する例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20, 'key3': 30};
  myMapValue.update('key2', (value) => value + 100, ifAbsent: () => -1);
  print(myMapValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/241d7f5e-cc93-8f44-24ed-a2522ae5b984.png)

#### Map型のupdateAllメソッド

`Map`型の`updateAll`メソッドは`map`メソッドのように関数の引数を使用して辞書の全ての要素に対して処理を反映します。`map`メソッドのように引数で指定する関数の引数にはキーと値、返却値には更新後の値が必要になります。

`map`メソッドと何が違うのか？というところですが、`map`メソッドは新しい辞書を返却する一方で`updateAll`メソッドの方は元の辞書自体を更新します。

```dart:元の辞書の全ての値に対して100加算する例
void main() {
  Map<String, int> myMapValue = {'key1': 10, 'key2': 20, 'key3': 30};
  myMapValue.updateAll((key, value) => value + 100);
  print(myMapValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/15c20670-ee09-716d-39df-966b9411c1f0.png)

### dynamic型

`dynamic`型は特定の型に限られない任意の型を表します。型が明示できない場合は複数の型になりうる場合などに使用します。

柔軟性は得られるものの安全面やIDEなどでの補完やチェック等でマイナス面が多いため不要であれば使わずに型を明示した方が好ましいです。

```dart:任意の型のキーと値を辞書に設定できるようにdynamic型を使用している例
void main() {
  Map<dynamic, dynamic> myMapValue = {
    'key1': 10,
    350.5: true,
    false: 0.005,
  };
  print(myMapValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/188e7a49-c2f1-3ac7-eaec-6123350fa98f.png)

### 型のキャストについて

Dartでの型のキャスト（型変換）は主にtoやasで始まる名前のメソッドを使います（例 : `toString`や`asMap`など）。

```dart:20.5という浮動小数点数をtoIntメソッドで20の整数に型変換する例
void main() {
  double myDoubleValue = 20.5;
  int myIntValue = myDoubleValue.toInt();
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/244b7b47-e9a1-c381-f5a6-afde1e05ffb4.png)

`as 型名`といったように`as`のキーワードを使う方法もあります。ただし多くのケースで変換が効かない印象なのと、ランタイムエラーにもなりやすいという面を加味して基本的には整数や文字列などの基本的な型変換用の各メソッドを使う形がメインになりそうな印象です（一方で型が不明になる際に特定の型で`as`を使う・・・といったケースはたまにあるかなという印象です）。

```dart:20.5という値はasを使って整数に変換できないためエラーになる例
void main() {
  int myIntValue = 20.5 as int;
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0236b174-5bfe-e36c-db97-fd48b94bf017.png)

また、他の言語で良くあるような対象の型のコンストラクタとしてキャストする・・・といったこともDartではできません。コンパイルエラーとなります。

```dart:int型によるコンストラクタでのキャストをしようとしてエラーになっている例
void main() {
  int myIntValue = int(20.5);
  print(myIntValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/093de8b4-6e2e-3773-e5cf-bea8d0dc4eef.png)

### void型

`void`型は関数やメソッドの返却値の型の指定などで使用します。`void`型を指定した場合返却値を返さないということを示します。関数に関してはまだ詳しくは触れていませんが、今までの節でも`main`関数などで記述してきた型となります。

```dart
void main() {
  printMessage();
}

void printMessage() {
  print('Hello, World!');
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/69b0fdc8-43ee-ca14-0eee-ff1afcd52236.png)

## nullについて

Dartでの何も値を持っていないものを表すには`null`を使います。有効な値を返却できない場合や初期化前などで様々なケースで使われます。

型で定義する際には型名の直後に`?`の記号を付けます。例えば`null`の値を取りうる文字列であれば`String?`といったように書きます。

```dart:nullで初期化してその後に文字列を設定している例
void main() {
  String? myStringValue = null;
  print(myStringValue);

  myStringValue = 'Hello!';
  print(myStringValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e6c3ce26-3cea-991d-d44e-70cfdb4d0309.png)

また、`null`の型指定をした値に関しては初期値を与えなくても参照することができます（初期化をしなくても使えます）。その場合その値は`null`となります。jsの`undefined`のような概念はありません。

```dart:初期化せずに変数をprintしている例
void main() {
  String? myStringValue;
  print(myStringValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/be6c4225-e8e3-48b1-9149-1b3581aa4140.png)

`null`の型指定をしていない場合、初期値を与えていないと参照時にエラーになります。

```dart
void main() {
  String myStringValue;
  print(myStringValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/77894130-1b63-744c-4c1b-ae8a4d936488.png)

### Dartのnull安全について

Dartは3系のバージョン以降では基本的にnull安全になっています。うっかりミスなどで`null`になって欲しくないケースで`null`を参照してしまっていてランタイムエラーになる・・・といったことをしっかりと減らすことができます（コンパイルエラーとなるためうっかり`null`に絡んでランタイムエラーになる条件が残ったままリリースしてしまうといったことを減らすことができます）。

たとえば以下のように文字列の引数を必要にする関数で`null`を受け付ける文字列の変数を指定した場合にはコンパイルエラーとなります。

```dart
void main() {
  String? myStringValue;
  printMessage(message: myStringValue);
}

void printMessage({required String message}) {
  print(message);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a78b232f-4849-2650-9451-f25423a8cdf0.png)

もうDart2系を使うケースもほとんど無くなって来ていると思うため、null安全の面で悩まされるケースも少ないのでは・・・という印象です。

### nullを取りうる値に対する型ガード

`null`を取りうる変数などに対してif文などで`null`かどうかを判定することで型ガードのように変数の型の絞り込みを行うことができます。

※if文などの条件分岐は後々の節で詳しく触れます。

```dart:if文でnullではないという条件で絞り込んでいるためエラーにならずに引数に指定できている例
void main() {
  String? myStringValue = getMessage();
  if (myStringValue != null) {
    printMessage(message: myStringValue);
  }
}

String? getMessage() {
  return 'Hello!';
}

void printMessage({required String message}) {
  print(message);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e1de2d3f-0f1c-0dc3-ccf5-47f98cf793a4.png)

### !記号による参照

if文などで確実にnull安全を担保できているとコンパイルエラーでミスを弾けて好ましいのですが、if文で判定するだけでは不便なことがあります。例えばif文などは挟んでいないものの、`null`であれば確実にエラーで弾くようにしているため`null`にはならないことが分かっている・・・といったケースです。

そういった場合には`null`を取りうる変数の後に`!`記号を付けることで`null`を取らない変数と同じように扱うことができコンパイルエラーを回避することができます。

```dart:myStringValue!と!記号を使っているのでnullに絡んでコンパイルエラーにならない例
void main() {
  String? myStringValue = getMessage();
  printMessage(message: myStringValue!);
}

String? getMessage() {
  return 'Hello!';
}

void printMessage({required String message}) {
  print(message);
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/961e02ca-1618-e42f-98cb-e8c586535433.png)

ただし、うっかりコード変更などで実は`null`を取りうる形になっていた・・・みたいなケースが発生すると`null`に起因してエラーが発生したりしてくるたと、安全のためにももしif文とかで対応が効くケースなら`!`の記号よりもそちらの方が推奨されます。

### ?記号による安全な属性やメソッドなどへのアクセス

`?`記号を使うことで`null`になりうる値で属性やメソッドにアクセスする場合、`null`の場合にはそれらの属性などにはアクセスできないためランタイムエラーになってしまいます。

そういった場合にはその変数などの直後に`?`の記号を付けることでランタイムエラーを回避することができます。例えば`myStringValue`という`null`を取りうる変数で`myStringValue.length`といった属性にアクセスすると変数が`null`だった場合にランタイムエラーになってしまいます。一方で`myStringValue?.length`といったように`?`記号を付与しつつアクセスした場合には変数が`null`であればランタイムエラーにはならずにそのまま`null`、`null`でなければ`length`属性の値が参照できる・・・といった挙動になります。

```dart:?記号を付与しているのでコンパイルエラーにならない例
void main() {
  String? myStringValue = getMessage();
  print(myStringValue?.length);
}

String? getMessage() {
  return 'Hello!';
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/8b7f3ee6-8ef8-e647-cebb-798a57b9c25c.png)

```dart:対象の変数がnullの場合に属性にアクセスしてもエラーにならない例
void main() {
  String? myStringValue = getMessage();
  print(myStringValue?.length);
}

String? getMessage() {
  return null;
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/38ca5fa6-b021-b5d1-6ff4-5d6fcd49a4a7.png)

### ??記号によるnull合体演算子

対象の変数などの値の後にスペースと`??`の記号を記述すると、もしその値が`null`だった場合にその後に指定した値を代わりに参照する・・・といった制御ができます。

例えば`myStringValue ?? '値がnullです。'`といった記述を使った場合、`myStringValue`がnullでなければ`myStringValue`の値がそのまま参照され、もし`null`であれば`'値がnullです。'`という文字列が参照されます。三項演算子のような挙動をします。

```dart:値がnullの場合に??の後の値が参照される例
void main() {
  String? myStringValue = getMessage();
  print(myStringValue ?? '値がnullです。');
}

String? getMessage() {
  return null;
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/67560c5b-8b0e-8535-dd40-f2c4c24b624f.png)

```dart:値がnullではないので変数の値がそのまま使われる例
void main() {
  String? myStringValue = getMessage();
  print(myStringValue ?? '値がnullです。');
}

String? getMessage() {
  return 'Hello!';
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/39e1c2b9-35b4-9baa-1d05-7fc0ecb2f21e.png)

### ??=記号によるnullの代替の値の設定

`??=`記号を使うと対象の変数がもし`null`であれば代替の値を設定するという挙動をします。たとえば`myStringValue ??= '値がnullです';`という記述であれば、もし`myStringValue`という変数が`null`であれば右辺の`'値がnullです'`という値が設定されます。値が`null`でなければ元の値がそのまま維持されます。

```dart:値がnullなので代替の文字列が設定される例
void main() {
  String? myStringValue = getMessage();
  myStringValue ??= '値がnullです';
  print(myStringValue);
}

String? getMessage() {
  return null;
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f0bc2804-be9d-fab6-7d12-1eb772cdbd8a.png)

```dart:値がnullではないので元の値がそのまま使用される例
void main() {
  String? myStringValue = getMessage();
  myStringValue ??= '値がnullです';
  print(myStringValue);
}

String? getMessage() {
  return 'Hello';
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e769cfd4-6914-a5b7-8506-ac33186462ff.png)

## メソッドチェーン風の記述

Dart自体ではクラスのメソッドで`this`を返却することでメソッドチェーンのようなことは他の言語と同じように実装することができます。
※クラスについての詳細は後々の節で触れます。

```dart
class Counter {
  int _currentCount = 0;

  Counter increment() {
    _currentCount++;
    return this;
  }

  Counter decrement() {
    _currentCount--;
    return this;
  }

  Counter printCurrentCount() {
    print('現在のカウント: $_currentCount');
    return this;
  }
}

void main() {
  Counter counter = Counter();
  counter.increment().increment().increment().decrement().printCurrentCount();
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/43731740-935c-17fe-f319-2a94c2b092ee.png)

一方で、メソッドなどでインスタンスを返却しない場合でもDartでは`..`の記号を使うことでメソッドチェーンのようなことができます。インスタンスを返却する形に作られていないクラスとかでも使えたり、返却用の無駄な行が不要になるため個人的にはこちらを使うことが多めです。

```dart:インスタンスを返却しない各メソッドで..記号を使ってメソッドチェーンのような書き方をしている例
class Counter {
  int _currentCount = 0;

  void increment() {
    _currentCount++;
  }

  void decrement() {
    _currentCount--;
  }

  void printCurrentCount() {
    print('現在のカウント: $_currentCount');
  }
}

void main() {
  Counter counter = Counter();
  counter..increment()..increment()..increment()..decrement()..printCurrentCount();
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/de7e6cc7-77d1-6fa0-b8b2-ca8ea4e82967.png)

なお、フォーマッタをかげると`..`記号で改行されるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1092eaba-286d-89cf-26bd-8b9f06cb44b0.png)

## 関数について

以降の節では関数について詳しく触れていきます。

### 関数定義の基本

関数は`返却値の型 関数名(引数内容) { 関数の処理内容 }`といったように定義します。引数定義の箇所は変数定義などと同じように`型名 引数名`といった順番で書きます。また、複数の引数を受け付ける場合にはコンマ区切りで定義します。

例えば返却値の型が`void`、関数名が`printMessage`、引数には`String`型で`message`という引数を受け付ける場合には`void printMessage(String message) { ... }`といった書き方になります。

```dart
void main() {
  printMessage('Hello!');
}

void printMessage(String message) {
  print(message);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/19a65413-2b10-7ff7-39de-f03d6e9120f5.png)

### 関数の呼び出し

関数を呼び出したい場合には`対象の関数名(指定する引数内容)`といった形で`()`の括弧を使います。また、返却値（後の節で触れます）を別の変数などに設定したければ`=`の記号を使って左側に対象の変数などを指定します。

```dart:10と20という2つの引数値を指定して、返却値をaddedValueという変数に設定している例
void main() {
  int addedValue = addTwoValues(10, 20);
  print(addedValue);
}

int addTwoValues(int a, int b) {
  return a + b;
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/21e215af-823a-4205-4599-8a34ad4c7e18.png)

### 位置引数の定義と指定

関数を呼び出す際に単純に順番通りに引数の値を指定する際には引数の定義も前節の通りシンプルにコンマ区切りで定義すれば扱うことができます（位置引数と言います）。関数を呼び出す時もコンマ区切りで順番に値を設定すれば対応ができます。

```dart
void main() {
  printMessage('Hello', ' World!');
}

void printMessage(String message1, String message2) {
  print('Message1: $message1\nMessage2: $message2');
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/428867ff-4e03-db1e-1597-e4f5f287bab4.png)

一方で引数名と共に引数の値を指定する方法もあり、こちらはDartだと名前付き引数などと呼ばれるようです（他の言語だとキーワード引数などと呼ばれたりもします）。名前付き引数については次の節で触れていきます。

### 名前付き引数の定義と指定

名前付き引数を使うと関数を呼び出す際に引数の値と共に引数名をセットで指定することができます。

名前付き引数を使うと何が嬉しいのか？というところですが、例えば関数呼び出し箇所で引数名も一緒に記述されるので何の引数なのかが分かりやすいといった面や、引数の順番を変えたりした際に影響を少なくする・・・といった形で可読性を高めたり堅牢なコードを書きたい時に役立つことがあります。

この辺りはDartではなくPythonで以前記事にもしているので必要でしたらご参照ください。

https://qiita.com/simonritchie/items/7d8382e821dd1e9bd98b

名前付き引数を使った関数を定義したい場合には引数の箇所で`{引数内容}`といったように`{}`の括弧で囲む必要があります。また、省略可能な引数以外では引数の前に`required`と付ける必要があります（省略可能な引数に関しては後々の節で触れます）。

名前付き引数を使う形で関数を呼び出す場合には`引数名: 指定する値`といった形でコロンで名前と値を区切って指定します。

```dart:名前付き引数のmessage1とmessage2を使って関数を呼び出している例
void main() {
  printMessage(message1: 'Hello', message2: ' World!');
}

void printMessage({required String message1, required String message2}) {
  print('Message1: $message1\nMessage2: $message2');
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e7575298-529e-6b6a-2a37-762e6b61e030.png)

### 名前付き引数のデフォルト値

名前付き引数を省略可の状態（`required`を付けない形）にするには対象の引数で`null`も設定可能にするか、もしくは`=`の記号を使ってデフォルト値を引数に設定しておく必要があります。

```dart:名前付き引数に?記号を付けてnullを受け付ける形にしたのでrequiredを省ける例
void main() {
  printMessage();
}

void printMessage({String? message}) {
  print('Message: $message');
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/41a53986-c839-208e-b792-4fcf08cc5a0a.png)

デフォルト値を設定する場合には`引数の型 引数名 = デフォルト値`といった形で引数部分で記述します。

```dart:'Hello!'という文字列のデフォルト値を引数に設定しているのでrequiredを省ける例
void main() {
  printMessage();
}

void printMessage({String message = 'Hello!'}) {
  print('Message: $message');
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/3c3b657d-52f2-7759-ed75-cae85207db37.png)

### 返却値の設定

関数に返却値を設定したい場合には関数の処理内で`return 返却値に設定する値`といった形で`return`キーワードを使います。

また、`return`部分で指定した値の型に合わせて関数名の左に記述する型を設定します。

```dart:2つの値を加算した整数を返却する例
void main() {
  int addedValue = addTwoValues(100, 200);
  print(addedValue);
}

int addTwoValues(int a, int b) {
  return a + b;
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/92daf1f5-06b6-c674-2aa0-fc703d8a62ca.png)

### 関数の型設定

関数を別の関数の引数に設定することはDart/Flutterでは結構あります（イベントのハンドラやコールバックなどで）。そういった場合には引数で型の定義が必要になることがありますが、関数の型の定義を含めた引数は位置引数の場合には`返却値の型 Function(引数内容) 引数名`といった形で書きます。例えば返却値の型は`void`、整数の`price`という引数と文字列の`message`という2つの位置引数を持ち、引数名が`otherFunction`という名前の関数の引数を定義したい場合には`void Function(int price, String message) otherFunction`という引数の書き方になります。

※関数呼び出し箇所で無名関数を使っていますが、そちらは後々の節で詳しく触れます。

```dart:myFunction関数の引数でotherFunctionという関数の引数を受け付けている場合
void main() {
  myFunction((int price, String message) => print('$price, $message'));
}

void myFunction(void Function(int price, String message) otherFunction) {
  otherFunction(120, 'Hello');
}
```

位置引数ではなく名前付き引数を設定したい場合には通常の関数定義時と同じように引数に`{}`の括弧で囲ったり必要に応じて`required`の設定やデフォルト値の設定などを行います（例 : `{required int price, required String message}`）。

```dart:名前付き引数を持った関数を引数に設定する例
void main() {
  myFunction(
    ({required int price, required String message}) =>
        print('$price, $message'),
  );
}

void myFunction(
  void Function({required int price, required String message}) otherFunction,
) {
  otherFunction(price: 120, message: 'Hello');
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6ecdc6db-fbd5-d26e-79ac-43c3334d849c.png)

### 無名関数について

Dartで無名関数（関数名が設定されていない関数）を定義するには`{}`の括弧を使う方法と`=>`の記号を使う場合の2パターンが存在します。

通常の関数と`{}`を使う無名関数の場合と`=>`を使う無名関数の場合の句使い分けですが、人によって意見は様々だと思いますが目安程度に個人的には以下のような感じで考えています。

- `=>`の記号を使った無名関数: 1つのステートメントもしくは単一行のシンプルな処理の関数
- `{}`の括弧を使った無名関数: 1行～数行程度のシンプルな処理の関数
- 通常の関数: 上記に該当しない処理が多めな関数など

#### =>を使った無名関数の定義方法

`=>`記号を使って無名関数を定義する場合には`(引数内容) => 単一のステートメントなど`といった形で書きます。`=>`よりも右側の値は返却値としても扱われます。返却値の設定のために`return`などのキーワードは不要です。

例えば整数の`a`と`b`という2つの引数を受け付け、`a`と`b`を加算した値を返却する無名関数を定義したい場合には`(int a, int b) => a + b`といった記述になります。

定義した無名関数は変数として扱ったり、もしくは引数に指定したりして扱うことができます。また、関数の呼び出しも通常の関数のように行うことができます。

```dart
void main() {
  final addTwoValuesFunc = (int a, int b) => a + b;
  int addedValue = addTwoValuesFunc(10, 20);
  print(addedValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4307eda2-6954-c5b2-9614-ec965da6f79a.png)

#### {}の括弧を使った無名関数の定義方法

`{}`の括弧を使って無名関数を定義する場合には`(引数内容) { 関数内容 }`といった形で書きます。こちらでは返却値を設定するには`return`キーワードが必要になります。

例えば整数の`a`と`b`という2つの引数を受け付け、`a`と`b`を加算した値を返却する無名関数を定義したい場合には以下のような書き方になります。

```dart:
void main() {
  final addTwoValuesFunc = (int a, int b) {
    return a + b;
  };
  int addedValue = addTwoValuesFunc(10, 20);
  print(addedValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e5d798c5-8fe0-e508-8dd5-d7bfd0eba49b.png)

#### 無名関数での名前付き引数の利用

無名関数でも名前付き引数を使うことができます。書き方も通常の関数の時と同様で、引数定義の箇所で`{}`の括弧や必要に応じて`required`の記述などを使います。また、呼び出す際も同様で`引数名: 指定する値`といった形で引数を指定して使います。

```dart
void main() {
  final addTwoValuesFunc = ({required int a, required int b}) {
    return a + b;
  };
  int addedValue = addTwoValuesFunc(a: 10, b: 20);
  print(addedValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1702ec6e-6a2b-c8b9-65b2-fb7f36cbb002.png)

#### 無名関数の引数に型の指定を行うかどうか

通常の関数もそうですが、無名関数では引数の型の記述を省略することができます。

```dart:無名関数のa引数とb引数で型を明示していない例
void main() {
  final addTwoValuesFunc = (a, b) {
    return a + b;
  };
  int addedValue = addTwoValuesFunc(10, 20);
  print(addedValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5b4f7e59-c085-a547-94d5-de315e995306.png)

ただしこの場合引数に任意の型の値を受け付けてしまうため、コンパイルエラーにならずに予期せぬランタイムエラーが発生することが起こり得ます。

```dart:各引数が整数と文字列だと関数の内容を処理できなくてランタイムエラーになる例
void main() {
  final addTwoValuesFunc = (a, b) {
    return a + b;
  };
  int addedValue = addTwoValuesFunc(10, 'Hello');
  print(addedValue);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0229ff14-386d-4fda-0384-52ba1a0c0610.png)

そのため前述のような無名関数を定義する場合には引数の型を明示しておいた方が無難だと思います。

一方で無名関数はイベントのハンドラやコールバックとして引数に指定することも多くあります。これらの用途の場合には呼び出す関数側で引数の関数の型が明示されていれば型の不一致などによるランタイムエラーは防げてビルド時点で事前にミスなどを検知することができます。

```dart:otherFunctionという引数側で関数の引数の型を明示している例
void main() {
  myFunction(otherFunction: (message) => print(message));
}

void myFunction({required void Function(String message) otherFunction}) {
  otherFunction('Hello!');
}
```

この場合には無名関数側は型を省略しても安全面で差は少ないですし記述がシンプルになるので積極的に使っても良いと思われる書き方と言えます（Effective Dartなどの資料でもこの辺りに触れられていたような気がします）。

### 関数でのジェネリックな型の利用

関数でジェネリックの型を使う際には`返却値の型 関数名<ジェネリックの型名>(引数内容) { ... }`といった形で書きます。例えば`T`というジェネリックの型名を`myFunction`という関数で使う場合には`void myFunction<T>() { ... }`といったように書きます。

定義したジェネリックの型は返却値や引数の型に設定することができます。

```dart:myFunction関数の返却値と引数にもジェネリックの型を設定している例
void main() {
  final returnedValue = myFunction('Hello!');
  print(returnedValue);
}

T myFunction<T>(T value) {
  return value;
}
```

また、関数を呼び出す箇所でジェネリックの型を明示することもできます。その場合は`呼び出す関数名<ジェネリックで設定したい型>(引数内容)`といったように書きます。

```dart:関数呼び出し箇所で<String>という形で文字列の型を指定している例
void main() {
  final returnedValue = myFunction<String>('Hello!');
  print(returnedValue);
}

T myFunction<T>(T value) {
  return value;
}
```

以下のように呼び出し側でジェネリックの型に指定した型以外が返却値や引数に使われていたらコンパイルエラーになることが確認できます。

```dart:ジェネリックの型にintを指定した一方で返却値と引数が文字列になっていてエラーになる例
void main() {
  String returnedValue = myFunction<int>('Hello!');
  print(returnedValue);
}

T myFunction<T>(T value) {
  return value;
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e12e2d73-96ad-1f6b-20c4-d2a240f43072.png)

## クラスについて

以降の各節でDartのクラスについて詳しく触れていきます。

### クラス定義の基本

Dartでクラスは`class クラス名 { クラス内容 }`といった形で書きます。

なにも内容が定義されていない最低限のクラス定義をすると以下のような感じになります。

```dart
void main() {
  final instance = MyClass();
  print(instance);
}

class MyClass {}
```

### クラスの型設定と初期化

クラスを初期化（インスタンス化）するには`クラス名(引数内容)`といった形で関数と同じように`()`の括弧を使います。

例えばコンストラクタで`id`という名前付き引数を必要とするクラスであれば`MyClass(id: 10)`といった書き方になります（コンストラクタについては後で触れます）。

```dart
void main() {
  final instance = MyClass(id: 10);
  print(instance.id);
}

class MyClass {
  final int id;
  MyClass({required this.id});
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/68261e40-9b90-b015-c016-179de1b85d91.png)

また、定義したクラスは型として他の型と同様に`クラス名 変数など`といった形で指定することができます（例 : `MyClass instance = MyClass()`）。

```dart:instance変数にクラスの型を指定している例
void main() {
  MyClass instance = MyClass(id: 10);
  print(instance.id);
}

class MyClass {
  final int id;
  MyClass({required this.id});
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fa7c06a8-92d2-d29a-9c44-a1cdfeadf37e.png)

### 属性の定義方法と参照

属性を定義するにはクラス内のスコープ（クラス名の後の`{}`の括弧内）で変数を宣言するのと似たような形で定義できます。例えば`id`という整数且つ初期値が0の属性をクラスで定義したい場合にはクラス内のスコープで`int id = 0;`といった記述を追加しておきます。また、定義した属性に関しては`インスタンス名.属性名`といった形でドットで繋いで参照したり更新などを行うことができます。

```dart
void main() {
  MyClass instance = MyClass();
  print(instance.id);

  instance.id = 20;
  print(instance.id);
}

class MyClass {
  int id = 0;
  MyClass();
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b23b232c-399f-f24f-61cb-60bb46bba4bd.png)

コンストラクタの引数で`this.引数名`とすることでコンストラクタに渡された引数の値を直接属性に設定することもできます（例 : `this.id`）。この辺りは後々の節で詳しく触れます。

```dart
void main() {
  MyClass instance = MyClass(20);
  print(instance.id);
}

class MyClass {
  int id;
  MyClass(this.id);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/bc964741-015d-c745-ba31-91e4b09546a4.png)

また、属性定義時に初期値を与えている場合やコンストラクタで属性に値を設定している場合には属性に`final`の設定を付与することができます（後の節で触れる`static`を使うケースを除いてクラスの属性に`const`の属性は使用できません）。

```dart:id属性をfinalで定義している例
void main() {
  MyClass instance = MyClass(20);
  print(instance.id);
}

class MyClass {
  final int id;
  MyClass(this.id);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a383d644-3655-deab-24f2-70d4207c2e3e.png)

### メソッドの書き方と呼び出し方

クラスのスコープ内で関数を定義する形でメソッド（クラスのインスタンスが持つ関数）を定義することができます。

そのメソッドを呼び出したい場合には`インスタンス名.メソッド名(引数内容) { メソッド内容 }`といった形でドットで繋ぐ形で関数呼び出しの時と同じように書きます（例 : `instance.incrementId();`）。

```dart:id属性を1加算するincrementIdメソッドをクラスに定義した例
void main() {
  MyClass instance = MyClass(20);
  instance.incrementId();
  print(instance.id);
}

class MyClass {
  int id;
  MyClass(this.id);

  void incrementId() {
    id++;
  }
}
```

また、クラスの属性や他のメソッドに関してはメソッド内ではそのまま参照します。他の言語であるような`this`や`self`などを経由しません（前記のコードでも`this.id`といった記述ではなく直接`id`属性をメソッド内で参照しています）。

ただし名前が被る際にはこのインスタンスの属性やメソッドということを明示するために`this`を付けて属性やメソッドにアクセスすることもできます（例 : `this.id++;`）。

その他、名前付き引数など関数側で説明してきた機能に関してはクラスのメソッドでも同様に使用することができます。

### staticの属性とメソッドの定義

クラスの属性やメソッドは`static`キーワードを付与して定義することもできます。例えば属性であれば`static int id = 0;`、メソッドであれば`static void incrementId() { ... }`みたいな形で定義します。

`static`のキーワードが付与されたものはその属性へのアクセスやメソッドの呼び出しはインスタンス化した後のインスタンス経由ではなくクラス自体を参照して利用する形が主になります。

例えば`MyClass`というクラスに定義されている`id`という`static`の属性を参照する場合には`MyClass.id`といったように書きます。

```dart:staticのメソッドと属性を利用する例
void main() {
  MyClass.incrementId();
  MyClass.incrementId();
  print(MyClass.id);
}

class MyClass {
  static int id = 0;

  static void incrementId() {
    MyClass.id++;
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/278a104b-1325-5b08-4939-770e92590d2a.png)

また、通常はクラスの定数の属性には`final`しか設定できませんが`static`キーワードを付与した場合には`const`も利用可能になります。

```dart:属性の定数にconstを使う例
void main() {
  print(Language.dart);
}

class Language {
  static const String dart = 'Dart';
  static const String python = 'Python';
  static const String rust = 'rust';
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1665c795-65e4-7a40-2bb0-959c102b281b.png)

### コンストラクタの引数で直接属性に値を設定する書き方

前節までで軽く触れてきましたがコンストラクタの引数を直接属性に設定したい場合には引数定義で`this.引数名`といった形でドット区切りで`this`を付けて定義します（例 : `this.id`）。記述がシンプルになっておすすめです。また、Flutterではウィジェットの引数とかでそもそもこの書き方を使わないと静的解析で警告が出たりすることもあります。

```dart
void main() {
  final instance = MyClass(id: 10, name: 'John');
  print(instance.id);
  print(instance.name);
}

class MyClass {
  final int id;
  final String name;

  MyClass({required this.id, required this.name});
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7ed63f1e-36f4-272a-4627-6e9f72fbafc6.png)

### クラスをconstキーワードと共に初期化する

属性が全て`final`で定義されている場合・・・といった制約がありますが、条件を満たした場合コンストラクタのクラス名の前に`const`キーワードを設定することができます（例 : `const MyClass();`）。

また、コンストラクタの定義で`const`キーワードを付与した場合にはそのクラスのインスタンスも`const`キーワードを使って定義することができます。

`const`で定義することでそのクラスのインスタンス自体を定数化でき堅牢になるのに加えて（そこまで差は大きくないと思いますが）`const`で定義した方がパフォーマンスが良いそうです。

```dart:コンストラクタの定義とインスタンス化でconstキーワードを使用している例
void main() {
  const instance = MyClass(id: 10, name: 'John');
  print(instance.id);
  print(instance.name);
}

class MyClass {
  final int id;
  final String name;

  const MyClass({required this.id, required this.name});
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0957c60b-5e3e-116d-16d4-4a708a52cdef.png)

### コンストラクタの引数で直接親クラスに値を渡す書き方

クラスの親子関係を作るための継承については後々の節で触れますが、親クラスのコンストラクタの引数に直接引数を渡したい場合には`super.親の引数名`といった形で書きます（例 : `super.id`）。

`this`を使った書き方と同様に、こちらの書き方で対応が効く場合には記述がシンプルになります。

```dart:thisを使って子のクラスのコンストラクタで親のコンストラクタに直接引数を渡している例
void main() {
  const instance = ChildClass(id: 10, name: 'John');
  print(instance.id);
  print(instance.name);
}

class ParentClass {
  final int id;
  final String name;

  const ParentClass({required this.id, required this.name});
}

class ChildClass extends ParentClass {
  const ChildClass({required super.id, required super.name});
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4453074a-490f-f247-d37e-0d8490b4d342.png)

### コンストラクタで直接親クラスの引数に引数を一部渡しつつ、別途固定値などを親の引数に渡す書き方

親のクラスのコンストラクタに`super`で直接引数を渡しつつ、一部の値や別の固定値などを子のクラス側で指定する・・・みたいな書き方をすることもできます。

その場合にはコンストラクタの引数の定義の後に` : super(固定値などを指定する残りの親クラスへの引数設定);`といった具合にコロンやメソッドとしての`super`を記述して書きます（例 : `ChildClass({required super.id, required super.name}) : super(language: '日本語')`）。

```dart:id引数とname引数は直接親のクラスの引数に渡し、language引数は別途固定値を指定している例
void main() {
  const instance = ChildClass(id: 10, name: 'John');
  print(instance.id);
  print(instance.name);
}

class ParentClass {
  final int id;
  final String name;
  final String language;

  const ParentClass({
    required this.id,
    required this.name,
    required this.language,
  });
}

class ChildClass extends ParentClass {
  const ChildClass({required super.id, required super.name})
      : super(language: '日本語');
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/03fd0a64-dcc8-7e59-7254-52a30bae20d5.png)

### クラスでのジェネリックな型の利用

クラスでジェネリックの型を使いたい場合にはクラス名の後に`<ジェネリックの型名>`といった形で設定します（例 : `class MyClass<T> { ... }`）。

ジェネリックの型を設定した後は属性や引数などに対象のジェネリックの型を設定することができます（属性での例 : `final T value;`）。

また、そのクラスを使ったインスタンスの変数をの型を定義する際やインスタンス化のタイミングでクラス名の直後に`<ジェネリックに指定する型>`といった形で指定する形で設定する型を明示することができます（例 : `MyClass<int>(value: 20);`）。

```dart
void main() {
  const instance = MyClass<int>(value: 20);
  print(instance.value);
}

class MyClass<T> {
  final T value;

  const MyClass({required this.value});
}
```

複数のジェネリックの型を使いたい場合にはコンマ区切りで型を設定します。

```dart:SとTという2つのジェネリックの型を使う例
void main() {
  const instance = MyClass<int, String>(value1: 20, value2: 'Hello!');
  print(instance.value1);
  print(instance.value2);
}

class MyClass<S, T> {
  final S value1;
  final T value2;

  const MyClass({required this.value1, required this.value2});
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/14362523-c8ca-0cce-b9b9-6fa1b9b84c9d.png)

### getterメソッドの定義

Dartのクラスでgetterメソッド（属性のように値の取得で利用できるメソッド）は対象のメソッド名の前に`get`キーワードを記述することで対応することができます。また、引数は使わないので引数の括弧なども使いません（例 : `int get value { ... }`）。返却値の型が実質的に属性の型のように動作します。

getterを参照する場合には括弧などは使わずに普通の属性のように参照します。

```dart:valueのgetterメソッドに対して属性のようにアクセスしている例
void main() {
  final instance = MyClass();
  print(instance.value);
}

class MyClass {
  int _value = 50;

  int get value {
    return _value;
  }

  MyClass();
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/079dc4bb-bf17-cc1b-fe9e-16b4901e77fd.png)

### setterメソッド定義

setterメソッド（属性のように値の更新で利用できるメソッド）は対象のメソッド名の前に`set`キーワードを記述することで対応することができます。引数は1つの値のみ受け付けます。

値を設定するには属性に値を設定するように`=`で値を指定します（例 : `instance.value = 100;`）。

また、基本的に返却値は`void`相当となるため返却値の型の記述は省略できます。

```dart
void main() {
  final instance = MyClass();
  instance.value = 100;
  print(instance.value);
}

class MyClass {
  int _value = 50;

  int get value {
    return _value;
  }

  set value(int value) {
    _value = value;
  }

  MyClass();
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/8c35e15d-6a95-ebcd-b121-71ad6d4029d6.png)

なお、他の言語と同様にsetterを設けずにgetterのみを設けて値の取得のみを許可して外部からの更新をできなくする・・・といった制御も行うことができます。

### 継承について

親となるクラスを継承して子クラスを作るには`class クラス名 extends 親クラス名 { ... }`といった形で`extends`キーワードを使って書きます。

継承に関しては使い方が微妙だと苦しくなるケースもあるとは思うため気軽に複雑だったり巨大な継承などは控えめになると良いとかはあるかもしれませんが、継承することで親クラスの属性定義やメソッド定義を使いまわすことができるようになります（Flutterでは継承を多用する形となります）。

```dart:Animalという親クラスを継承したCatというクラスを使用している例
void main() {
  final cat = Cat(name: 'タマ', age: 8);
  print(cat.species);
  print(cat.name);
  print(cat.age);
}

class Animal {
  final String species;
  final String name;
  final int age;

  const Animal({
    required this.species,
    required this.name,
    required this.age,
  });
}

class Cat extends Animal {
  const Cat({
    required super.name,
    required super.age,
  }) : super(species: '猫');
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fc35f58d-3f41-bf6e-44e7-31290b0a73b5.png)

#### 継承したメソッドの上書き

継承した親クラスのメソッドなどを上書きしたい場合にはメソッドの上に`@override`と付けて、子のクラス側で同名・同じ型で再度メソッドを定義します。

```dart:walkメソッドを子のCatクラス側で上書きしている例
void main() {
  final cat = Cat(name: 'タマ');
  cat.walk();
  print(cat.xPosition);
}

class Animal {
  final String species;
  final String name;
  int xPosition = 0;

  Animal({
    required this.species,
    required this.name,
  });

  void walk() {
    xPosition += 1;
  }
}

class Cat extends Animal {
  Cat({
    required super.name,
  }) : super(species: '猫');

  @override
  void walk() {
    xPosition += 3;
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/abe0871b-eaee-4b0f-bcc3-4e46169c29fc.png)

## privateでの定義とpublicでの定義

本記事では別ファイルに記述したDartコードの取り扱いまでは触れません（次記事でFlutterとかと併せて触れます）が、Dartではprivate（他のファイル内からは参照できないようにする設定）をしたい場合には変数名・定数名・関数（メソッド）名・クラス名などの先頭に`_`のアンダースコアを付けます（例 : `_id`）。

クラスの属性やメソッドなどに関しては同じファイル内であればアンダースコアを付けていても参照できます（そのクラス内でしか参照できないといったことはありません）。

```dart:変数やクラス・メソッドなどでプライベートの定義を利用した例
late _MyClass _instance;

void main() {
  _instance = _MyClass();
  _instance._printIdAndName();
}

class _MyClass {
  final int _id = 10;
  final String _name = 'John';

  _MyClass();

  void _printIdAndName() {
    print(_id);
    print(_name);
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d842354d-b371-cdc1-cf61-c0e542a546d5.png)

privateで定義したものは対象のファイル内で参照されていなければ警告が出るようになります（Lintとかでチェックされるようにしてあれば使用していないものを切り落としたりなどがしやすくなります）。

```dart:参照していないprivateのものが存在する場合に警告が出る例
late _MyClass _instance;

void main() {
  _instance = _MyClass();
}

class _MyClass {
  final int _id = 10;
  final String _name = 'John';

  _MyClass();

  void _printIdAndName() {
    print(_id);
    print(_name);
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1d548626-7bbe-fbd8-2230-e30f059f0aaf.png)

public（他のファイルから参照できる定義設定）で定義したい場合には前節までのようにアンダースコアを先頭に付けずに各名前を設定します。

## ミックスインについて

通常の継承とは別に、Dartではミックスインをクラスに設定することができます。ミックスインは通常の継承のように特定のクラスに属性やメソッドなどの機能を付与することができます（Rustのトレイトのような機能になります）。

通常の継承と比べて以下のような違いがあります。

- 通常の継承のようにミックスイン自体が他のミックスインやクラスなどを継承することはできません（何階層も継承を重ねるといったことはできません）。
  - ただし特定のクラスの継承やミックスイン設定が無いと対象のミックスインを利用できない制約を付与することはできます。
- 複数のミックスインを特定のクラスに同時に設定することができます。
  - 通常の継承は1度に1つの継承しか行うことはできません。

また、人によって意見は様々だとは思いますがミックスインは継承よりも複雑で変更が効きづらい形にはなりにくく、パーツのように付け外しなどを行うことで機能の追加や削除などの調整が効きやすいので個人的には通常の継承よりも好みではあります（Flutterを扱う上では継承は多用する形にはなりますが・・・）。

### ミックスインの設定の仕方

ミックスインを定義するにはクラスと似たような感じで`mixin ミックスイン名 { ... }`といった形で`mixin`キーワードを使用して定義します。

```dart
mixin WalkMixin {
  int xPosition = 0;

  void walk() {
    xPosition += 1;
  }
}
```

定義したミックスインを特定のクラスで使うためにはクラス名の後などに`with ミックスイン名`といった形で`with`キーワードを使います（通常の継承をしている場合には`extends`での継承の記述の後などに`with`の設定を追加していきます）。

```dart:WalkMixinをAnimalクラスに設定して、Animalクラスでwalkメソッドを利用可能にしている例
void main() {
  final animal = Animal(name: 'タマ');
  animal.walk();
  print(animal.xPosition);
}

mixin WalkMixin {
  int xPosition = 0;

  void walk() {
    xPosition += 1;
  }
}

class Animal with WalkMixin {
  final String name;

  Animal({required this.name});
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/73d1ceef-a4b6-4c77-e693-3025928322ca.png)

### 複数のミックスインを設定する

複数のミックスインを特定のクラスに設定したい場合には`with`の後の指定でコンマ区切りで複数のミックスインを設定することができます。

```dart:新たにRunMixinをAnimalクラスに設定した例
void main() {
  final animal = Animal(name: 'タマ');
  animal.walk();
  print(animal.xPosition);

  animal.run();
  print(animal.xPosition);
}

mixin WalkMixin {
  int xPosition = 0;

  void walk() {
    xPosition += 1;
  }
}

mixin RunMixin {
  int xPosition = 0;

  void run() {
    xPosition += 3;
  }
}

class Animal with WalkMixin, RunMixin {
  final String name;

  Animal({required this.name});
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f66251d1-a9ba-7d12-9db5-7df3bb866d73.png)

### 他の特定のクラスやミックスインを継承していないと使えないミックスインにする

ミックスインを特定のクラスや他のミックスインが設定されていないと使えないようにする・・・といった設定も行うことができます。

これによって、ミックスイン自体は他のクラスやミックスインを継承したりはしていないものの、それらの属性やメソッドをミックスイン内で参照できるようになります（コンパイルが通るようになります）。

そのような設定をしたい場合にはミックスイン名の後に`on 制限で設定したいクラス名やミックスイン名`といった形で`on`のキーワードを使います。

例えば以下の例では`RunMixin`の方は`on`キーワードで`WalkMixin`を設定しているクラスなどにしか設定できない制約を追加してあります。また、`RunMixin`の方には`xPosition`属性を定義していませんが`on`で設定した制約のおかげでコンパイルエラーにならずに対象の属性を参照できています。

```dart
void main() {
  final animal = Animal(name: 'タマ');
  animal.walk();
  print(animal.xPosition);

  animal.run();
  print(animal.xPosition);
}

mixin WalkMixin {
  int xPosition = 0;

  void walk() {
    xPosition += 1;
  }
}

mixin RunMixin on WalkMixin {
  void run() {
    xPosition += 3;
  }
}

class Animal with WalkMixin, RunMixin {
  final String name;

  Animal({required this.name});
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f5251a0d-f74d-a76a-d8aa-4b1d84f2dfad.png)

## インターフェイスについて

Dartでは通常の継承やミックスインの他にもインターフェイス設定も行うことができます。任意のインターフェイスを設定したクラスではインターフェイス側で定義されていたメソッドは必ず`override`で上書きする必要があります。

インターフェイスを使うにはまずインターフェイス用にクラスを定義し、そのインターフェイスを設定したいクラスで`class クラス名 implements インターフェイス名`といった感じで`implements`キーワードを使って設定します。

また、インターフェイスを設定したクラスではインターフェイスのメソッドを一通り`@override`を付けて内容を上書きする必要があります。

```dart
void main() {
  final animal = Animal(name: 'タマ');
  animal.walk();
  print(animal.xPosition);
}

class WalkInterface {
  void walk() {}
}

class Animal implements WalkInterface {
  final String name;
  int xPosition = 0;

  Animal({required this.name});

  @override
  void walk() {
    xPosition += 1;
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/72adf5dc-dceb-a112-0807-1256f155b302.png)

## クラス・ミックスイン・インターフェイスを継承しているかの判定と型のキャスト

インスタンスが特定のクラス・ミックスイン・インターフェイスを継承しているかどうかを判定するには`is`キーワードを使います。例えば`instance is Animal`みたいな書き方をします。結果は真偽値になるため`if`文などの条件分岐で使えます。

また、`is`を使った条件分岐をした場合には型ガードが有効になる場合があり、条件を満たしたスコープ内ではそのクラスなどの属性やメソッドなどにアクセスすることができます。任意の型となる`dynamic`型を使う場合などに型を絞り込んで安全に処理を行うことができます（参照した属性やメソッドが存在せずにエラーになるといったことを避けられます）。

```dart:isで対象がCatクラスのインスタンスかどうかを判定し、安全にwalkメソッドの呼び出しなどを行っている例
void main() {
  final animal = createAnimalInstance();
  if (animal is Cat) {
    animal.walk();
    print(animal.xPosition);
  }
}

dynamic createAnimalInstance() {
  return Cat();
}

class WalkInterface {
  void walk() {}
}

class Cat implements WalkInterface {
  int xPosition = 0;

  @override
  void walk() {
    xPosition += 1;
  }
}
```

dynamic型でisで判定していない、且つ定義されていないメソッドや属性などを参照している場合にはランタイムエラーになります。

```dart:runメソッドはanimalインスタンスで定義されていないのでエラーになる例
void main() {
  final animal = createAnimalInstance();
  animal.run();
  print(animal.xPosition);
}

dynamic createAnimalInstance() {
  return Cat();
}

class WalkInterface {
  void walk() {}
}

class Cat implements WalkInterface {
  int xPosition = 0;

  @override
  void walk() {
    xPosition += 1;
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/776b02b0-16ab-63ac-19f5-2cedae1542fc.png)

もし`is`を使ったif文での条件分岐の型ガードがされていればそのクラスなどで定義されていないメソッドなどを参照していればコンパイルエラーとなるためデプロイ前などに気づけて安全です。

```dart:isを使った条件分岐と型ガードを使っているので定義されていないrunメソッドで事前にコンパイルエラーになる例
void main() {
  final animal = createAnimalInstance();
  if (animal is Cat) {
    animal.run();
    print(animal.xPosition);
  }
}

dynamic createAnimalInstance() {
  return Cat();
}

class WalkInterface {
  void walk() {}
}

class Cat implements WalkInterface {
  int xPosition = 0;

  @override
  void walk() {
    xPosition += 1;
  }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2666051a-f710-96c3-9eee-f1c34b34674b.png)

このisの判定は通常のクラスの継承だけでなくインターフェイスやミックスインなどでも使用できます。

```dart:isの判定でインターフェイスを利用している例
void main() {
  final animal = createAnimalInstance();
  if (animal is WalkInterface) {
    animal.walk();
    print('walkメソッドが実行されました。');
  }
}

dynamic createAnimalInstance() {
  return Cat();
}

class WalkInterface {
  void walk() {}
}

class Cat implements WalkInterface {
  int xPosition = 0;

  @override
  void walk() {
    xPosition += 1;
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/aba1fdf6-07cb-4c12-5133-e6e9ca6cc22d.png)

## enumについて

Dartでenumを定義するには`enum enum名 { コンマ区切りでのenumの各値 }`といった形で定義します。

```dart
enum EquipmentType {
  weapon,
  armor,
  accesory,
}
```

Pythonなどの言語のようにenumの値に任意の値を設定することはできません（`weapon = 10,`といったような右辺の設定はできません）。そういった個別の値も必要になる場合はクラスで`static const 定数名 = ...`といったように`static`キーワードを使って定数を定義して扱います。

定義したenumは引数などの型でも使用することができ、特定のenumの定義のみ受け付ける・・・といった制御が可能です（どんな値を引数に指定すれば良いのかが分かりやすくなります）。

```dart
enum EquipmentType {
  weapon,
  armor,
  accesory,
}

void main() {
  final jpLabel = getEquipmentTypeJpLabel(
    equipmentType: EquipmentType.armor
  );
  print(jpLabel);
}

String getEquipmentTypeJpLabel({
  required EquipmentType equipmentType
}) {
  if (equipmentType == EquipmentType.weapon) {
    return '武器';
  }
  if (equipmentType == EquipmentType.armor) {
    return '防具';
  }
  if (equipmentType == EquipmentType.accesory) {
    return '装飾';
  }

  throw new Error();
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b78e7825-e39d-07a1-ce28-82039d2b00da.png)

### enumをswitchでの条件分岐で使った場合の挙動

`switch`文に関しては後々の節で詳しく触れますが、Dartでenumに対してswitch文で分岐を書く場合、defaultケースを書いていない場合であれば全enumに対して分岐を書かない場合コンパイルエラーになってくれます。

これを利用することでenumの各値に対する分岐を確実に記述するように制約を設けることができます。途中でenumの値を追加したりした時も分岐条件の追加が漏れていればコンパイルエラーで事前に気づくことができます。

```dart:switch文で全てのenumの値の分岐をカバーしている例
enum EquipmentType {
  weapon,
  armor,
  accesory,
}

void main() {
  final jpLabel = getEquipmentTypeJpLabel(
    equipmentType: EquipmentType.armor
  );
  print(jpLabel);
}

String getEquipmentTypeJpLabel({
  required EquipmentType equipmentType
}) {
  switch (equipmentType) {
    case EquipmentType.weapon:
      return '武器';
    case EquipmentType.armor:
      return '防具';
    case EquipmentType.accesory:
      return '装飾';
  }
}
```

`switch`文で一通りのenumの分岐条件が書かれていない場合には`default`ケースが無ければコンパイルエラーになります。

```dart:分岐条件が足りないのでコンパイルエラーになる例
enum EquipmentType {
  weapon,
  armor,
  accesory,
}

void main() {
  final jpLabel = getEquipmentTypeJpLabel(
    equipmentType: EquipmentType.armor
  );
  print(jpLabel);
}

String getEquipmentTypeJpLabel({
  required EquipmentType equipmentType
}) {
  switch (equipmentType) {
    case EquipmentType.weapon:
      return '武器';
    case EquipmentType.armor:
      return '防具';
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/07351e27-7b96-0abd-2a8e-2295dd4d5952.png)

## 比較演算子について

条件分岐について触れる前に、必要となる比較演算子について以降の節で触れていきます。

比較演算子の結果は真偽値（`true`もしくは`false`）となります。条件分岐でも真偽値が必要になるため、比較演算子は条件分岐と共によく使われます。

```dart:等値の比較演算子の結果で真偽値が返却されている例
void main() {
  int age = getAge();
  bool result = age == 17;
  print(result);
}

int getAge() {
  return 17;
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0189eafc-7f5f-cc54-03bf-e294e2054c98.png)

### 値が一致しているかどうかの演算子

値が一致しているかどうかの比較演算子には`==`の記号を使い、その左右に比較したい2つの値を記述します（例 : `age == 17`）。一致していれば値が`true`、一致していなければ`false`となります。

### 値が一致していないかどうかの演算子

値が一致していないかどうかの比較演算子は`!=`の記号を使い、その左右に比較したい2つの値を記述します（例 : `age != 17`）。一致していなければ`true`、一致していれば`false`となります。

```dart
void main() {
  int age = getAge();
  bool result = age != 17;
  print(result);
}

int getAge() {
  return 17;
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d4eaf541-9e80-a207-ae84-fae64e29396d.png)

### 未満の演算子

値が未満かどうかの比較演算子には`<`の記号を使い、その左右に比較したい2つの値を記述します（例 : `age < 17`）。

### 以下の演算子

値が以下かどうかの比較演算子には`<=`の記号を使い、その左右に比較したい2つの値を記述します（例 : `age <= 17`）。

### 超過の演算子

値が大きいかどうか（超過）の比較演算子には`>`の記号を使い、その左右に比較したい2つの値を記述します（例 : `age > 17`）。

### 以上の演算子

値が以上かどうかの比較演算子には`>=`の記号を使い、その左右に比較したい2つの値を記述します（例 : `age >= 17`）。

### AND条件

複数の条件を満たすかどうか（AND条件）を比較演算子での比較で扱う場合には、それぞれの比較の記述の間を`&&`で繋げます。例えば年齢と性別の2つの変数それぞれに対して比較演算子を使ってAND条件で比較したい場合には`age <= 20 && gender == Gender.male`といった形で`&&`で繋げます。

```dart:年齢が20歳以下、且つ性別が男性かどうかを判定する例
enum Gender {
  male,
  female,
}

void main() {
  final age = 17;
  final gender = Gender.male;

  if (age <= 20 && gender == Gender.male) {
    print('年齢は20歳以下で且つ性別は男性です。');
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1275dc69-b88b-6a21-c500-8ce9e1033bde.png)

なお、3つ以上の条件が必要な場合には追加でさらに`&&`で条件を繋げていくことができます。

### OR条件

複数の条件でいずれかの1つを満たすかどうか（OR条件条件）を比較演算子での比較で扱う場合には、それぞれの比較の記述の間を`||`で繋げます。例えば年齢が15歳もしくは17歳のどちらかを満たすかどうかという条件であれば`age == 15 || age == 17`といったように書きます。

```dart:年齢が15歳もしくは17歳かどうかを判定する例
void main() {
  final age = 17;

  if (age == 15 || age == 17) {
    print('年齢は15歳もしくは17歳です。');
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1a3f6a73-fe37-196d-9d2c-6cbc8ab1b698.png)

## 条件分岐について

前節まででも少し使ってきましたが、以降の節ではDartでの条件分岐について触れていきます。

### if文

まずは一番基本的な`if`文からです。

`if`文では条件を満たした場合に特定の処理を実行する場合に利用します。`if (真偽値) { 条件を満たした場合に実行する処理 }`といった具合に書きます。

真偽値の部分はそのまま真偽値を指定する場合や、比較演算子を使った条件式を記述することもあります。

```dart:age変数の値が17の場合のみprint関数を実行する例
void main() {
  final age = 17;
  if (age == 17) {
    print('年齢は17歳です。');
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/28f16463-6c89-b7fb-7ebf-cf357755fdb7.png)

### else if文

`if`文で条件を書いた後に、その条件には当てはまらない場合に別の条件を満たすかどうかを判定したい場合には`else if`文を使います。`if (真偽値) { ... }`の括弧の直後に記述していく必要があります。

`else if (真偽値) { 条件を満たした場合に実行する処理 }`といった形で書きます。

```dart:age変数の値が17ではない場合に追加で15かどうかの判定を行う例
void main() {
  final age = 15;
  if (age == 17) {
    print('年齢は17歳です。');
  } else if (age == 15) {
    print('年齢は15歳です。');
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/9fdd1f68-cb80-d797-08ba-4daa55ada25d.png)

なお、`else if`文は複数設定することができます。

```dart:age変数の値が13かどうかという条件を書き足した例
void main() {
  final age = 13;
  if (age == 17) {
    print('年齢は17歳です。');
  } else if (age == 15) {
    print('年齢は15歳です。');
  } else if (age == 13) {
    print('年齢は13歳です。');
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/9cf57582-c8e8-7a44-4bf0-de2a7344455f.png)

### else文

`if`文で条件を書いた後もしくは`else if`文の条件を書いた後に、それらの全ての条件にも該当しない場合の条件を一括して扱う場合には`else`文を使います。

書き方は`else { 条件を満たした場合に実行する処理 }`といった形になります。直前の各条件を一通り満たさない場合を全て対象とするため、判定用の条件の真偽値は必要ありません。

```dart:else文で各条件を満たさなかった場合を扱っている例
void main() {
  final age = 13;
  if (age == 17) {
    print('年齢は17歳です。');
  } else if (age == 15) {
    print('年齢は15歳です。');
  } else {
    print('年齢は17歳でも15歳でもありません。');
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/430d658c-3fbc-5942-b1c6-4578544c4eab.png)

### switch文

比較の対象値が特定の値かどうかの判定条件（`==`による等値の比較演算子を使うような条件）がたくさんあるときには`else if`文をひたすら記述していくよりも`switch`文を使うと記述がシンプルになります。

書き方としては`switch (対象値の変数など) { 各条件のケース }`といった形で書きます。各条件のケースでは`case 比較で使う値:`といった形で書いてセミコロンの後の行で比較条件を満たした時の処理を書いていきます。対象値と比較で使う値が一致している場合に条件を満たしたと判定されます。

```dart
void main() {
  final age = 13;
  switch (age) {
    case 11:
      print('年齢は12歳です。');
    case 13:
      print('年齢は13歳です。');
    case 15:
      print('年齢は15歳です。');
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/223d5c6b-3912-c090-2f78-46ff9d43b786.png)

#### switch文のデフォルトケース設定

`switch`文では各ケース全てに該当しない場合に実行するためのデフォルトケースを設定することができます。`if`文の`else`と同じような挙動になります。

書き方としては`case`による各ケース設定の最後に`default:`という記述をすることでデフォルトケースを設定することができます。

```dart
void main() {
  final age = 13;

  switch (age) {
    case 15:
      print('年齢は15歳です。');
    case 17:
      print('年齢は17歳です。');
    default:
      print('年齢は15歳でも17歳でもありません。');
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5aca3216-a9ea-53e3-8e5d-2a42a0aea3da.png)

#### switch文で複数の値をケースの条件に設定する

`switch`文の特定のケースで複数の値の条件を満たしている場合の処理を設定したい場合には`||`記号によるOR条件を使うことで対応することができます。

```dart
void main() {
  final age = 17;

  switch (age) {
    case 15 || 17:
      print('年齢は15歳もしくは17歳です。');
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b5171f66-b0d9-9d9d-1339-6d8f4c14b4fb.png)

#### switch文でのbreakについて

他の言語では`switch`文の各ケースの中で複数ケースの条件を満たす場合に複数のケースの処理が実行される場合がありますが、Dart（正確にはDart3系以降）では特定のケースの条件を満たした場合にはそれ以降のケースはチェックされず、必ず最大で1つのケースの処理のみ実行されます（`braek`の記述は必要ありません）。

他の言語だと場合によっては`break`の記述（`switch`文の判定終了用の記述）が無いと想定外な感じで複数のケースの処理が実行されてしまったり、もしくはそもそもコンパイルが通らないといったケースがあると思います。うっかりミスを防ぎつつ、且つ記述もシンプルになるので個人的にはDart3系のこの挙動は好みではあります。

例えば以下の例では1番目のケースと2番目のケースを条件自体は満たす感じになっていますが、最初のケースの処理のみ実行されて「年齢は17歳です。」というメッセージのみ表示されていることを確認できます。

```dart
void main() {
  final age = 17;

  switch (age) {
    case 17:
      print('年齢は17歳です。');
    case 15 || 17:
      print('年齢は15歳もしくは17歳です。');
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e8312e2a-1b9d-b207-8296-b4f62443eafe.png)

#### switch文でのenumの網羅性のチェック

enumの節でも触れましたが、`switch`文でenumを使った場合には網羅性がチェックされます。

例えば以下のように特定の関数などでenumを`switch`文の判定対象にしている場合、enumが全てカバーされていればswitch文の後に`return`キーワードで返却値が設定されていなくともコンパイルエラーにはなりません（必ず`switch`文のどこかのケースを通るといった判定にすることができます）。

```dart
enum EquipmentType {
  weapon,
  armor,
  accesory,
}

void main() {
  final jpLabel = getEquipmentTypeJpLabel(
    equipmentType: EquipmentType.armor
  );
  print(jpLabel);
}

String getEquipmentTypeJpLabel({
  required EquipmentType equipmentType
}) {
  switch (equipmentType) {
    case EquipmentType.weapon:
      return '武器';
    case EquipmentType.armor:
      return '防具';
    case EquipmentType.accesory:
      return '装飾';
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2848d136-03b3-f8f2-03b4-2f74d5460d15.png)

## ループについて

以降の節ではリスト（もしくは他の`Iterable`）や辞書に対するループ処理（繰り返し処理）について触れていきます。

### リストなどに対してインデックスを設定するループを行う

リスト（もしくは他の`Iterable`）のインデックスを参照する形でループを回すには色々と書き方はありますが書き方の一つとして`for (var i = 0; i < リストなど.length; i++) { ループでの処理 }`といった形で書きます（`i`は別の名前でも動きます）。

`var i = 0;`という部分はインデックスを扱うための変数`i`を`0`で初期化するといった処理になります。`0`で初期化しているのでループ中のインデックスを参照してみると`0`からスタートしています。

`i < リストなど.length;`という部分はループを継続する条件です。`length`プロパティでリストなどの要素の件数を参照しているのと`<`記号を使って未満の比較演算子での比較をしているため、インデックス（`i`）がリストの要素の件数未満であればループを行うという条件になります。

`i++`の部分はループが1回分終わったタイミングで行う処理です。`++`でインクリメント（値を1増やす）挙動となるためループが1回終わるたびにインデックスが1加算されるという挙動になります。

以下の例ではリストに3つの要素が格納されているため3回分ループが実行され、`print`関数でのインデックスの出力が0, 1, 2の3つになっていることを確認できます。

```dart
void main() {
  final names = ['John', 'Mike', 'Olivia'];

  for (var i = 0; i < names.length; i++) {
    print(i);
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5a84c69c-913b-c579-1881-002c102b5aa3.png)

インデックスを使用してループ中にリストの各要素を参照したい場合には`対象のリスト[i]`といった形でリストのインデックスを設定すれば対象の要素にアクセスすることができます。

```dart
void main() {
  final names = ['John', 'Mike', 'Olivia'];

  for (var i = 0; i < names.length; i++) {
    print(names[i]);
  }
}
```

### リストなどに対して要素を設定するループを行う

インデックスを参照する必要がなく、直接ループでリストなどの各要素を参照したい場合には`for (final 要素の変数名 in リストなど) { ループ中の処理 }`といった形でシンプルに書くことができます（例 : `for (final name in names) { ... }`）。

```dart
void main() {
  final names = ['John', 'Mike', 'Olivia'];

  for (final name in names) {
    print(name);
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c9ca8e36-7fbe-631d-6561-3007f17f320f.png)

### Map（辞書）のキーを設定するループを行う

`Map`（辞書）の各キーをループで扱いたい場合には`keys`属性を使う形で`for (final キーの変数名 in 対象のMapの変数.keys) { ループ中の処理 }`といった形で書けます（例 : `for (final name in agesMap.keys) { ... }`）。

```dart
void main() {
  final agesMap = {'John': 17, 'Mike': 15, 'Olivia': 13};

  for (final name in agesMap.keys) {
    print(name);
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e961c612-5557-7598-cdb9-3716b61cf17a.png)

### Map（辞書）の値を設定するループを行う

`Map`（辞書）の値をループで扱いたい場合には`values`属性を使う形で`for (final 値の変数名 in 対象のMapの変数.values) { ループ中の処理 }`といった形で書きます（例 : `for (final age in agesMap.values) { ... }`）。

```dart
void main() {
  final agesMap = {'John': 17, 'Mike': 15, 'Olivia': 13};

  for (final age in agesMap.values) {
    print(age);
  }
}
```

### Map（辞書）のキーと値を同時に設定するループを行う

`Map`（辞書）のキーと値を両方ループの中で扱うにはいくつか方法があります。この節では主な方法についていくつか触れていきます。

1つ目は`for`文ではなく`Map`の`forEach`メソッドを使う方法です。メソッドの第一引数に渡される関数の引数にはキーと値の2つの引数が渡されるのとキーの数だけその関数が呼び出されるため`for`文でループを回した時と同じような形でキーと値を参照して処理を行うことができます。

```dart
void main() {
  final agesMap = {'John': 17, 'Mike': 15, 'Olivia': 13};

  agesMap.forEach((key, value) {
    print('key: $key, value: $value');
  });
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/955997b7-fa3b-f498-c703-bc234ac36ac9.png)

2つ目は`Map`の`entries`属性を`for`文で使う方法です。この場合ループ中の変数は`MapEntry`型となり、そちらの`key`と`value`属性でキーと値を参照することができます。

```dart
void main() {
  final agesMap = {'John': 17, 'Mike': 15, 'Olivia': 13};

  for (final mapEntry in agesMap.entries) {
    print('key: ${mapEntry.key}, value: ${mapEntry.value}');
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/cb6af641-8158-abff-da3c-43bffd3367fa.png)

その他、キーに対してループを行ってループの中でそのキーを使用して対象の値を参照するという形もシンプルで良いと思います。

## コメントについて

Dartではいくつかコメントの書き方があるので以降の節ではそれぞれを触れていきます。

### 通常のインラインコメント

通常のインラインコメントは`//`の記号を使います。対象行でこの記号の後の記述はプログラムとして実行されません。主に何らかの処理の説明の補足などに使います。また、基本的にスラッシュの後に半角スペースを1つ入れて使います。

```dart
void main() {
  // いろは歌
  print('いろはにほへとちりぬるを');
}
```

### ドキュメンテーションコメント用のインラインコメント

`///`といった形でスラッシュを3つ重ねるとドキュメンテーションコメントとなります（jsDocやPythonのdocstringのような扱いとなります）。

ドキュメンテーションコメントは主にVS Codeなどのエディタで使ったりドキュメントを出力したりする際に参照されます。

また、ドキュメンテーションコメントはファイル全体（ライブラリファイル対象）、変数、定数、クラス、関数、メソッドなど様々な箇所に設定することができます。

例えば関数に設定すると以下のような雰囲気になります。

```dart
/// メッセージを出力する
///
/// [message]には出力するメッセージを指定する。
void printMessage({required String message}) {
  print(message);
}
```

VS Code上で対象の関数にマウスオーバーしてみると以下のようにドキュメンテーションコメントの内容が表示されることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/789ea2a1-1e6a-ec31-ed97-e4fbfdd428d9.png)

### ブロックコメント

Dartで`/* */`で囲むとブロックコメント（複数行のコメントアウト）となります。一括で処理を無効化できます。主にDartでは一時的に特定部分の処理を止めたりに使います。一方でドキュメントや補足として残すためのコメントは前節までの各インラインコメントを使用します。

```dart
void main() {
  /*print('いろはにほへと');
  print('ちりぬるを');*/
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/288c83a1-7b64-7cde-2cbc-6e25009a1d2a.png)

## 例外について

Dartで例外（ランタイムエラー）を投げたい場合には`throw エラークラス名(引数内容);`といった形で`throw`キーワードを使います（例 : `throw Exception('エラーメッセージ')`）。

※DartPadではランタイムエラーの詳細なエラーメッセージが表示されなかったりしますが、通常のビルド時などにはエラークラスに指定したメッセージなどが確認できるようになっています（VS Code上からビルドした場合など）。

```dart
void main() {
  throw Exception('エラーが発生しました。');
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c6d39dbd-e434-1e56-59d3-6d8df273aaad.png)

※実際に`throw`によるエラー設定を行う場合はより具体的なビルトインのエラークラスを使うか、もしくは継承などして独自に設けたクラスを使うなどが推奨されます。

# 参考サイト・参考文献まとめ

https://gitstar-ranking.com/repositories

https://ja.wikipedia.org/wiki/Flutter

https://ja.wikipedia.org/wiki/Dart

https://inveritasoft.com/blog/flutter-vs-react-native-vs-native-deep-performance-comparison

https://github.com/flutter/flutter

https://zenn.dev/t_fukuyama/articles/5decdea5d9bc8c

https://www.amazon.co.jp/Flutter-Dart-Cookbook-English-Richard-ebook/dp/B0BQ8T32LC/

https://otihateten.hatenablog.com/entry/2023/05/16/233508

https://qiita.com/tez/items/755f340e84e87cfd7609

https://docs.flutter.dev/get-started/install/windows/mobile

https://zenn.dev/knot/articles/flutter-environment

https://zenn.dev/knot/articles/flutter-environment

https://qiita.com/tetsukick/items/cdecf64172c5f4a126d6

https://qiita.com/tetsukick/items/cdecf64172c5f4a126d6

https://zenn.dev/heyhey1028/books/flutter-basics/viewer/getting_started_windows

https://flutter.keicode.com/basics/how-to-create-flutter-project.php

https://pub.dev/

https://pub.dev/packages/flutter_svg

https://qiita.com/tetsukick/items/f3c0744a0b6e40a53cc1

https://dartpad.dev/

https://qiita.com/sileader/items/875f7f1fdc3995ceafda

https://dart.dev/effective-dart/style

https://dart.dev/effective-dart

https://qiita.com/simonritchie/items/daa7c58778660696417e

https://www.cresc.co.jp/tech/java/Google_Dart2/language/built-in_types/built-in_types.html

https://stackoverflow.com/questions/31687376/what-is-the-max-value-of-integer-in-dart

https://api.flutter.dev/flutter/dart-core/BigInt-class.html

https://api.dart.dev/stable/3.3.1/dart-core/double-class.html

https://ja.wikipedia.org/wiki/%E7%AB%AF%E6%95%B0%E5%87%A6%E7%90%86

https://qiita.com/bon127/items/491b25e90208188dafbd

https://zenn.dev/ashphy/articles/c0a7c6fe5e4371

https://ja.wikipedia.org/wiki/Null

https://ja.wikibooks.org/wiki/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0/Null%E5%AE%89%E5%85%A8%E6%80%A7

https://qiita.com/smicle/items/7d3b9881834dc0142fb7

https://qiita.com/simonritchie/items/7d8382e821dd1e9bd98b

https://web.sfc.keio.ac.jp/~minohara/lecture/logical2012/lecturenotes/lecture4.html

https://qiita.com/YoshinoriKanno/items/5093dd53760e34b5f002

https://api.flutter.dev/flutter/dart-core/Exception-class.html

https://stackoverflow.com/questions/54715554/should-i-prefer-to-iterate-over-map-entries-or-map-values