基礎
Hello World
main()はDartプログラムのエントリポイントです
// Hello World in Dart
void main() {
print('Hello, World!');
}
/* Multi-line
block comment */
/// Documentation comment
/// Supports Markdown
void greet(String name) {
print('Hi, $name');
}コメント
ドキュメントコメントはMarkdownをサポートしています
var name = 'Alice'; // type inferred
String city = 'NYC'; // explicit type
final age = 30; // runtime constant
const PI = 3.14; // compile-time constant
final now = DateTime.now(); // OK: runtime value
// const time = DateTime.now(); // ERROR: not compile-time
const list = [1, 2, 3]; // const list (immutable)
final list2 = [4, 5, 6]; // final ref, mutable contentセミコロン
すべての文はセミコロンで終わる必要があります
int value = 42; // non-nullable
int? nullableValue; // nullable (default null)
print(nullableValue); // null
print(nullableValue ?? 0); // 0 (null coalescing)
nullableValue ??= 10; // assign if null
print(nullableValue); // 10
// int x = null; // ERROR: non-nullable
String name = 'Alice';
print(name.length); // safe, no null check出力
printは自動的に改行を追加します
import 'dart:io';
void main() {
print('Hello'); // stdout with newline
stdout.write('no newline'); // no trailing newline
stderr.writeln('an error'); // stderr with newline
String? input = stdin.readLineSync(); // read a line
int? n = int.tryParse(input ?? ''); // safe parse
print('You entered: $n');
}変数宣言キーワード
var/finalを優先
var x = 10; // inferred as int
var y = 3.14; // inferred as double
var s = 'hi'; // inferred as String
dynamic d = 10; // type can change
d = 'now string'; // OK
// d.foo(); // compiles, may fail at runtime
Object o = 'hello'; // supertype of all non-null types
// o.length; // ERROR: Object has no length
print((o as String).length); // 5 (cast)変数
var型推論
コンパイラが自動的に型を推論します
int a = 42;
double b = 3.14;
num c = 10; // num is supertype of int & double
num d = 2.71;
print(a.bitLength); // 6
print(b.toStringAsFixed(2)); // '3.14'
print(10 ~/ 3); // 3 (integer division)
print(10.remainder(3)); // 1
print(0xFF); // 255 (hex)
print(1.5e3); // 1500.0 (scientific)finalとconst
constはfinalより厳格です
var s1 = 'single';
var s2 = "double";
var s3 = '''multi
line string''';
var name = 'Alice';
print('Hi, $name'); // interpolation
print('Length: ${name.length}'); // expression interpolation
var raw = r'No escape: \n'; // raw string (literal)
var escaped = 'It\'s ok'; // escaped quote
print(s1[0]); // 's' (index access)late変数
遅延初期化、最初の使用前に代入する必要があります
bool isTrue = true;
bool isFalse = false;
print(!isTrue); // false
print(isTrue && isFalse); // false
print(isTrue || isFalse); // true
// Only bool is allowed in conditions; no truthy/falsy
// if (1) {} // ERROR: must be bool
if ('text'.isNotEmpty) {
print('non-empty');
}dynamic型
型チェックを無効にします、注意して使用してください
var list = [1, 2, 3];
var typed = <String>['a', 'b'];
var constList = const [1, 2, 3];
list.add(4);
list.addAll([5, 6]);
print(list.length); // 6
print(list[0]); // 1
print(list.sublist(1, 3)); // [2, 3]
var spread = [...list, 7]; // spread
// constList.add(0); // ERROR: immutable定数コンストラクタ
constコンストラクタを使用してコンパイル時定数を作成
var map = {
'name': 'Alice',
'age': 30,
};
var typed = <String, int>{'a': 1, 'b': 2};
map['city'] = 'NYC'; // add entry
print(map['name']); // Alice
print(map.length); // 3
print(map.containsKey('age')); // true
map.forEach((k, v) => print('$k: $v'));
var keys = map.keys.toList(); // [name, age, city]Sets & Runes
Sets are unordered collections of unique elements—useful for deduplication and set operations (union, intersection, difference). Runes expose the Unicode code points of a string, which is needed for emoji and non-BMP characters stored as surrogate pairs in UTF-16.
var set = {1, 2, 3};
set.add(2); // no duplicate added
set.add(4);
print(set); // {1, 2, 3, 4}
print(set.contains(2)); // true
print(set.intersection({2, 3, 5})); // {2, 3}
// Runes (Unicode code points)
var heart = '♥'; // ♥
print(heart); // ♥
print('A'.codeUnitAt(0)); // 65データ型
数値型
numはintとdoubleのスーパータイプです
print(5 + 3); // 8
print(5 - 3); // 2
print(5 * 3); // 15
print(5 / 3); // 1.6666... (double)
print(5 ~/ 3); // 1 (integer division)
print(5 % 3); // 2 (modulo)
print(-(5)); // -5 (unary minus)
print(2.toString()); // '2'文字列
単一引用符、二重引用符、三重引用符をサポート
var i = 5;
print(i++); // 5 (postfix: use, then add)
print(i); // 6
print(++i); // 7 (prefix: add, then use)
print(i--); // 7
print(--i); // 5ブール型
trueとfalseの値のみ
print(3 == 3); // true
print(3 != 4); // true
print(3 < 4); // true
print(3 > 4); // false
print(3 <= 3); // true
print(3 >= 4); // false
print('a' == 'a'); // true (content equality)
var l1 = [1, 2];
var l2 = [1, 2];
print(l1 == l2); // false (reference equality)List
他の言語の配列に似ています
bool a = true, b = false;
print(a && b); // false
print(a || b); // true
print(!a); // false
// short-circuit evaluation
bool check() { print('called'); return true; }
false && check(); // check() NOT called
true || check(); // check() NOT calledMap
キーと値のペアのコレクション
Object x = 'hello';
print(x is String); // true
print(x is! int); // true
if (x is String) {
print(x.length); // smart-cast to String
}
Object y = 42;
print((y as int) + 1); // 43 (cast)
// (y as String); // runtime TypeErrorSet
一意の要素の順序なしコレクション
class Builder {
String? name;
int? size;
Builder setName(String n) { name = n; return this; }
Builder setSize(int s) { size = s; return this; }
}
var b = Builder()
..setName('widget')
..setSize(10);
print(b.name); // widget
String? path;
print(path?.length); // null (safe access)
print(path?.length ?? 0); // 0演算子
算術演算子
~/はDartの整数除算演算子です
int score = 85;
if (score >= 90) {
print('A');
} else if (score >= 80) {
print('B');
} else {
print('C');
}
// ternary expression
var grade = score >= 60 ? 'pass' : 'fail';
print(grade); // pass
// if (score) {} // ERROR: condition must be boolインクリメントとデクリメント
前置:演算後に代入、後置:代入後に演算
for (var i = 0; i < 3; i++) {
print(i);
}
var list = ['a', 'b', 'c'];
for (var item in list) {
print(item);
}
// for-in with Map entries
var map = {'x': 1, 'y': 2};
for (var entry in map.entries) {
print('${entry.key}: ${entry.value}');
}型テスト演算子
asは型キャストを行います
var i = 0;
while (i < 3) {
print('while $i');
i++;
}
var j = 0;
do {
print('do $j');
j++;
} while (j < 3);条件式
??はnull合体演算子です
// Classic switch statement
var color = 'red';
switch (color) {
case 'red':
print('stop');
break;
case 'green':
print('go');
break;
default:
print('unknown');
}
// Dart 3 switch expression
String describe(int n) => switch (n) {
0 => 'zero',
1 || 2 => 'small',
>= 3 && <= 10 => 'medium',
_ => 'large',
};
print(describe(5)); // mediumカスケード演算子
..はオブジェクト自体を返す連鎖呼び出しを可能にします
for (var i = 0; i < 5; i++) {
if (i == 2) continue; // skip 2
if (i == 4) break; // stop at 4
print(i); // 0, 1, 3
}
// labels for nested loops
outer:
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (i + j > 2) break outer;
print('$i,$j');
}
}assert
assert(condition, message) checks a condition during development. Assertions are enabled in debug mode and removed in production (release) builds. Use them for internal invariants and debugging—not for input validation that must run in production.
void setAge(int age) {
assert(age >= 0, 'age must be non-negative');
// ...
}
assert(1 == 1); // passes in debug
// assert(1 == 2, 'math is broken'); // fails in debug
// Assertions are stripped in release/production builds制御フロー
if-else
条件はbool型である必要があります
// named function with return type
int add(int a, int b) {
return a + b;
}
// functions are first-class objects
int Function(int) makeAdder(int n) {
return (int x) => x + n;
}
var add5 = makeAdder(5);
print(add5(3)); // 8
print(add(2, 3)); // 5forループ
Cスタイルとfor-inをサポート
// single-expression function with =>
int square(int x) => x * x;
String greet(String name) => 'Hi, $name';
// arrow with nullable
String? firstChar(String? s) => s?.isEmpty ?? true ? null : s[0];
print(square(4)); // 16
print(greet('Al')); // Hi, Al
print(firstChar('hi')); // hwhileループ
do-whileは少なくとも1回実行されます
// optional positional params wrapped in []
String greet(String name, [String? title]) {
if (title != null) {
return 'Hello, $title $name';
}
return 'Hello, $name';
}
print(greet('Alice')); // Hello, Alice
print(greet('Bob', 'Dr.')); // Hello, Dr. Bobswitch
Dart 3はswitch式をサポートしています
// named params wrapped in {}, required marks mandatory
void createUser({
required String name,
int age = 0,
String? email,
}) {
print('$name, $age, $email');
}
createUser(name: 'Alice', age: 30);
createUser(name: 'Bob', email: '[email protected]');
// named params are passed by name, order-independentbreakとcontinue
breakはループを終了し、continueはこの反復をスキップします
// default values for optional positional
double calc(double a, [double b = 1.0, double c = 0.0]) {
return a * b + c;
}
// default values for named
void config({String host = 'localhost', int port = 8080}) {
print('$host:$port');
}
print(calc(5)); // 5.0
print(calc(5, 2)); // 10.0
config(port: 3000); // localhost:3000Anonymous Functions & Closures
Anonymous functions (lambdas) have no name and are often assigned to variables or passed as callbacks. Closures capture variables from their enclosing scope and keep them alive. The parameter list can be typed (int a, int b) or untyped (a, b).
// anonymous function assigned to a variable
var multiply = (int a, int b) => a * b;
print(multiply(3, 4)); // 12
// closure capturing a variable
Function counter() {
int count = 0;
return () => ++count;
}
var c = counter();
print(c()); // 1
print(c()); // 2
// used as callbacks
[1, 2, 3].forEach((n) => print(n));関数
関数宣言
Dartでは、関数は第一級オブジェクトです
class Person {
String name;
int age;
// constructor
Person(this.name, this.age);
void greet() {
print('Hi, I am $name');
}
}
var p = Person('Alice', 30);
p.greet(); // Hi, I am Alice
print(p.name); // Aliceアロー関数
=>は単一式の関数に使用されます
class Point {
double x;
double y;
Point(this.x, this.y);
double distanceTo(Point other) {
return ((x - other.x) * (x - other.x) +
(y - other.y) * (y - other.y));
}
void moveBy(double dx, double dy) {
this.x += dx; // 'this' is optional when unambiguous
y += dy;
}
}オプションパラメータ
[]は位置指定オプションパラメータを囲みます
class Rectangle {
double width, height;
Rectangle(this.width, this.height);
// computed getter
double get area => width * height;
set size(double v) {
width = v;
height = v;
}
}
var r = Rectangle(3, 4);
print(r.area); // 12 (accessed like a field)
r.size = 10;
print(r.area); // 100名前付きパラメータ
{}は名前付きパラメータを囲み、requiredは必須を示します
class MathUtils {
static const double PI = 3.14159;
static double circleArea(double r) => PI * r * r;
}
// accessed via the class, not an instance
print(MathUtils.PI); // 3.14159
print(MathUtils.circleArea(2)); // 12.566
// static members belong to the class, not instancesデフォルト値
オプションパラメータはデフォルト値を持てます
class Logger {
final String name;
static final Map<String, Logger> _cache = {};
// factory may return a cached instance
factory Logger(String name) {
return _cache.putIfAbsent(name, () => Logger._internal(name));
}
Logger._internal(this.name);
}
var a = Logger('app');
var b = Logger('app');
print(identical(a, b)); // true (same cached instance)匿名関数
匿名関数はコールバックとしてよく使用されます
class Book {
String title;
Book(this.title);
@override
String toString() => 'Book($title)';
@override
bool operator ==(Object other) =>
other is Book && other.title == title;
@override
int get hashCode => title.hashCode;
}
print(Book('Dart')); // Book(Dart)
print(Book('A') == Book('A')); // trueクラス
クラス定義
コンストラクタはクラスと同じ名前を持ちます
class Animal {
String species;
// generative constructor
Animal(this.species);
// if no constructor is written, Dart provides:
// Animal() : species = 'unknown';
}
var a = Animal('cat');
print(a.species); // cat名前付きコンストラクタ
クラスは複数の名前付きコンストラクタを持てます
class Point {
double x, y;
Point(this.x, this.y);
// named constructor
Point.origin() : x = 0, y = 0;
Point.fromList(List<double> l) : x = l[0], y = l[1];
}
var p1 = Point.origin();
var p2 = Point.fromList([3, 4]);
print('${p1.x},${p1.y}'); // 0.0,0.0
print('${p2.x},${p2.y}'); // 3.0,4.0ゲッターとセッター
get/setキーワードを使用
class Temperature {
final double celsius;
// initializer list runs before body
Temperature(double c) : celsius = c;
Temperature.fromFahrenheit(double f)
: celsius = (f - 32) * 5 / 9;
// assert in initializer list
Temperature.clamped(double c)
: assert(c >= -273.15),
celsius = c < -273.15 ? -273.15 : c;
}
print(Temperature.fromFahrenheit(32).celsius); // 0.0静的メンバ
staticメンバはクラスに属し、インスタンスには属しません
class Point {
double x, y;
Point(this.x, this.y);
// redirect to another constructor with 'this'
Point.alongX(double x) : this(x, 0);
Point.origin() : this(0, 0);
Point.fromDouble(double n) : this.alongX(n);
}
print(Point.alongX(5).y); // 0.0
print(Point.origin().x); // 0.0ファクトリコンストラクタ
factoryは常に新しいインスタンスを作成するとは限りません
class ImmutablePoint {
final double x;
final double y;
// const constructor: creates compile-time constant instances
const ImmutablePoint(this.x, this.y);
static const origin = ImmutablePoint(0, 0);
}
const p = ImmutablePoint(1, 2);
const o = ImmutablePoint.origin;
print(identical(o, ImmutablePoint(0, 0))); // trueFactory & Caching
Factory constructors can return const or cached instances and may use Dart 3 switch expressions. They differ from generative constructors which always create a new instance. Use factories when construction logic must choose what to return.
class Shape {
final String type;
const Shape._(this.type);
factory Shape(String kind) {
return switch (kind) {
'circle' => const Shape._('circle'),
'square' => const Shape._('square'),
_ => const Shape._('unknown'),
};
}
}
print(Shape('circle').type); // circle
print(identical(Shape('circle'), Shape('circle'))); // true継承
extends継承
Dartは単一継承です
class Animal {
String name;
Animal(this.name);
void speak() => print('$name makes a sound');
}
class Dog extends Animal {
Dog(String name) : super(name);
@override
void speak() => print('$name barks');
}
var d = Dog('Rex');
d.speak(); // Rex barkssuper呼び出し
@overrideはメソッドのオーバーライドを示します
class Vehicle {
int speed = 0;
void accelerate(int by) => speed += by;
void describe() => print('Vehicle at $speed');
}
class Car extends Vehicle {
@override
void accelerate(int by) {
super.accelerate(by); // call parent method
print('Car now at $speed');
}
}
Car().accelerate(10); // Car now at 10抽象クラス
抽象クラスはインスタンス化できません
class Base {
String tag;
Base(this.tag) {
print('Base created: $tag');
}
}
class Derived extends Base {
Derived(String tag) : super(tag) {
print('Derived created');
}
}
// order: super initializer -> super body -> derived body
Derived('x');
// Base created: x
// Derived createdインターフェース実装
すべてのクラスは暗黙的にインターフェースを定義します
class Proxy implements Object {
@override
dynamic noSuchMethod(Invocation inv) {
print('Called: ${inv.memberName}');
return null;
}
}
var p = Proxy();
p.someMissingMethod(); // Called: Symbol("someMissingMethod")noSuchMethod
存在しないメソッドへの呼び出しを処理します
sealed class Shape {
const Shape();
}
class Circle extends Shape {
final double r;
const Circle(this.r);
}
class Square extends Shape {
final double s;
const Square(this.s);
}
// switch is exhaustive over sealed subtypes
double area(Shape s) => switch (s) {
Circle(:var r) => 3.14 * r * r,
Square(:var s) => s * s,
};
print(area(const Circle(2))); // 12.56Mixin
Mixinの定義
mixinはコンスト ラクタを持てません
abstract class Animal {
// abstract method: no body, must be overridden
void makeSound();
// concrete method: inherited as-is
void breathe() => print('breathing');
}
class Cat extends Animal {
@override
void makeSound() => print('meow');
}
var c = Cat();
c.makeSound(); // meow
// Animal(); // ERROR: cannot instantiate abstract classMixinの使用
withキーワードを使用して複数のmixinを使用
class Television {
void turnOn() => print('on');
void turnOff() => print('off');
}
// every class implicitly defines an interface
class SmartTV implements Television {
@override
void turnOn() => print('smart on');
@override
void turnOff() => print('smart off');
}
SmartTV().turnOn(); // smart onmixin制約
onはmixinを特定のクラスに制約します
abstract class Flyer {
void fly();
}
abstract class Swimmer {
void swim();
}
// implement multiple interfaces
class Duck implements Flyer, Swimmer {
@override
void fly() => print('flying');
@override
void swim() => print('swimming');
}
Duck().fly(); // flying
Duck().swim(); // swimmingmixin class
Dart 3はmixin classをサポートしています
class Base {
void greet() => print('hello');
void wave() => print('waving');
}
// extends: reuse implementation, single parent
class A extends Base {
@override
void greet() => print('A says hi');
}
// implements: contract only, must override all
class B implements Base {
@override
void greet() => print('B says hi');
@override
void wave() => print('B waving');
}Abstract vs Interface Guidelines
Use abstract classes to share implementation among closely related types (inheritance of code). Use interfaces (abstract classes used via implements) to define capabilities or contracts that unrelated types can fulfill. Dart merges these concepts: an abstract class can serve as both.
// Abstract class: share code among related types
abstract class Repository<T> {
T? find(int id); // abstract
void save(T item) => print('saved'); // shared
}
// Interface: define a capability
abstract class Comparable<T> {
int compareTo(T other);
}
class Product extends Repository<Product>
implements Comparable<Product> {
@override
Product? find(int id) => null;
@override
int compareTo(Product other) => 0;
}Async/Await
async関数
asyncは非同期関数を示し、Futureを返します
mixin Greeter {
String get name;
void greet() => print('Hello, $name!');
}
class User with Greeter {
@override
String name;
User(this.name);
}
User('Alice').greet(); // Hello, Alice!await
awaitはasync関数内でのみ使用できます
mixin Walker {
void walk() => print('walking');
}
mixin Talker {
void talk() => print('talking');
}
// mix in multiple mixins
class Person extends Object with Walker, Talker {
String name;
Person(this.name);
}
var p = Person('Bob');
p.walk(); // walking
p.talk(); // talkingtry-catch非同期
非同期エラーはtry-catchで捕捉します
mixin Musician on Performer {
void playNote() => print('playing note');
}
abstract class Performer {
void perform();
}
class Singer extends Performer with Musician {
@override
void perform() => print('singing');
}
Singer().playNote(); // playing note
// Musician can only be mixed into Performer subtypesFuture.wait並列
複数のFutureを並列実行
// 'mixin class' can be both extended and mixed in
mixin class Counter {
int _count = 0;
int get count => _count;
void increment() => _count++;
}
class App extends Counter {}
class Tool with Counter {}
print(App().count); // 0
App().increment();
Tool().increment();
print(App().count); // 0 (separate instance)async forループ
await forはStreamを消費します
mixin A {
void hello() => print('A');
}
mixin B {
void hello() => print('B');
}
class X with A, B {}
class Y with B, A {}
X().hello(); // B (later mixin wins)
Y().hello(); // A (later mixin wins)
// resolution order: class -> last mixin -> ... -> first mixinStream
Streamの作成
async*はStreamを作成し、yieldは値を送出します
class Stack<T> {
final List<T> _items = [];
void push(T item) => _items.add(item);
T pop() => _items.removeLast();
bool get isEmpty => _items.isEmpty;
}
var s = Stack<int>();
s.push(1);
s.push(2);
print(s.pop()); // 2
var names = Stack<String>();
names.push('Al');Streamのリッスン
listenはStreamSubscriptionを返します
// generic method with its own type parameter
T firstOrDefault<T>(List<T> list, T defaultValue) {
return list.isEmpty ? defaultValue : list.first;
}
print(firstOrDefault<int>([1, 2, 3], 0)); // 1
print(firstOrDefault([], 'none')); // none (type inferred)
// type often inferred from argumentsStreamメソッド
Streamは様々な便利なメソッドを提供します
class Comparable<T> {
int compareTo(T other);
}
// constrain T to subtypes of Comparable<T>
T max<T extends Comparable<T>>(T a, T b) {
return a.compareTo(b) >= 0 ? a : b;
}
// number sum constraint
num sum<T extends num>(List<T> nums) =>
nums.fold(0, (a, b) => a + b);
print(sum([1, 2.5, 3])); // 6.5StreamController
Streamのデータフローを手動制御
List<int> nums = [1, 2, 3];
Map<String, int> scores = {'a': 1, 'b': 2};
Set<double> uniq = {1.1, 2.2, 1.1};
// runtime type checks work (reified generics)
print(nums is List<int>); // true
print(nums is List<String>); // false
print(scores is Map); // true
// generic variance
void process(List<num> list) => print(list);
process(nums); // OK: int is a numStream変換
Listの連鎖操作に似ています
// generic function type alias
typedef Transformer<T> = T Function(T input);
int doubler(int x) => x * 2;
String upper(String s) => s.toUpperCase();
Transformer<int> dt = doubler;
Transformer<String> ut = upper;
print(dt(5)); // 10
print(ut('hi')); // HI
// generic class alias
typedef IntList = List<int>;
IntList xs = [1, 2, 3];Future
Futureの作成
Futureは非同期結果を表します
var nums = [3, 1, 2];
nums.sort();
print(nums); // [1, 2, 3]
print(nums.reversed.toList()); // [3, 2, 1]
print(nums.indexOf(2)); // 1
print(nums.contains(3));// true
print(nums.where((n) => n > 1).toList()); // [2, 3]
print(nums.map((n) => n * 2).toList()); // [2, 4, 6]
print(nums.fold(0, (a, b) => a + b)); // 6thenチェーン
thenは新しいFutureを返します
var a = {1, 2, 3};
var b = {2, 3, 4};
print(a.union(b)); // {1, 2, 3, 4}
print(a.intersection(b)); // {2, 3}
print(a.difference(b)); // {1}
var dedup = [1, 1, 2, 3, 3].toSet();
print(dedup.toList()); // [1, 2, 3]
print(dedup.contains(2)); // trueFuture.delayed
遅延実行
var ages = {'Alice': 30, 'Bob': 25};
ages['Carol'] = 28;
print(ages.keys); // (Alice, Bob, Carol)
print(ages.values); // (30, 25, 28)
print(ages.length); // 3
ages.update('Bob', (v) => v + 1);
print(ages['Bob']); // 26
ages.remove('Alice');
ages.forEach((k, v) => print('$k=$v'));
var mapped = ages.map((k, v) => MapEntry(k, v + 100));Future.any
最初に完了したFutureの結果を返します
var a = [1, 2];
var b = [0, ...a, 3]; // [0, 1, 2, 3]
print(b);
List<int>? maybe;
var c = [0, ...?maybe, 4]; // [0, 4] (null-spread is safe)
print(c);
var m1 = {'a': 1};
var m2 = {'b': 2, ...m1}; // {b: 2, a: 1}
print(m2);Completer
Futureを手動で完了
var promo = true;
var menu = [
'home',
'products',
if (promo) 'sale',
'about',
];
print(menu); // [home, products, sale, about]
var nums = [1, 2, 3];
var doubled = [
for (var n in nums) n * 2,
];
print(doubled); // [2, 4, 6]
// combine: [for (var x in xs) if (x > 0) x]Higher-order Methods
Lists/Iterables support any, every, firstWhere, reduce, fold, skip, take, expand, and more. fold is powerful—it carries an accumulator of any type. These higher-order methods enable declarative, functional-style data processing without explicit loops.
var nums = [1, 2, 3, 4, 5];
print(nums.any((n) => n > 4)); // true
print(nums.every((n) => n > 0)); // true
print(nums.firstWhere((n) => n > 2)); // 3
print(nums.reduce((a, b) => a + b)); // 15
var byParity = nums.fold(<bool, List<int>>{}, (m, n) {
m[n.isOdd] = [...?m[n.isOdd], n]; return m;
});
print(byParity); // {true: [1,3,5], false: [2,4]}コレクション
List操作
Listは重複を許す順序付きコレクションです
Future<String> fetchUser() {
return Future.delayed(Duration(seconds: 1), () => 'Alice');
}
void main() {
fetchUser().then((name) {
print('Got: $name'); // Got: Alice (after 1s)
});
print('waiting...');
}Set操作
Setは一意の要素の順序なしコレクションです
Future<String> fetchUser() async {
await Future.delayed(Duration(seconds: 1));
return 'Alice';
}
Future<void> main() async {
print('start');
String name = await fetchUser();
print('Got: $name');
print('done');
}Map操作
Mapはキーと値のペアのコレクションです
Future<int> divide(int a, int b) async {
if (b == 0) throw Exception('divide by zero');
return a ~/ b;
}
Future<void> main() async {
try {
var result = await divide(10, 0);
print(result);
} catch (e) {
print('Error: $e'); // Error: Exception: divide by zero
} finally {
print('done');
}
}コレクションスプレッド
...スプレッド演算子
Future<int> compute() async => 42;
compute()
.then((v) => v * 2)
.then((v) => print(v)) // 84
.catchError((e) => print('err: $e'))
.whenComplete(() => print('cleanup'));
// chaining transforms the result type
Future<String> fetch() async => 'data';
fetch().then((s) => s.length).then(print); // 4collection-ifとcollection-for
Dart固有のコレクション内条件/ループ
Future<int> task(int n) async {
await Future.delayed(Duration(milliseconds: n));
return n;
}
// run in parallel, wait for all
var all = await Future.wait([task(100), task(50), task(200)]);
print(all); // [100, 50, 200]
// resolve with the first to complete
var first = await Future.any([task(100), task(50), task(200)]);
print(first); // 50 (fastest)Completer
A Completer lets you manually create and complete a Future. Call complete(value) or completeError(error) to finish it. Completers are useful when wrapping callback-based APIs into Futures, or when a Future's completion is triggered by an external event you control.
import 'dart:async';
Completer<String> completer = Completer<String>();
// complete the future from elsewhere
Future<String> get value => completer.future;
completer.complete('resolved!');
void main() async {
print(await value); // resolved!
}
// useful when bridging callback-based APIs to Futures文字列メソッド
文字列補間
$variableまたは${expression}
Stream<int> countDown(int from) async* {
while (from > 0) {
await Future.delayed(Duration(seconds: 1));
yield from;
from--;
}
}
void main() async {
await for (var n in countDown(3)) {
print(n); // 3, 2, 1 (one per second)
}
}一般的なメソッド
文字列は不変です
var sub = countDown(3).listen(
(n) => print('got $n'),
onDone: () => print('done'),
onError: (e) => print('err: $e'),
);
// pause/resume/cancel
sub.pause();
sub.resume();
// sub.cancel(); // stop listening
Stream<int> countDown(int from) async* {
while (from > 0) yield from--;
}部分文字列
インデックスは0から始まります
var stream = Stream.fromIterable([1, 2, 3, 4]);
// transform like an Iterable
var evens = stream.where((n) => n.isEven);
var doubled = stream.map((n) => n * 2);
await for (var n in Stream.fromIterable([1,2,3]).map((n) => n * 10)) {
print(n); // 10, 20, 30
}
print(await stream.first); // 1
print(await stream.last); // 4
print(await stream.length); // 4置換と分割
正規表現置換をサポート
import 'dart:async';
var controller = StreamController<int>();
// add events manually
controller.add(1);
controller.add(2);
controller.addError('oops');
controller.close();
controller.stream.listen(
(n) => print(n), // 1, 2
onError: (e) => print(e), // oops
onDone: () => print('done'),
);
// use controller.addError/sink.add for errorsStringBuilder
重い 連結にはStringBufferを使用
// await for consumes a stream like a loop
Future<int> sumStream(Stream<int> s) async {
var total = 0;
await for (var n in s) {
total += n;
}
return total;
}
print(await sumStream(Stream.fromIterable([1, 2, 3]))); // 6
// broadcast stream: multiple listeners
var bc = StreamController<int>.broadcast();
bc.stream.listen(print);
bc.stream.listen((n) => print('got $n'));
bc.add(5); // both listeners receive 5例外処理
throw
任意のオブジェクトをスローできます
void checkAge(int age) {
if (age < 0) {
throw ArgumentError('age cannot be negative');
}
if (age > 150) {
throw StateError('unrealistic age: $age');
}
}
// you can throw any non-null object
void fail() => throw 'something went wrong';try-catch-finally
onは特定の例外型を捕捉します
try {
checkAge(-5);
} on ArgumentError catch (e) {
print('argument error: $e');
} on StateError catch (e) {
print('state error: $e');
} catch (e, stackTrace) {
print('unknown: $e');
print(stackTrace);
} finally {
print('always runs');
}カスタム例外
Exceptionインターフェースを実装
class InvalidCredentialsException implements Exception {
final String message;
InvalidCredentialsException(this.message);
@override
String toString() => 'InvalidCredentialsException: $message';
}
void login(String user, String pass) {
if (user.isEmpty) {
throw InvalidCredentialsException('username required');
}
}
try {
login('', 'x');
} on InvalidCredentialsException catch (e) {
print(e); // InvalidCredentialsException: username required
}rethrow
rethrowは例外を再スローします
Future<void> logErrors(Future<void> Function() action) async {
try {
await action();
} catch (e) {
print('logging error: $e');
rethrow; // re-throw the caught exception
}
}
void main() async {
try {
await logErrors(() async => throw Exception('fail'));
} catch (e) {
print('handled upstream: $e');
}
}Error vs Exception
Exception is for runtime conditions a program can reasonably catch and recover from (e.g., network failure, bad input). Error represents programming bugs (type errors, assertion failures, index out of range) that should be fixed in code, not caught at runtime. Catching Errors is discouraged.
// Exception: recoverable, expected to be caught
class MyException implements Exception {}
// Error: programming bugs, not meant to be caught
class MyError extends Error {
@override
String toString() => 'MyError: invalid state';
}
void risky() {
throw MyError(); // bug: should fix the code
throw MyException(); // runtime condition: catch it
}
// assert failures, type errors, range errors are Errors列挙型
基本の列挙型
列挙型の値はnameとindexを持ちます
// alias for a function type
typedef IntOperator = int Function(int, int);
int add(int a, int b) => a + b;
int mul(int a, int b) => a * b;
IntOperator op = add;
print(op(2, 3)); // 5
op = mul;
print(op(2, 3)); // 6
// pass as a parameter
void apply(IntOperator f, int a, int b) => print(f(a, b));拡張列挙型(Dart 3)
列挙型はフィールドとメソッドを持てます
// generic function type alias
typedef Mapper<T, R> = R Function(T input);
String stringify(int n) => n.toString();
int lenOf(String s) => s.length;
Mapper<int, String> intToStr = stringify;
Mapper<String, int> strToLen = lenOf;
print(intToStr(42)); // '42'
print(strToLen('hi')); // 2列挙型の反復
valuesはすべての列挙型の値を返します
// Dart 2.13+: alias for any type, not just functions
typedef IntList = List<int>;
typedef StringMap<V> = Map<String, V>;
IntList nums = [1, 2, 3];
StringMap<int> scores = {'a': 1};
// alias for a record type (Dart 3)
typedef Point = ({double x, double y});
Point p = (x: 1.0, y: 2.0);
print(p.x); // 1.0switch列挙型
Dart 3はbreakを必要としません
typedef Predicate<T> = bool Function(T);
bool isEven(int n) => n.isEven;
List<T> filter<T>(List<T> list, Predicate<T> test) {
return list.where(test).toList();
}
print(filter([1, 2, 3, 4], isEven)); // [2, 4]
print(filter(['', 'a', ''], (s) => s.isNotEmpty)); // [a]
// typedef makes callback contracts explicittypedef vs inline Function types
A typedef is just an alias—it's identical to the inline function type at runtime and for type checking. typedef improves readability and centralizes the contract so changes happen in one place. Prefer typedef for any function type used in more than one location.
// these two are equivalent
typedef Handler = void Function(String event);
class EventBus {
// using typedef
void on(Handler handler) {}
// using inline type
void on2(void Function(String) handler) {}
}
// both accept the same functions
void myHandler(String e) => print(e);
EventBus().on(myHandler);
EventBus().on2(myHandler);ジェネリクス
ジェネリッククラス
Tは型パラメータです
enum Color { red, green, blue }
var c = Color.red;
print(c); // Color.red
print(c.name); // 'red'
print(c.index); // 0
print(Color.values); // [Color.red, Color.green, Color.blue]
print(Color.green.index); // 1ジェネリックメソッド
メソッドも型パラメータを持てます
enum Vehicle {
car('Car', 4),
bike('Bike', 2),
truck('Truck', 6);
final String label;
final int wheels;
const Vehicle(this.label, this.wheels);
int get axles => wheels ~/ 2;
}
print(Vehicle.car.label); // Car
print(Vehicle.bike.wheels); // 2
print(Vehicle.truck.axles); // 3ジェネリック制約
extendsは型パラメータを制約します
enum Status { pending, active, done }
// iterate all values
for (var s in Status.values) {
print(s.name);
}
// exhaustive switch (no default needed)
String label(Status s) => switch (s) {
Status.pending => 'Waiting',
Status.active => 'Running',
Status.done => 'Finished',
};
print(label(Status.active)); // Runningジェネリックコレクション
コレクションはジェネリクスを広く使用します
enum HttpStatus {
ok(200),
notFound(404),
serverError(500);
final int code;
const HttpStatus(this.code);
bool get isSuccess => code >= 200 && code < 300;
String get reason => switch (this) {
ok => 'OK',
notFound => 'Not Found',
serverError => 'Internal Server Error',
};
}
print(HttpStatus.ok.isSuccess); // true
print(HttpStatus.notFound.reason); // Not FoundEnum Comparison
Each enum value is a singleton—there's exactly one instance per value per program. == compares by identity effectively. index allows ordering by declaration position. Use == for equality; identical() also works since values are canonicalized. Enums make great Map keys and Set elements.
enum Priority { low, medium, high }
var a = Priority.low;
var b = Priority.high;
print(a == b); // false
print(a == Priority.low);// true
print(a.index < b.index);// true
print(identical(a, Priority.low)); // true
// enums are singletons: only one instance per value
// use == for equality, not identical (though both work)Typedef
関数型エイリアス
関数型のエイリアスを作成
class Animal {
@override
String toString() => 'Animal';
@Deprecated('use newName instead')
String oldName = 'x';
String newName = 'x';
@protected
void internalMethod() {}
@visibleForTesting
String testHook() => 'test';
}ジェネリックtypedef
ジェネリックパラメータをサポート
class Base {
void greet() {}
String name = 'base';
}
class Derived extends Base {
@override
void greet() => print('hi');
// @override verifies the parent method exists
// typo here would be a compile error:
// @override void greeet() {}
}
// @Deprecated emits a warning at the call site
@Deprecated('use bar()')
void foo() {}
void bar() {}新スタイルtypedef
Dart 2.13+は非関数型エイリアスをサポート
// a custom annotation is just a const constructor class
class Todo {
final String msg;
const Todo(this.msg);
}
class Service {
@Todo('refactor to use cache')
void fetchData() {}
@Todo('add tests')
void process() {}
}
// annotations are accessed via dart:mirrors (VM) or
// code generation (build_runner) in practice@immutable & @JsonSerializable
@immutable (from package:meta) marks a class whose instances should not change after construction; subclasses and fields should be final. @JsonSerializable (from json_serializable) triggers code generation for JSON conversion via build_runner. Annotations drive many Dart/Flutter ecosystems.
import 'package:meta/meta.dart';
// requires the 'meta' package
@immutable
class User {
final String name;
final int age;
const User(this.name, this.age);
}
// with package:json_annotation / json_serializable
// @JsonSerializable()
// class Product {
// final String id;
// Product(this.id);
// factory Product.fromJson(Map<String, dynamic> j) => ...;
// }Annotation on Parameters
Annotations can be placed on parameters, library declarations, and typedefs too. Before null safety, @required marked mandatory named params; the modern equivalent is the 'required' keyword. Annotations on parameters are widely used by serialization and DI frameworks.
class Required {
const Required([this.reason]);
final String? reason;
}
class Service {
// annotation on a parameter
void create({
@Required('name is mandatory') String? name,
@protected int? internal,
}) {
print(name);
}
}
// @required is built into Dart (the 'required' keyword
// is preferred in Dart 2.12+ for null safety)Null安全
Null許容型
?はNull許容型を示します
// import an entire library
import 'dart:io';
import 'package:http/http.dart';
// import only specific names
import 'dart:math' show Random, pi;
// hide specific names
import 'dart:async' hide Timer;
// prefix to avoid name clashes
import 'package:http/http.dart' as http;
http.get(Uri.parse('https://example.com'));Nullアサーション
!は非Nullをアサートします、注意して使用
// library.dart
library my_lib;
// split implementation across files
part 'src/widget_a.dart';
part 'src/widget_b.dart';
// re-export another library's API
export 'src/utils.dart' show formatDate, parseDate;
// users import library.dart and get everythingNull安全演算子
?. ?? ??= Null安全操作
// names starting with _ are library-private
class _Internal {
void _helper() {}
}
class Public {
String _secret = 'hidden'; // private field
String name = 'visible'; // public field
String _process() => 'internal';
String reveal() => _process();
}
// _secret is accessible anywhere in the SAME library/file
// but not from other libraries that import this file型プロモーション
Nullチェック後の自動型プロモーション
// load a library on demand (web only)
import 'package:heavy_lib/heavy.dart' deferred as heavy;
Future<void> main() async {
// library is NOT loaded until this call
await heavy.loadLibrary();
heavy.SomeClass().doWork();
}
// useful for splitting large web bundles and
// loading rarely-used features only when neededlateとnull
lateは非Null変数の初期化を遅延させます
// explicit library declaration
library my_package.utils;
import 'dart:math';
part 'src/helper.dart';
const double version = 1.0;
// a 'library' name is optional in modern Dart;
// it's mainly used with part/part-of and tooling
// most files omit it and are treated as anonymous librariespart & part of
part/part-of splits one library across files: the main file declares part 'file.dart'; the part file declares 'part of library;'. Parts share the library's scope including private (_name) members. Prefer separate libraries with export for new code—parts are for tightly-coupled implementations.
// shapes.dart
library shapes;
part 'circle.dart';
part 'square.dart';
class Shape {}
// circle.dart
part of shapes;
class Circle extends Shape {}
// files in the same library share private members
// (_name visible across all parts)拡張
拡張メソッド
既存の型に機能を追加
int a = 42; // non-nullable: cannot be null
int? b; // nullable: can be null
print(b); // null
b = 10;
print(b); // 10
String name = 'Al'; // non-nullable
String? middle; // nullable
// non-nullable types are guaranteed non-null
// int x = null; // ERROR
// print(a.length); // safe: a is non-null拡張の使用
通常のメソッドのように呼び出し
String? maybeName;
// int len = maybeName.length; // ERROR: maybeName is nullable
maybeName = 'Alice';
int len = maybeName!.length; // ! asserts non-null
print(len); // 5
// throws if null at runtime:
// String? n; print(n!.length); // NoSuchMethodError/nullジェネリック拡張
ジェネリック型パラメータをサポート
String? name;
print(name?.length); // null (safe access)
print(name?.length ?? 0);// 0 (default if null)
name = 'Alice';
print(name?.length); // 5
name ??= 'Bob'; // assign only if null
print(name); // Alice (already set)
List<int>? list;
print(list?.first); // null
print(list?.first ?? -1);// -1Type Promotion
Type promotion: after a null check (x != null) or type check (x is String), the compiler narrows the type within that branch—no explicit cast needed. Promotion also happens after a non-null assignment. Local variables promote well; fields may need explicit local copies.
String? name;
if (name != null) {
// name is promoted to non-nullable String here
print(name.length); // safe, no ! needed
}
// promoted via is check
Object obj = 'hello';
if (obj is String) {
print(obj.length); // smart-cast to String
}
// promoted via assignment
int? x;
x = 5;
print(x.abs()); // x is non-null after assignmentlate & required
late marks a non-nullable variable that will be initialized after declaration but before first use—deferring initialization. late final initializes once (lazily if given an initializer). required marks a named parameter as mandatory. Together they integrate cleanly with null safety.
class Config {
// late: non-nullable, initialized later
late final String value = _load();
// late without initializer: assign before first use
late final int computed;
Config() {
computed = expensive();
}
String _load() => 'loaded';
int expensive() => 42;
}
// required: mandatory named parameter
void build({required String name}) {}
build(name: 'Al'); // OK
// build(); // ERROR: missing requiredlate Lazy Initialization
A late field with an initializer is lazy—the initializer runs on first access, not at construction. The result is cached for subsequent accesses. This is great for expensive initialization, circular references, and fields that depend on 'this' being fully constructed. late final makes it a one-time computation.
class Service {
// lazy: _expensive runs only on first access
late final int cache = _expensive();
int _expensive() {
print('computing...');
return 42;
}
}
var s = Service();
print('created');
print(s.cache); // computing... 42
print(s.cache); // 42 (cached, no recompute)
// late fields with initializers are evaluated lazily関連する Dart スニペット
Copy-paste ready code for common tasks.
Was this helpful?