レシピ#4-8: 実践 - APIを使ったニュースアプリを作る

このレシピのゴール

Cursor + Claudeを使って、実際のAPIからデータを取得して表示するニュースアプリを作ります。

機能: - ✅ ニュース記事一覧表示 - ✅ 記事詳細表示 - ✅ カテゴリ別表示 - ✅ Pull-to-refresh - ✅ ページネーション - ✅ お気に入り機能

技術: - ✅ REST API通信 - ✅ JSONパース - ✅ 非同期処理 - ✅ エラーハンドリング - ✅ ローディング状態管理

Step 1: API選定

無料で使えるニュースAPI: - NewsAPI - 無料プランあり - GNews API - 無料プランあり - または、公開されている任意のREST API

今回の例: NewsAPIを使用

Step 2: プロジェクトセットアップ

flutter create news_app
cd news_app
cursor .

Cursorに依頼:

pubspec.yamlに以下のパッケージを追加してください:
- http: 最新版
- cached_network_image: 最新版
- url_launcher: 最新版
- shared_preferences: 最新版(お気に入り機能用)

Step 3: データモデル作成

プロンプト:

ニュース記事のデータモデルを作成してください。

以下はNewsAPIのレスポンス例です:
{
  "articles": [
    {
      "source": {"id": "abc-news", "name": "ABC News"},
      "author": "John Doe",
      "title": "Sample News Title",
      "description": "Sample description",
      "url": "https://example.com/article",
      "urlToImage": "https://example.com/image.jpg",
      "publishedAt": "2025-01-07T10:00:00Z",
      "content": "Full article content..."
    }
  ]
}

必要なもの:
1. ArticleクラスとSourceクラス
2. fromJson メソッド
3. NewsResponseクラス(記事リストを含む)

シンプルな構造でお願いします。

Step 4: ApiService作成

プロンプト:

NewsApiServiceクラスを作成してください。

機能:
- トップニュース取得
- カテゴリ別ニュース取得
  (business, technology, sports, entertainment など)
- 検索機能
- ページネーション対応

構成:
- シングルトンパターン
- httpパッケージ使用
- エラーハンドリング(ネットワークエラー、APIエラー)
- APIキーの管理(定数として)

注意:
- タイムアウト設定(10秒)
- カスタム例外クラス作成

Step 5: メイン画面作成

プロンプト:

ニュース一覧画面を作成してください。

UI構成:
- AppBar
  - タイトル「ニュース」
  - 検索アイコン

- TabBar
  - 「トップ」
  - 「ビジネス」
  - 「テクノロジー」
  - 「スポーツ」
  - 「エンタメ」

- Body
  - ニュース記事カード一覧(ListView)
  - Pull-to-refresh機能
  - 無限スクロール(ページネーション)

各記事カード:
- 画像(左側、正方形)
- タイトル(太字)
- 説明(2行まで)
- ソース名と時刻
- タップで詳細画面へ

状態:
- ローディング表示
- エラー表示
- 空の場合の表示

実装:
- StatefulWidget
- NewsApiServiceを使用
- ScrollControllerでページネーション検知

Step 6: 記事詳細画面

プロンプト:

記事詳細画面を作成してください。

UI:
- AppBar
  - タイトル(ソース名)
  - 共有ボタン
  - お気に入りボタン

- Body(スクロール可能)
  - 記事画像(全幅)
  - タイトル(大きく)
  - ソース名と投稿日時
  - 記事内容
  - 「続きを読む」ボタン
    → url_launcherで元記事をブラウザで開く

機能:
- お気に入り追加/削除
- URLを開く(url_launcher使用)
- (オプション)記事共有

実装:
- StatefulWidget
- Articleモデルを受け取る

Step 7: お気に入り機能

プロンプト:

お気に入り機能を実装してください。

機能:
- 記事をお気に入りに追加
- お気に入りから削除
- お気に入り一覧表示

データ保存:
- SharedPreferencesを使用
- 記事IDのリストを保存
- FavoriteServiceクラスで管理

UI:
- BottomNavigationBarで切り替え
  - ホーム
  - お気に入り
- お気に入りアイコンの状態表示
  (お気に入り登録済みは塗りつぶし)

実装:
- シングルトンパターンのServiceクラス
- お気に入り状態の同期

Step 8: 検索機能

プロンプト:

検索機能を追加してください。

UI:
- AppBarの検索アイコンをタップ
- 検索画面に遷移
- 検索TextField(自動フォーカス)
- 検索結果一覧

機能:
- キーワードで記事検索
- APIに検索クエリを送信
- デバウンス処理(入力から500ms後に検索)
- 検索履歴保存(オプション)

実装:
- Timer使用(デバウンス)
- NewsApiService.search() メソッド使用

Step 9: エラーハンドリングとローディング

プロンプト:

より良いエラーハンドリングとローディング表示を実装してください。

ローディング:
- 初回ロード: 中央にCircularProgressIndicator
- 追加ロード(ページネーション): リスト下部に小さなインジケータ
- Pull-to-refresh: 標準のRefreshIndicator

エラー:
- ネットワークエラー: 「インターネット接続を確認してください」
- APIエラー: 具体的なエラーメッセージ
- タイムアウト: 「接続がタイムアウトしました」
- リトライボタンを表示

実装:
- エラー状態を enum で管理
- エラーごとに適切なメッセージとアイコン表示

Step 10: キャッシュ機能(オプション)

プロンプト:

簡易キャッシュ機能を追加してください。

機能:
- API レスポンスをメモリにキャッシュ
- 5分間有効
- オフラインでも最後のデータを表示

実装:
- Map<String, CachedData> 形式
- CachedData クラス(data + timestamp)
- ApiService 内で管理
- キャッシュヒット時はAPIコールをスキップ

完成後のファイル構成

lib/
├── main.dart
├── models/
│   ├── article.dart
│   └── news_response.dart
├── services/
│   ├── news_api_service.dart
│   └── favorite_service.dart
├── screens/
│   ├── news_list_screen.dart
│   ├── article_detail_screen.dart
│   ├── search_screen.dart
│   └── favorites_screen.dart
└── widgets/
    ├── article_card.dart
    ├── loading_indicator.dart
    └── error_view.dart

まとめ

このアプリで学んだこと:

API通信

非同期処理

UXパターン

実践的パターン

重要なポイント: - ✅ Riverpod不要で実用アプリ完成 - ✅ シンプルで保守しやすい構造 - ✅ AIが理解しやすいコード - ✅ バージョンアップに強い

Section 4 完了!

おめでとうございます!Section 4「秘伝のソース」を完了しました。

学んだこと: 1. Cursor + Claudeの効率的なワークフロー 2. Riverpodを避けるべき理由 3. 自己完結型Widgetパターン 4. Serviceクラスパターン 5. 実践的なアプリ開発(メモ、TODO、ニュース) 6. Antigravityの現状と注意点

次のステップ: - Section 5: シェフの特製料理(より高度なアプリ) - または、自分のアイデアでアプリを作ってみましょう!

Happy Coding with AI! 🎉