目次
API って何?
API (Application Programming Interface) は、アプリとサーバーが会話するための仕組み!
比喩で言うと: レストランのようなもの
- あなた(アプリ): 料理を注文
- ウェイター(API): 注文を厨房に伝える
- 厨房(サーバー): 料理を作る
- ウェイター(API): 料理を運んでくる
アプリ → API → サーバー → API → アプリ の流れ!
HTTP リクエストの種類
GET→データを取得(ブログ記事を読む)
POST→データを作成(新規記事を投稿)
PUT→データを更新(記事を編集)
DELETE→データを削除(記事を削除)
GET を覚えよう
http パッケージのインストール
dependencies:
flutter:
sdk: flutter
http: ^1.1.0
超シンプルな GET リクエスト
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class SimpleApiDemo extends StatefulWidget {
@override
_SimpleApiDemoState createState() => _SimpleApiDemoState();
}
class _SimpleApiDemoState extends State<SimpleApiDemo> {
String _result = '取得前';
// ========================================
// GET リクエスト
// ========================================
Future<void> fetchData() async {
// URLを指定
final url = Uri.parse('https://jsonplaceholder.typicode.com/posts/1');
// GETリクエストを送信
final response = await http.get(url);
// ステータスコード確認
if (response.statusCode == 200) {
// 成功! JSONをデコード
final data = json.decode(response.body);
setState(() {
_result = data['title']; // タイトルを取得
});
} else {
// 失敗
setState(() {
_result = 'エラー: ${response.statusCode}';
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Simple API')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_result, style: TextStyle(fontSize: 18)),
SizedBox(height: 20),
ElevatedButton(
onPressed: fetchData,
child: Text('データ取得'),
),
],
),
),
);
}
}
基本の流れ(3ステップ)
ステップ1: URL を作る
final url = Uri.parse('https://api.example.com/data');
ステップ2: GET リクエスト
final response = await http.get(url);
ステップ3: レスポンスを処理
if (response.statusCode == 200) {
final data = json.decode(response.body);
print(data);
}
HTTP ステータスコード
| 200 | 成功 | データ取得成功 ✅ |
| 201 | 作成成功 | 新規投稿成功 |
| 400 | リクエストエラー | パラメータが間違い |
| 401 | 認証エラー | ログインが必要 |
| 404 | Not Found | データが見つからない |
| 500 | サーバーエラー | サーバー側の問題 |
ヘッダーを追加
APIによっては、ヘッダー(認証トークンなど)が必要!
Future<void> fetchWithHeaders() async {
final url = Uri.parse('https://api.example.com/data');
final response = await http.get(
url,
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN_HERE',
'Accept': 'application/json',
},
);
if (response.statusCode == 200) {
final data = json.decode(response.body);
print(data);
}
}
タイムアウト設定
Future<void> fetchWithTimeout() async {
final url = Uri.parse('https://api.example.com/data');
try {
final response = await http.get(url).timeout(
Duration(seconds: 10), // 10秒でタイムアウト
onTimeout: () {
throw Exception('タイムアウトしました');
},
);
if (response.statusCode == 200) {
final data = json.decode(response.body);
print(data);
}
} catch (e) {
print('エラー: $e');
}
}
実践例: パラメータ付き GET リクエスト
class SearchDemo extends StatefulWidget {
@override
_SearchDemoState createState() => _SearchDemoState();
}
class _SearchDemoState extends State<SearchDemo> {
List<dynamic> _posts = [];
bool _isLoading = false;
// ========================================
// ユーザーIDで絞り込み
// ========================================
Future<void> fetchPostsByUserId(int userId) async {
setState(() {
_isLoading = true;
});
try {
// パラメータ付きURL
final url = Uri.parse(
'https://jsonplaceholder.typicode.com/posts?userId=$userId'
);
// または、queryParametersを使う(推奨)
// final url = Uri.https(
// 'jsonplaceholder.typicode.com',
// '/posts',
// {'userId': userId.toString()},
// );
final response = await http.get(url);
if (response.statusCode == 200) {
final List<dynamic> data = json.decode(response.body);
setState(() {
_posts = data;
_isLoading = false;
});
}
} catch (e) {
print('エラー: $e');
setState(() {
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('検索デモ')),
body: Column(
children: [
// ========================================
// ユーザー選択
// ========================================
Padding(
padding: EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () => fetchPostsByUserId(1),
child: Text('User 1'),
),
ElevatedButton(
onPressed: () => fetchPostsByUserId(2),
child: Text('User 2'),
),
ElevatedButton(
onPressed: () => fetchPostsByUserId(3),
child: Text('User 3'),
),
],
),
),
Divider(),
// ========================================
// リスト表示
// ========================================
Expanded(
child: _isLoading
? Center(child: CircularProgressIndicator())
: _posts.isEmpty
? Center(child: Text('ユーザーを選択してください'))
: ListView.builder(
itemCount: _posts.length,
itemBuilder: (context, index) {
final post = _posts[index];
return Card(
margin: EdgeInsets.all(8),
child: ListTile(
title: Text(post['title']),
subtitle: Text(post['body'], maxLines: 2),
),
);
},
),
),
],
),
);
}
}
パラメータの書き方
方法1: 文字列で直接書く
final url = Uri.parse('https://api.example.com/posts?userId=1&limit=10');
方法2: Uri.https を使う
final url = Uri.https(
'api.example.com', // ホスト
'/posts', // パス
{ // パラメータ
'userId': '1',
'limit': '10',
'sort': 'desc',
},
);
// 結果: https://api.example.com/posts?userId=1&limit=10&sort=desc
コツ
try-catch で囲む
// ❌ エラーでアプリがクラッシュ
Future<void> fetchData() async {
final response = await http.get(url);
final data = json.decode(response.body);
}
// ✅ 安全
Future<void> fetchData() async {
try {
final response = await http.get(url);
if (response.statusCode == 200) {
final data = json.decode(response.body);
}
} catch (e) {
print('エラー: $e');
// ユーザーにエラー表示
}
}
ローディング状態を管理
class MyPage extends StatefulWidget {
@override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
bool _isLoading = false;
List<dynamic> _data = [];
String? _error;
Future<void> fetchData() async {
setState(() {
_isLoading = true;
_error = null;
});
try {
final response = await http.get(url);
if (response.statusCode == 200) {
setState(() {
_data = json.decode(response.body);
_isLoading = false;
});
} else {
throw Exception('エラー: ${response.statusCode}');
}
} catch (e) {
setState(() {
_error = e.toString();
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
if (_isLoading) return CircularProgressIndicator();
if (_error != null) return Text('エラー: $_error');
if (_data.isEmpty) return Text('データがありません');
return ListView.builder(...);
}
}
Service クラスで整理
// api_service.dart
class ApiService {
static const String baseUrl = 'https://api.example.com';
Future<List<dynamic>> fetchPosts() async {
final url = Uri.parse('$baseUrl/posts');
final response = await http.get(url);
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('Failed to load posts');
}
}
Future<dynamic> fetchPostById(int id) async {
final url = Uri.parse('$baseUrl/posts/$id');
final response = await http.get(url);
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('Failed to load post');
}
}
}
// 使う
final apiService = ApiService();
final posts = await apiService.fetchPosts();
まとめ: GET リクエスト
基本の流れ:
- Uri.parse() で URL を作る
- http.get() でリクエスト
- response.statusCode で確認
- json.decode() でデコード
ポイント:
- FutureBuilder で簡潔に書ける
- パラメータは Uri.https() が安全
- try-catch でエラー処理
- ローディング状態を管理
- Service クラスで整理
一言まとめ:
API の GET リクエストはhttp
パッケージで簡単! http.get(url) でデータ取得、
json.decode() でJSON変換。
FutureBuilder でローディング・エラー処理が自動。
パラメータは Uri.https() で安全に!
