Dart

#19
TIOBE#28
PYPL#16
GitHub#27
RedMonk#19
IEEESpectrum#19
JetBrains#15
プログラミング言語FlutterGoogleモバイル開発クロスプラットフォームWeb開発

プログラミング言語

Dart

概要

DartはGoogleが開発したプログラミング言語で、Flutterフレームワークと組み合わせてクロスプラットフォームモバイルアプリ開発で使用されます。

詳細

Dartは2011年にGoogleによって発表されたオブジェクト指向プログラミング言語です。当初はJavaScriptの代替としてWeb開発向けに設計されましたが、現在はFlutterフレームワークを通じてモバイルアプリ開発の分野で大きな成功を収めています。DartはC風の構文を持ちながら、型安全性と開発効率性を重視した設計となっています。ガベージコレクション、非同期プログラミングサポート、型推論など、モダンな言語機能を備えています。Flutterの普及により、単一のコードベースでiOS、Android、Web、デスクトップアプリケーションを開発することが可能となり、開発コストの大幅な削減を実現しています。Hot Reload機能により、コード変更が即座にアプリに反映されるため、開発効率が向上します。

書き方の例

Hello World

// 基本的な出力
void main() {
  print('Hello, World!');
}

// 複数の値を出力
void helloMultiple() {
  print('Hello, Dart!');
  print('Welcome to Flutter development!');
}

// 変数を使った出力
void helloWithVariables() {
  String message = 'こんにちは、Dart!';
  String name = '太郎';
  int age = 25;
  
  print(message);
  print('私の名前は$nameで、$age歳です。');
  
  // 式の埋め込み
  print('来年は${age + 1}歳になります。');
}

// 関数を使った例
String createGreeting(String name, int age) {
  return '私の名前は$nameで、$age歳です。';
}

void main() {
  print('Hello, World!');
  helloMultiple();
  helloWithVariables();
  
  String greeting = createGreeting('山田', 30);
  print(greeting);
}

// エントリーポイントの例
void main() {
  print('Dartプログラムが開始されました');
  
  // 非同期処理の例
  fetchData().then((data) {
    print('データ: $data');
  });
  
  print('メイン処理終了');
}

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 1));
  return 'サンプルデータ';
}

変数とデータ型

// 変数宣言
void main() {
  // var - 型推論
  var name = '田中太郎';        // String
  var age = 25;              // int
  var height = 175.5;        // double
  var isStudent = true;      // bool
  
  // 明示的な型指定
  String explicitString = 'Hello';
  int explicitInt = 42;
  double explicitDouble = 3.14;
  bool explicitBool = false;
  
  // dynamic - 実行時に型が決定
  dynamic dynamicValue = 'はじめは文字列';
  print(dynamicValue);
  dynamicValue = 123;
  print(dynamicValue);
  dynamicValue = true;
  print(dynamicValue);
  
  // Object - すべての型の基底クラス
  Object objectValue = 'オブジェクト型';
  print(objectValue);
  
  // 定数
  const String constantString = 'コンパイル時定数';
  final String finalString = '実行時定数';
  final currentTime = DateTime.now(); // 実行時に決定
  
  // Null許容型
  String? nullableString = null;
  int? nullableInt;
  
  // Null安全性
  String nonNullableString = 'Null許容しない';
  // nonNullableString = null; // コンパイルエラー
  
  // Null合体演算子
  String defaultValue = nullableString ?? 'デフォルト値';
  
  // 数値型
  int integer = 42;
  double floating = 3.14159;
  num number = 123; // intとdoubleの親クラス
  
  // 数値リテラル
  int binary = 0b1010;      // 二進数
  int octal = 0o755;        // 八進数
  int hexadecimal = 0xFF;   // 十六進数
  double scientific = 1.25e2; // 指数表記
  
  // 文字列
  String singleQuote = 'シングルクォート';
  String doubleQuote = "ダブルクォート";
  String multiLine = '''
  複数行の
  文字列を
  記述できます
  ''';
  
  // 文字列補間
  String interpolation = '名前: $name, 年齢: $age';
  String expression = '来年は${age + 1}歳です';
  
  // Raw文字列(エスケープ文字を無視)
  String rawString = r'これは\nエスケープされません\t';
  
  // リスト(配列)
  List<String> fruits = ['りんご', 'バナナ', 'オレンジ'];
  List<int> numbers = [1, 2, 3, 4, 5];
  var mixedList = ['文字列', 123, true]; // List<Object>
  
  // リストの操作
  fruits.add('いちご');
  fruits.insert(0, 'ぶどう');
  fruits.remove('バナナ');
  
  // 固定長リスト
  List<int> fixedList = List.filled(3, 0); // [0, 0, 0]
  
  // セット(重複なし)
  Set<String> uniqueFruits = {'りんご', 'バナナ', 'オレンジ'};
  uniqueFruits.add('りんご'); // 重複は追加されない
  
  // マップ(辞書)
  Map<String, int> scores = {
    '数学': 85,
    '英語': 92,
    '理科': 78
  };
  
  // マップの操作
  scores['社会'] = 80;
  scores.remove('理科');
  
  // 型のキャスト
  Object obj = 'これは文字列です';
  String str = obj as String;
  
  // 型チェック
  if (obj is String) {
    print('objは文字列型です: $obj');
  }
  
  // 型チェック(否定)
  if (obj is! int) {
    print('objは整数型ではありません');
  }
  
  print('=== 変数とデータ型の例 ===');
  print('名前: $name');
  print('年齢: $age');
  print('身長: $height');
  print('学生: $isStudent');
  print('デフォルト値: $defaultValue');
  print('果物: $fruits');
  print('重複なし果物: $uniqueFruits');
  print('点数: $scores');
  print('Raw文字列: $rawString');
  print('型情報: ${name.runtimeType}');
}

関数と制御構造

// 基本的な関数
int add(int a, int b) {
  return a + b;
}

// 単一式の関数(アロー関数)
int multiply(int a, int b) => a * b;

// 名前付き引数
String greet({required String name, int age = 0, String? hometown}) {
  String message = 'こんにちは、$name さん!';
  if (age > 0) {
    message += ' $age 歳ですね。';
  }
  if (hometown != null) {
    message += ' $hometown 出身なんですね。';
  }
  return message;
}

// 位置引数(オプション)
String createMessage(String name, [int? age, String? city]) {
  String message = '名前: $name';
  if (age != null) message += ', 年齢: $age';
  if (city != null) message += ', 都市: $city';
  return message;
}

// 可変長引数
int sum(List<int> numbers) {
  return numbers.fold(0, (prev, element) => prev + element);
}

// 高階関数
List<T> processlist<T>(List<T> items, T Function(T) processor) {
  return items.map(processor).toList();
}

// ジェネリック関数
T getFirst<T>(List<T> items) {
  if (items.isEmpty) {
    throw ArgumentError('リストが空です');
  }
  return items.first;
}

// 非同期関数
Future<String> fetchUserData(int userId) async {
  // ネットワーク処理をシミュレート
  await Future.delayed(Duration(seconds: 1));
  return 'ユーザー$userId のデータ';
}

// Stream関数
Stream<int> countDown(int start) async* {
  for (int i = start; i >= 0; i--) {
    await Future.delayed(Duration(milliseconds: 500));
    yield i;
  }
}

void main() async {
  print('=== 関数の例 ===');
  
  // 基本的な関数呼び出し
  print('足し算: ${add(5, 3)}');
  print('掛け算: ${multiply(4, 6)}');
  
  // 名前付き引数
  print(greet(name: '田中', age: 25));
  print(greet(name: '山田', hometown: '東京'));
  
  // 位置引数
  print(createMessage('佐藤'));
  print(createMessage('高橋', 30));
  print(createMessage('渡辺', 28, '大阪'));
  
  // 可変長引数
  print('合計: ${sum([1, 2, 3, 4, 5])}');
  
  // 高階関数
  List<int> numbers = [1, 2, 3, 4, 5];
  List<int> doubled = processlist(numbers, (x) => x * 2);
  print('倍数: $doubled');
  
  // ジェネリック関数
  print('最初の果物: ${getFirst(['りんご', 'バナナ', 'オレンジ'])}');
  print('最初の数値: ${getFirst([10, 20, 30])}');
  
  // 非同期処理
  try {
    String userData = await fetchUserData(123);
    print('取得データ: $userData');
  } catch (e) {
    print('エラー: $e');
  }
  
  // Stream処理
  print('カウントダウン開始:');
  await for (int count in countDown(5)) {
    print('カウント: $count');
  }
  
  // 制御構造
  demonstrateControlFlow();
}

void demonstrateControlFlow() {
  print('\n=== 制御構造の例 ===');
  
  // if-else文
  int score = 85;
  if (score >= 90) {
    print('優秀です!');
  } else if (score >= 70) {
    print('良好です。');
  } else {
    print('もう少し頑張りましょう。');
  }
  
  // 三項演算子
  String result = score >= 60 ? '合格' : '不合格';
  print('結果: $result');
  
  // switch文
  String grade = 'A';
  switch (grade) {
    case 'A':
      print('優秀');
      break;
    case 'B':
      print('良好');
      break;
    case 'C':
      print('普通');
      break;
    default:
      print('不明な成績');
  }
  
  // for文
  print('for文:');
  for (int i = 0; i < 5; i++) {
    print('  $i');
  }
  
  // for-in文
  List<String> colors = ['赤', '青', '緑'];
  print('for-in文:');
  for (String color in colors) {
    print('  色: $color');
  }
  
  // forEach
  print('forEach:');
  colors.forEach((color) => print('  色: $color'));
  
  // while文
  print('while文:');
  int count = 0;
  while (count < 3) {
    print('  カウント: $count');
    count++;
  }
  
  // do-while文
  print('do-while文:');
  int doCount = 0;
  do {
    print('  do-カウント: $doCount');
    doCount++;
  } while (doCount < 3);
  
  // continue とbreak
  print('continue と break:');
  for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) continue; // 偶数をスキップ
    if (i > 7) break;         // 7より大きい場合は終了
    print('  奇数: $i');
  }
  
  // try-catch-finally
  try {
    int divisionResult = 10 ~/ 0; // 整数除算でゼロ除算
    print(divisionResult);
  } catch (e) {
    print('エラーが発生しました: $e');
  } finally {
    print('cleanup処理が実行されました');
  }
  
  // assert文(デバッグ時のみ有効)
  int value = 5;
  assert(value > 0, '値は正の数である必要があります');
  print('assertチェック完了');
}

クラスとオブジェクト指向

// 基本的なクラス
class Person {
  // プロパティ
  String name;
  int age;
  String? email; // Null許容プロパティ
  
  // コンストラクタ
  Person(this.name, this.age, [this.email]);
  
  // 名前付きコンストラクタ
  Person.withEmail(this.name, this.age, this.email);
  
  Person.unknown() : this('Unknown', 0);
  
  // メソッド
  String introduce() {
    return '私の名前は$nameで、$age歳です。';
  }
  
  void haveBirthday() {
    age++;
    print('$nameさん、${age}歳のお誕生日おめでとう!');
  }
  
  // Getter
  String get description => '$name ($age歳)';
  
  // Setter
  set userEmail(String? newEmail) {
    if (newEmail != null && newEmail.contains('@')) {
      email = newEmail;
    } else {
      print('無効なメールアドレスです');
    }
  }
  
  // toString オーバーライド
  @override
  String toString() {
    return 'Person{name: $name, age: $age, email: $email}';
  }
}

// 継承
class Student extends Person {
  String school;
  List<String> subjects;
  
  Student(String name, int age, this.school, this.subjects) 
      : super(name, age);
  
  // メソッドのオーバーライド
  @override
  String introduce() {
    return '${super.introduce()} ${school}に通っています。';
  }
  
  void study(String subject) {
    if (subjects.contains(subject)) {
      print('$nameは$subjectを勉強しています。');
    } else {
      print('$subjectは履修していません。');
    }
  }
  
  // 追加のgetter
  String get academicInfo => '$school - 履修科目: ${subjects.join(', ')}';
}

// 抽象クラス
abstract class Vehicle {
  String brand;
  String model;
  
  Vehicle(this.brand, this.model);
  
  // 抽象メソッド
  void start();
  void stop();
  
  // 具象メソッド
  String getInfo() => '$brand $model';
}

// インターフェース(抽象クラスとして実装)
abstract class Flyable {
  void fly();
  double get maxAltitude;
}

// 複数のクラス/インターフェースからの継承
class Car extends Vehicle {
  int numberOfDoors;
  
  Car(String brand, String model, this.numberOfDoors) 
      : super(brand, model);
  
  @override
  void start() {
    print('$brand $model のエンジンを始動しました');
  }
  
  @override
  void stop() {
    print('$brand $model のエンジンを停止しました');
  }
}

class Airplane extends Vehicle implements Flyable {
  @override
  double maxAltitude = 12000.0; // メートル
  
  Airplane(String brand, String model) : super(brand, model);
  
  @override
  void start() {
    print('$brand $model の航空機エンジンを始動');
  }
  
  @override
  void stop() {
    print('$brand $model の航空機エンジンを停止');
  }
  
  @override
  void fly() {
    print('$brand $model が最高高度${maxAltitude}mで飛行中');
  }
}

// Mixin
mixin Swimmer {
  void swim() {
    print('泳いでいます');
  }
  
  double get swimSpeed => 5.0; // km/h
}

mixin Walker {
  void walk() {
    print('歩いています');
  }
  
  double get walkSpeed => 4.0; // km/h
}

// Mixinを使ったクラス
class Amphibian with Swimmer, Walker {
  String name;
  
  Amphibian(this.name);
  
  void move() {
    print('$name が移動します:');
    walk();
    swim();
    print('歩行速度: ${walkSpeed}km/h, 水泳速度: ${swimSpeed}km/h');
  }
}

// 列挙型
enum Color {
  red,
  green,
  blue,
  yellow;
  
  // 列挙型のメソッド
  String get displayName {
    switch (this) {
      case Color.red:
        return '赤';
      case Color.green:
        return '緑';
      case Color.blue:
        return '青';
      case Color.yellow:
        return '黄';
    }
  }
}

// 拡張列挙型(Enhanced Enums)
enum Planet {
  mercury(3.303e+23, 2.4397e6),
  venus(4.869e+24, 6.0518e6),
  earth(5.976e+24, 6.37814e6);
  
  const Planet(this.mass, this.radius);
  
  final double mass;           // kg
  final double radius;         // meters
  
  double get surfaceGravity => 6.67300E-11 * mass / (radius * radius);
}

void main() {
  print('=== クラスとオブジェクト指向の例 ===');
  
  // 基本的なクラスの使用
  Person person1 = Person('田中太郎', 25, '[email protected]');
  print(person1.introduce());
  print('説明: ${person1.description}');
  
  person1.haveBirthday();
  print(person1);
  
  // 名前付きコンストラクタ
  Person person2 = Person.withEmail('山田花子', 30, '[email protected]');
  Person person3 = Person.unknown();
  
  // Setter使用
  person3.userEmail = '[email protected]';
  print('person3のメール: ${person3.email}');
  
  // 継承の例
  Student student = Student('佐藤一郎', 20, '東京大学', ['数学', '物理学', '化学']);
  print(student.introduce());
  print('学術情報: ${student.academicInfo}');
  student.study('数学');
  student.study('英語');
  
  // 抽象クラスと継承
  Car car = Car('Toyota', 'Prius', 4);
  print('車の情報: ${car.getInfo()}');
  car.start();
  car.stop();
  
  Airplane airplane = Airplane('Boeing', '747');
  print('航空機の情報: ${airplane.getInfo()}');
  airplane.start();
  airplane.fly();
  airplane.stop();
  
  // Mixinの例
  Amphibian frog = Amphibian('カエル');
  frog.move();
  
  // 列挙型の例
  Color favoriteColor = Color.blue;
  print('好きな色: ${favoriteColor.displayName}');
  
  // すべての色を表示
  for (Color color in Color.values) {
    print('色: ${color.name} (${color.displayName})');
  }
  
  // 拡張列挙型の例
  Planet earth = Planet.earth;
  print('地球の質量: ${earth.mass} kg');
  print('地球の半径: ${earth.radius} m');
  print('地球の表面重力: ${earth.surfaceGravity.toStringAsFixed(2)} m/s²');
  
  // 型チェック
  if (student is Person) {
    print('studentはPersonのインスタンスです');
  }
  
  if (airplane is Flyable) {
    print('airplaneは飛行可能です');
  }
}

非同期プログラミング

import 'dart:async';
import 'dart:math';

// Future を返す非同期関数
Future<String> fetchDataFromAPI(String endpoint) async {
  print('API呼び出し開始: $endpoint');
  
  // ネットワーク遅延をシミュレート
  await Future.delayed(Duration(seconds: 2));
  
  // ランダムで成功/失敗を決定
  if (Random().nextBool()) {
    return 'データ取得成功: $endpoint からのデータ';
  } else {
    throw Exception('$endpoint からのデータ取得に失敗');
  }
}

// 複数の非同期処理を並行実行
Future<List<String>> fetchMultipleData() async {
  List<Future<String>> futures = [
    fetchDataFromAPI('users'),
    fetchDataFromAPI('posts'),
    fetchDataFromAPI('comments'),
  ];
  
  try {
    // 全ての処理が完了するまで待機
    List<String> results = await Future.wait(futures);
    return results;
  } catch (e) {
    print('並行処理中にエラー: $e');
    rethrow;
  }
}

// タイムアウト付きの非同期処理
Future<String> fetchWithTimeout(String endpoint, {Duration timeout = const Duration(seconds: 5)}) async {
  try {
    return await fetchDataFromAPI(endpoint).timeout(timeout);
  } on TimeoutException {
    throw Exception('$endpoint: タイムアウトしました');
  }
}

// Stream を生成する関数
Stream<int> generateNumbers(int count) async* {
  for (int i = 1; i <= count; i++) {
    await Future.delayed(Duration(milliseconds: 500));
    yield i;
  }
}

// Stream を変換する関数
Stream<String> processNumbers(Stream<int> numbers) async* {
  await for (int number in numbers) {
    String processed = 'Number: $number (${number % 2 == 0 ? 'Even' : 'Odd'})';
    yield processed;
  }
}

// ファイル読み込みをシミュレートする非同期関数
Future<String> readFile(String filename) async {
  print('ファイル読み込み開始: $filename');
  await Future.delayed(Duration(milliseconds: 800));
  
  if (filename.endsWith('.txt')) {
    return 'ファイル内容: $filename の中身です';
  } else {
    throw ArgumentError('サポートされていないファイル形式: $filename');
  }
}

// エラーハンドリング付きの非同期処理
Future<void> processFiles(List<String> filenames) async {
  for (String filename in filenames) {
    try {
      String content = await readFile(filename);
      print('✅ $content');
    } catch (e) {
      print('❌ エラー: $e');
    }
  }
}

// StreamController を使った例
class DataProcessor {
  late StreamController<String> _controller;
  Stream<String> get stream => _controller.stream;
  
  DataProcessor() {
    _controller = StreamController<String>.broadcast();
  }
  
  void addData(String data) {
    _controller.sink.add('処理済み: $data');
  }
  
  void addError(String error) {
    _controller.sink.addError('エラー: $error');
  }
  
  void close() {
    _controller.close();
  }
}

// Completer を使った例
Future<String> createCompleterExample() {
  Completer<String> completer = Completer<String>();
  
  // 非同期でタスクを実行
  Timer(Duration(seconds: 2), () {
    if (Random().nextBool()) {
      completer.complete('Completer による非同期処理完了');
    } else {
      completer.completeError('Completer でエラー発生');
    }
  });
  
  return completer.future;
}

void main() async {
  print('=== 非同期プログラミングの例 ===');
  
  // 基本的な非同期処理
  try {
    String result = await fetchDataFromAPI('sample-endpoint');
    print('✅ $result');
  } catch (e) {
    print('❌ エラー: $e');
  }
  
  print('\n--- 並行処理の例 ---');
  try {
    List<String> results = await fetchMultipleData();
    for (int i = 0; i < results.length; i++) {
      print('結果 ${i + 1}: ${results[i]}');
    }
  } catch (e) {
    print('❌ 並行処理エラー: $e');
  }
  
  print('\n--- タイムアウト処理の例 ---');
  try {
    String result = await fetchWithTimeout('slow-endpoint', timeout: Duration(seconds: 1));
    print('✅ $result');
  } catch (e) {
    print('❌ タイムアウトエラー: $e');
  }
  
  print('\n--- Stream処理の例 ---');
  Stream<int> numberStream = generateNumbers(5);
  Stream<String> processedStream = processNumbers(numberStream);
  
  await for (String processed in processedStream) {
    print(processed);
  }
  
  print('\n--- ファイル処理の例 ---');
  List<String> files = ['data.txt', 'config.txt', 'image.jpg', 'document.txt'];
  await processFiles(files);
  
  print('\n--- StreamController の例 ---');
  DataProcessor processor = DataProcessor();
  
  // Stream をリッスン
  StreamSubscription subscription = processor.stream.listen(
    (data) => print('受信: $data'),
    onError: (error) => print('エラー受信: $error'),
    onDone: () => print('Stream終了'),
  );
  
  // データを送信
  processor.addData('データ1');
  processor.addData('データ2');
  processor.addError('テストエラー');
  processor.addData('データ3');
  
  // 少し待ってからクローズ
  await Future.delayed(Duration(milliseconds: 100));
  processor.close();
  
  print('\n--- Completer の例 ---');
  try {
    String completerResult = await createCompleterExample();
    print('✅ $completerResult');
  } catch (e) {
    print('❌ Completer エラー: $e');
  }
  
  print('\n--- Future.value と Future.error の例 ---');
  Future<String> immediateSuccess = Future.value('即座に成功');
  Future<String> immediateError = Future.error('即座にエラー');
  
  print(await immediateSuccess);
  
  try {
    await immediateError;
  } catch (e) {
    print('即座のエラー: $e');
  }
  
  print('\n--- Stream の変換メソッド例 ---');
  Stream<int> sourceStream = Stream.fromIterable([1, 2, 3, 4, 5]);
  
  // map: 各要素を変換
  List<String> mapped = await sourceStream
      .map((i) => 'Item $i')
      .toList();
  print('Map結果: $mapped');
  
  // where: フィルタリング
  Stream<int> sourceStream2 = Stream.fromIterable([1, 2, 3, 4, 5, 6]);
  List<int> filtered = await sourceStream2
      .where((i) => i % 2 == 0)
      .toList();
  print('Filter結果 (偶数): $filtered');
  
  // take: 指定数まで取得
  Stream<int> sourceStream3 = Stream.fromIterable([1, 2, 3, 4, 5]);
  List<int> taken = await sourceStream3
      .take(3)
      .toList();
  print('Take結果 (最初の3個): $taken');
  
  // サブスクリプションのクリーンアップ
  await subscription.cancel();
  
  print('\n非同期プログラミングの例が完了しました。');
}

Flutterアプリの基本構造

// Flutter アプリケーションの基本例
// pubspec.yaml に flutter の依存関係が必要です

import 'package:flutter/material.dart';

// アプリのエントリーポイント
void main() {
  runApp(MyApp());
}

// アプリのルートウィジェット
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Dart & Flutter アプリ',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomePage(),
    );
  }
}

// ホームページウィジェット
class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _counter = 0;
  String _message = 'ボタンをタップしてカウントアップ!';
  List<String> _items = [];
  
  void _incrementCounter() {
    setState(() {
      _counter++;
      _message = 'カウント: $_counter';
      _items.add('アイテム $_counter');
    });
  }
  
  void _resetCounter() {
    setState(() {
      _counter = 0;
      _message = 'リセットしました!';
      _items.clear();
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Dart & Flutter デモ'),
        actions: [
          IconButton(
            icon: Icon(Icons.refresh),
            onPressed: _resetCounter,
          ),
        ],
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // メッセージ表示
            Card(
              child: Padding(
                padding: EdgeInsets.all(16.0),
                child: Text(
                  _message,
                  style: Theme.of(context).textTheme.headlineSmall,
                  textAlign: TextAlign.center,
                ),
              ),
            ),
            
            SizedBox(height: 20),
            
            // カウンター表示
            Container(
              padding: EdgeInsets.all(20),
              decoration: BoxDecoration(
                color: Colors.blue.shade50,
                borderRadius: BorderRadius.circular(10),
                border: Border.all(color: Colors.blue),
              ),
              child: Column(
                children: [
                  Text(
                    'カウンター',
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  SizedBox(height: 10),
                  Text(
                    '$_counter',
                    style: TextStyle(
                      fontSize: 48,
                      fontWeight: FontWeight.bold,
                      color: Colors.blue,
                    ),
                  ),
                ],
              ),
            ),
            
            SizedBox(height: 20),
            
            // アイテムリスト
            Text(
              'アイテムリスト:',
              style: TextStyle(
                fontSize: 16,
                fontWeight: FontWeight.bold,
              ),
            ),
            
            SizedBox(height: 10),
            
            Expanded(
              child: _items.isEmpty
                  ? Center(
                      child: Text(
                        'まだアイテムがありません',
                        style: TextStyle(
                          color: Colors.grey,
                          fontStyle: FontStyle.italic,
                        ),
                      ),
                    )
                  : ListView.builder(
                      itemCount: _items.length,
                      itemBuilder: (context, index) {
                        return Card(
                          margin: EdgeInsets.symmetric(vertical: 2),
                          child: ListTile(
                            leading: CircleAvatar(
                              child: Text('${index + 1}'),
                              backgroundColor: Colors.blue,
                              foregroundColor: Colors.white,
                            ),
                            title: Text(_items[index]),
                            subtitle: Text('作成時刻: ${DateTime.now().toString().substring(11, 19)}'),
                            trailing: IconButton(
                              icon: Icon(Icons.delete, color: Colors.red),
                              onPressed: () {
                                setState(() {
                                  _items.removeAt(index);
                                });
                              },
                            ),
                          ),
                        );
                      },
                    ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'カウントアップ',
        child: Icon(Icons.add),
      ),
    );
  }
}

// カスタムウィジェットの例
class CustomButton extends StatelessWidget {
  final String text;
  final VoidCallback onPressed;
  final Color? color;
  
  const CustomButton({
    Key? key,
    required this.text,
    required this.onPressed,
    this.color,
  }) : super(key: key);
  
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      style: ElevatedButton.styleFrom(
        backgroundColor: color ?? Theme.of(context).primaryColor,
        padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(8),
        ),
      ),
      onPressed: onPressed,
      child: Text(
        text,
        style: TextStyle(
          fontSize: 16,
          fontWeight: FontWeight.w500,
        ),
      ),
    );
  }
}

// データクラスの例
class User {
  final String name;
  final int age;
  final String email;
  
  const User({
    required this.name,
    required this.age,
    required this.email,
  });
  
  // JSON からの変換
  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      name: json['name'] as String,
      age: json['age'] as int,
      email: json['email'] as String,
    );
  }
  
  // JSON への変換
  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'age': age,
      'email': email,
    };
  }
  
  @override
  String toString() {
    return 'User(name: $name, age: $age, email: $email)';
  }
}

// 状態管理の例(簡単なカウンターストア)
class CounterStore extends ChangeNotifier {
  int _count = 0;
  
  int get count => _count;
  
  void increment() {
    _count++;
    notifyListeners();
  }
  
  void decrement() {
    _count--;
    notifyListeners();
  }
  
  void reset() {
    _count = 0;
    notifyListeners();
  }
}

print('Flutter アプリケーションの基本構造を定義しました。');
print('実際の Flutter プロジェクトで動作します。');
print('');
print('必要な依存関係:');
print('- flutter/material.dart');
print('- pubspec.yaml での Flutter SDK 設定');

メリット・デメリット

メリット

  • クロスプラットフォーム開発: 単一コードベースでiOS、Android、Web、デスクトップアプリが開発可能
  • Hot Reload: コード変更が即座にアプリに反映され、開発効率が大幅向上
  • 高性能: ネイティブコードにコンパイルされるため高いパフォーマンスを実現
  • 豊富なウィジェット: Flutterの充実したUI コンポーネントライブラリ
  • 型安全: 静的型付けによりコンパイル時にエラーを検出
  • 非同期サポート: async/await とStreamによる優れた非同期プログラミング

デメリット

  • 学習コスト: 新しい言語とフレームワークの習得が必要
  • エコシステム: JavaやJavaScriptと比較してライブラリやツールが限定的
  • アプリサイズ: Flutter アプリは比較的サイズが大きくなる傾向
  • プラットフォーム依存機能: ネイティブ機能へのアクセスには追加実装が必要
  • デバッグの複雑さ: マルチプラットフォーム特有のデバッグ課題

主要リンク

公式ドキュメント

学習リソース

開発ツール