FlutterとDartの違い
Flutterは1つのコードベースから複数の環境向けのアプリを作るためのフレームワークです。コードを書く言語はDartです。SwiftやKotlinを先に覚える必要はありません。この講座ではDartの文法を学んでからFlutterの画面へ進みます。
Flutterを準備する
公式の導入手順で、OSに合ったFlutter SDKとエディターを設定します。VS CodeならFlutter拡張機能を入れます。Chromeもインストールし、ターミナルで次を確認します。Dart SDKはFlutterに同梱されています。
flutter --version
dart --version
flutter doctor
flutter devicesdoctorは開発環境の診断、devicesは実行先の一覧です。Webで試す段階ではChromeが利用可能かを確認します。Android・iOSの診断が未完でも、Webの準備ができていれば本講座を進められます。
flutter create study_app
cd study_app
flutter run -d chromeChromeに生成されたサンプルアプリが表示される終了は実行中のターミナルでqです。以降のFlutter例はstudy_app内の lib/main.dart を全体ごと置き換えます。flutter run -d chrome で起動し、実行中は保存後にrでホットリロード、Rでホットリスタートできます。
プロジェクトの構成
lib/main.dart はアプリの入口、pubspec.yaml は依存パッケージや画像の設定、test/ はテストの置き場です。buildは自動生成される出力なので、そこを編集してもソースの変更にはなりません。
次のDartの基礎例はFlutter画面とは別に試します。study_app直下に basics.dart を作り、各例ごとに全体を置き換えます。
void main() {
print('こんにちは、Dart!');
}dart run basics.dartこんにちは、Dart!Dartのprintはターミナルへ出力します。FlutterのTextは画面へ文字を表示します。ここを区別して読み進めましょう。
Dartの変数と型
var は初期値から型を推論します。final は一度だけ代入でき、const はコンパイル時に決まる定数です。intは整数、doubleは小数、Stringは文字列、boolは真偽値です。文字列には単一・二重引用符を使えます。
void main() {
final String name = 'Mity';
var age = 20;
const double height = 170.5;
final bool isStudent = true;
age += 1;
print('${name}さんは${age}歳です');
print(height);
print(isStudent);
}Mityさんは21歳です
170.5
true$変数名 は識別子の範囲が曖昧になる場合があるため、日本語などへ続けるときは ${name} のように波かっこで囲むと確実です。Dartでは文末にセミコロンを付けます。
nullと型変換
String? は文字列またはnullです。?. は値がある場合だけアクセスし、?? はnullの代替値を指定します。int.tryParse は変換に失敗するとnullを返すため、入力を安全に扱えます。
void main() {
String? nickname;
print(nickname?.length ?? 0);
for (final text in ['20', 'abc']) {
final age = int.tryParse(text);
if (age != null && age >= 0) {
print('来年は${age + 1}歳です');
} else {
print('整数を入力してください');
}
}
}0
来年は21歳です
整数を入力してください末尾の ! はnullでないという強制で、実際にnullなら実行時エラーです。まずifや??で値がない場合を処理しましょう。画面からの入力はフォームの発展ページで扱います。
演算子と条件分岐
/ は小数の割り算、~/ は整数の商、% は余りです。== は比較、&& は両方が真、|| は少なくとも一方が真です。ifの後の丸かっこに条件を書きます。
void main() {
print(7 / 2);
print(7 ~/ 2);
print(7 % 2);
final score = 82;
if (score >= 80) {
print('よくできました');
} else if (score >= 60) {
print('合格');
} else {
print('再挑戦');
}
print(score >= 60 && score <= 100);
print(score == 0 || score == 82);
print(!(score < 60));
}3.5
3
1
よくできました
true
true
true! を真偽値の前に置くと反転になります。nullの強制に使う末尾の!とは位置と役割が異なります。
リストと繰り返し
Listは順番のある値で、位置は0から数えます。add で追加し、length で数を調べます。for-inで順に取り出せます。isNotEmpty は空でないかの確認です。
void main() {
final fruits = <String>['りんご', 'みかん'];
fruits.add('もも');
if (fruits.isNotEmpty) { print(fruits[0]); }
print(fruits.length);
for (final fruit in fruits) { print(fruit); }
var count = 2;
while (count > 0) {
print('残り $count');
count -= 1;
}
}りんご
3
りんご
みかん
もも
残り 2
残り 1回数を指定するforは初期化・条件・更新をセミコロンで区切ります。continueは今回を飛ばし、breakは全体を終了します。
void main() {
for (var number = 1; number <= 5; number += 1) {
if (number == 2) continue;
if (number == 4) break;
print(number);
}
}1
3const ["りんご"] は変更できません。範囲外の位置へのアクセスはRangeErrorになります。Map・Set・変換
Mapはキーと値の対応、Setは重複しない要素を扱います。map は各要素を変換し、toList() で結果をリストにします。(value) => 式 は短い関数の書き方です。
void main() {
final stocks = <String, int>{'りんご': 5, 'みかん': 8};
print(stocks['みかん'] ?? 0);
print(stocks['もも'] ?? 0);
final tags = <String>{'Dart', 'Web', 'Dart'};
print(tags.length);
final doubled = [1, 2, 3].map((value) => value * 2).toList();
print(doubled);
}8
0
2
[2, 4, 6]関数と名前付き引数
関数は戻り値の型、名前、引数の順に書きます。波かっこ内の引数は名前付きです。required は必須、= 1 は省略したときの値です。FlutterのWidget作成でもこの書き方をよく使います。
int total({required int price, int count = 1}) {
return price * count;
}
void main() {
print(total(price: 500, count: 3));
print(total(price: 500));
}1500
500文字列の前後を整えるtrimと、含むか調べるcontainsも試しましょう。処理した結果は戻り値で受け取ります。
void main() {
final text = ' Dart '.trim();
print(text);
print(text.toUpperCase());
print(text.contains('art'));
}Dart
DART
trueクラスとコンストラクター
クラスは値と処理をまとめます。コンストラクターはインスタンス作成時の初期化です。this.name は受け取った引数を同名プロパティへ保存する省略形です。constコンストラクターは、定数として作れる型に使います。
class Product {
final String name;
final int price;
const Product({required this.name, required this.price});
int total(int count) => price * count;
}
void main() {
const pen = Product(name: 'ペン', price: 120);
print(pen.name);
print(pen.total(3));
}ペン
360最初のFlutter画面
ここからはbasics.dartではなくlib/main.dartへ保存し、flutter run -d chrome で実行します。runAppがWidgetを画面の根元に置きます。MaterialAppはアプリの基本設定、Scaffoldは画面の枠組み、Textは文字の部品です。
import 'package:flutter/material.dart';
void main() => runApp(const StudyApp());
class StudyApp extends StatelessWidget {
const StudyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('学習メモ')),
body: const Center(child: Text('こんにちは、Flutter!')),
),
);
}
}上部に「学習メモ」
中央に「こんにちは、Flutter!」extends StatelessWidget は状態を自身で管理しないWidgetを定義します。buildは表示内容を返し、@overrideは親のメソッドを実装する印です。super.key はWidgetを識別するキーを親へ渡します。BuildContextはWidgetツリー上の場所を表します。
縦・横・余白のレイアウト
Column は縦、Row は横に配置します。children は複数の子、child は1つの子です。Paddingは余白、SizedBoxは間隔を作ります。長い文字はExpandedで横幅に合わせて折り返します。
import 'package:flutter/material.dart';
void main() => runApp(const StudyApp());
class StudyApp extends StatelessWidget {
const StudyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('学習メモ')),
body: const Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('今日の目標'),
SizedBox(height: 12),
Row(
children: [
Text('01'),
SizedBox(width: 12),
Expanded(child: Text('Dartの変数と型を復習する')),
],
),
],
),
),
),
);
}
}「今日の目標」の下に「01」と学習内容が横並びになる
狭い画面では学習内容が折り返されるColumnに大量の項目を置いても自動でスクロールしません。長い一覧は次の章のListViewや発展ページで扱います。
一覧を表示する
ListView.builder は必要な行を作ってスクロール表示します。itemCountは件数、itemBuilderのindexは0から始まる位置です。ListTileは1行分の表示に使えます。
import 'package:flutter/material.dart';
void main() => runApp(const StudyApp());
class StudyApp extends StatelessWidget {
const StudyApp({super.key});
@override
Widget build(BuildContext context) {
const lessons = ['変数', '関数', 'Widget'];
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('学習一覧')),
body: ListView.builder(
itemCount: lessons.length,
itemBuilder: (context, index) {
return ListTile(title: Text('${index + 1}: ${lessons[index]}'));
},
),
),
);
}
}「1: 変数」「2: 関数」「3: Widget」が縦に並ぶボタンと状態の更新
押した回数は変化する状態なので、StatefulWidgetとStateに分けます。setState で状態を変更すると画面の再構築が予約され、buildが新しい値で呼ばれます。onPressedへ渡す関数はボタンを押したときに動きます。
import 'package:flutter/material.dart';
void main() => runApp(const StudyApp());
class StudyApp extends StatelessWidget {
const StudyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: CounterPage());
}
}
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('学習カウンター')),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('学習回数: $_count'),
ElevatedButton(
onPressed: () {
setState(() { _count += 1; });
},
child: const Text('1回追加'),
),
],
),
),
);
}
}初期表示は「学習回数: 0」
「1回追加」を1回押すと「学習回数: 1」
もう1回押すと「学習回数: 2」先頭に_が付く名前は同じDartライブラリ内だけから使えます。_countはbuildの外で宣言し、再描画のたびに0へ戻らないようにします。ホットリロードは状態を保つため、0から試すならホットリスタートします。
練習問題
問題1:Dartで[60, 80, 100]の平均を求め、空のリストなら「データなし」と表示します。basics.dartへ保存して試してください。
解答例と結果を確認する
まず自分で書いてから、処理の順番と結果を比べましょう。
void main() {
final scores = <int>[60, 80, 100];
if (scores.isEmpty) {
print('データなし');
} else {
var sum = 0;
for (final score in scores) { sum += score; }
print(sum / scores.length);
}
}80.0問題2:直前のカウンターへ「リセット」ボタンを追加します。2回追加してからリセットすると0になることを確かめてください。次のコードはchildren内の「1回追加」のElevatedButtonの直後へ追加します。
追加するコードと結果
ElevatedButton(
onPressed: () {
setState(() { _count = 0; });
},
child: const Text('リセット'),
),「1回追加」を2回押す → 学習回数: 2
「リセット」を押す → 学習回数: 0次に学ぶこと
基礎の次は、以下の4ページを順に進めましょう。各ページに手順、コード例、確認する結果と練習問題があります。
公式ドキュメントで確認する:Flutterの導入 / Dartの言語入門 / Flutterの学習ガイド
