🔗 公式サイトからダウンロード
✅ インストール手順(macOS)
cd ~/development
unzip ~/Downloads/flutter*macos*<version>.zip
echo 'export PATH="$PATH:$HOME/development/flutter/bin"' >> ~/.zshrc
source ~/.zshrc
flutter --version
<!-- ディレクトリがなければ作成 -->
mkdir ~/development
<!-- ダウンロードされた場所からdevelopmentに移動 -->
mv ~/Desktop/flutter ~/development/
<!-- 1.Xcodeコマンドラインツールの有効化 -->
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
<!-- 2.初回起動用セットアップの実行 -->
sudo xcodebuild -runFirstLaunch
<!-- 3.CocoaPodsのインストール -->
sudo gem install cocoapods
<!-- 4.CocoaPodsのインストール成功後の確認 -->
pod --version
このコマンドで、以下をチェックしてくれます:Xcode(iOS開発用)、Android Studio(Android用のエミュレータ)、VS Codeとの連携
flutter doctor
flutter doctor
をして以下のエラーがでたら行う
<!-- flutter doctorを実行後 -->
[!] Xcode - develop for iOS and macOS (Xcode 16.3)
✗ Unable to get list of installed Simulator runtimes.
Xcodeがシミュレータのランタイムを認識できない状態ですが、これはよくある症状で以下の手順で直ることがあります。
🔁 Xcode側でRuntimeの再インストールを試す Xcodeを開く メニュー → Settings...(旧バージョンでは Preferences...) Components タブを開く 最新の iOS Simulator(例:iOS 17.5など)が表示されていれば、「Install」ボタンで再インストール 完了後、Xcodeを再起動 → 再度 flutter doctor ✅ 最後に再確認
Simulator runtimeも問題なければ再度:
<!-- flutter doctor実行後 --> 「Xcode」の項目が [✓] になれば準備完了です。
シンプルなToDo(やること)を登録・一覧表示・編集・削除できるモバイルアプリケーション。
画面名 | 概要 |
---|---|
一覧画面 | 登録されたToDoリストを表示し、操作できる |
追加/編集画面 | ToDoを新規作成 or 既存の内容を更新する |
機能カテゴリ | 内容 |
---|---|
🔍 一覧表示 | ListView でToDoをリスト形式で表示 |
➕ ToDo追加 | FloatingActionButtonから追加画面に遷移し、タイトルを入力して追加可能 |
✔️ 完了チェック | ToDoをタップで isDone 状態をトグルし、チェックアイコンと装飾を更新 |
🗑 削除 | ToDoを左にスワイプすることで削除(Dismissible を使用) |
✏️ 編集 | ToDoを長押しで編集画面に遷移し、タイトルを変更可能 |
🔁 画面遷移 | Navigator.push で画面遷移し、戻ると結果を pop() で受け取る形式を採用 |
lib/
├── main.dart // アプリの起点・ToDo一覧画面
├── models/
│ └── todo.dart // ToDoモデル定義(title, isDone)
└── screens/
└── add_todo_screen.dart // ToDo追加・編集共通画面
StatefulWidget
(将来 Riverpod
などに置き換え可能)shared_preferences
や sqflite
を検討)widget_test.dart
に追加可能)優先度 | 機能 | 備考 |
---|---|---|
★★★ | データの永続化 | アプリ再起動後もToDoが残るようにする |
★★☆ | 状態管理の導入 | Riverpod や Provider など |
★★☆ | UI改善(テーマ・アイコンなど) | ダークモード対応、マテリアル3の活用など |
★★☆ | テスト自動化 | ユニットテスト・ウィジェットテスト |
<!-- lib/screens/add_todo_screen.dart -->import 'package:flutter/material.dart'; import 'models/todo.dart'; import 'screens/add_todo_screen.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'ToDo App', theme: ThemeData(primarySwatch: Colors.blue), home: const TodoListScreen(), ); } } class TodoListScreen extends StatefulWidget { const TodoListScreen({super.key}); @override State<TodoListScreen> createState() => _TodoListScreenState(); } class _TodoListScreenState extends State<TodoListScreen> { final List<Todo> _todos = []; void _addTodo(Todo todo) { setState(() { _todos.add(todo); }); } void _editTodo(int index, Todo updated) { setState(() { _todos[index] = updated; }); } void _toggleTodo(int index) { setState(() { final todo = _todos[index]; _todos[index] = todo.copyWith(isDone: !todo.isDone); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('ToDo 一覧')), body: ListView.builder( itemCount: _todos.length, itemBuilder: (context, index) { final todo = _todos[index]; return Dismissible( key: ValueKey(todo.title + todo.isDone.toString()), direction: DismissDirection.endToStart, // 左スワイプで削除 onDismissed: (_) { setState(() { _todos.removeAt(index); }); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('削除しました: ${todo.title}')), ); }, background: Container( color: Colors.red, alignment: Alignment.centerRight, padding: const EdgeInsets.only(right: 20), child: const Icon(Icons.delete, color: Colors.white), ), child: ListTile( title: Text( todo.title, style: TextStyle( decoration: todo.isDone ? TextDecoration.lineThrough : null, color: todo.isDone ? Colors.grey : null, ), ), trailing: Icon( todo.isDone ? Icons.check_box : Icons.check_box_outline_blank, color: todo.isDone ? Colors.green : null, ), onTap: () => _toggleTodo(index), onLongPress: () async { final updated = await Navigator.push<Todo>( context, MaterialPageRoute( builder: (context) => AddTodoScreen(initialTodo: _todos[index]), ), ); if (updated != null) _editTodo(index, updated); }, ), ); }, ), floatingActionButton: FloatingActionButton( onPressed: () async { final newTodo = await Navigator.push<Todo>( context, MaterialPageRoute(builder: (context) => const AddTodoScreen()), ); if (newTodo != null) _addTodo(newTodo); }, child: const Icon(Icons.add), ), ); } }
<!-- lib/models/todo.dart -->import 'package:flutter/material.dart'; import '../models/todo.dart'; class AddTodoScreen extends StatefulWidget { final Todo? initialTodo; const AddTodoScreen({super.key, this.initialTodo}); @override State<AddTodoScreen> createState() => _AddTodoScreenState(); } class _AddTodoScreenState extends State<AddTodoScreen> { late final TextEditingController _controller; @override void initState() { super.initState(); _controller = TextEditingController( text: widget.initialTodo?.title ?? '', ); } void _submit() { final title = _controller.text.trim(); if (title.isEmpty) return; final updatedTodo = Todo( title: title, isDone: widget.initialTodo?.isDone ?? false, ); Navigator.pop(context, updatedTodo); } @override Widget build(BuildContext context) { final isEditing = widget.initialTodo != null; return Scaffold( appBar: AppBar(title: Text(isEditing ? 'ToDo編集' : 'ToDo追加')), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: _controller, decoration: const InputDecoration(labelText: 'ToDoのタイトル'), onSubmitted: (_) => _submit(), ), const SizedBox(height: 20), ElevatedButton( onPressed: _submit, child: Text(isEditing ? '更新' : '追加'), ), ], ), ), ); } }
class Todo { final String title; final bool isDone; Todo({ required this.title, this.isDone = false, }); Todo copyWith({String? title, bool? isDone}) { return Todo( title: title ?? this.title, isDone: isDone ?? this.isDone, ); } }
chat gptにコードを書いてもらったが、自分一人だとこれを初めて書くのは時間がかかりそうだと感じた。 関数の定義の仕方やその後のWidget書き方はどこかReactのクラスコンポーネントを思わせるようだった。 chat gptがはじめにモデルを作っていたのを見ると、やはりWebフロントエンドの感覚(特にReact)とは異なる思想があるのかもしれないと感じた。 スマホならではのスライドや長押しの操作は関数を渡すだけで対応できるのは初めてFlutterを触るとすごく便利に感じた。 chat gptを使うことで、環境構築もほとんど躓くことがなかった。 良い経験にはなったが、実務で書いていくとなるとまだまだ覚えることがあるのだと感じた。