外部APIと連携しよう
このページで学ぶこと
- Flutter から HTTP リクエストを送れる
- JSON をDartのオブジェクトに変換できる
- ローディング・エラーの状態を適切に表示できる
http パッケージをインストールする
Flutter 標準では HTTP 通信の機能がないため、パッケージを追加します。
flutter pub add httpやってみよう
ステップ1: APIからデータを取得する
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: UserListPage());
}
}
class UserListPage extends StatefulWidget {
const UserListPage({super.key});
@override
State<UserListPage> createState() => _UserListPageState();
}
class _UserListPageState extends State<UserListPage> {
List<dynamic> _users = [];
bool _loading = true;
String? _error;
@override
void initState() {
super.initState();
_fetchUsers(); // ページが表示されたらAPIを叩く
}
Future<void> _fetchUsers() async {
try {
final response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/users'),
);
if (response.statusCode == 200) {
setState(() {
_users = jsonDecode(response.body); // JSONをDartのListに変換
_loading = false;
});
} else {
setState(() {
_error = 'エラー: ${response.statusCode}';
_loading = false;
});
}
} catch (e) {
setState(() {
_error = '通信エラーが発生しました';
_loading = false;
});
}
}
@override
Widget build(BuildContext context) {
if (_loading) {
return const Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
if (_error != null) {
return Scaffold(
body: Center(child: Text(_error!, style: const TextStyle(color: Colors.red))),
);
}
return Scaffold(
appBar: AppBar(title: const Text('ユーザー一覧')),
body: ListView.builder(
itemCount: _users.length,
itemBuilder: (context, index) {
final user = _users[index];
return ListTile(
leading: CircleAvatar(child: Text('${index + 1}')),
title: Text(user['name']),
subtitle: Text(user['email']),
);
},
),
);
}
}ステップ2: モデルクラスを作る
JSON を都度 user['name'] のように扱うのは大変です。
モデルクラスを作ってDartのオブジェクトに変換しましょう。
// lib/models/user.dart
class User {
final int id;
final String name;
final String email;
User({
required this.id,
required this.name,
required this.email,
});
// JSONからUserオブジェクトを作るファクトリコンストラクタ
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'],
name: json['name'],
email: json['email'],
);
}
}// APIから取得したJSONをUserオブジェクトのリストに変換
final List<User> users = (jsonDecode(response.body) as List)
.map((json) => User.fromJson(json))
.toList();
// 使うとき
print(users[0].name); // user['name'] の代わりに
print(users[0].email);ステップ3: POSTリクエストを送る
Future<void> createTodo(String title) async {
final response = await http.post(
Uri.parse('https://jsonplaceholder.typicode.com/todos'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'title': title,
'completed': false,
'userId': 1,
}),
);
if (response.statusCode == 201) {
final newTodo = jsonDecode(response.body);
print('作成成功: ${newTodo['id']}');
}
}FutureBuilder を使う
非同期処理と Widget を組み合わせる方法として FutureBuilder があります。
class UserPage extends StatelessWidget {
const UserPage({super.key});
Future<List<User>> fetchUsers() async {
final res = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
final List<dynamic> data = jsonDecode(res.body);
return data.map((json) => User.fromJson(json)).toList();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('ユーザー一覧')),
body: FutureBuilder<List<User>>(
future: fetchUsers(),
builder: (context, snapshot) {
// ローディング中
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
// エラー
if (snapshot.hasError) {
return Center(child: Text('エラー: ${snapshot.error}'));
}
// データあり
final users = snapshot.data!;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return ListTile(title: Text(users[index].name));
},
);
},
),
);
}
}確認しよう
-
flutter pub add httpでパッケージを追加した - APIからデータを取得してリスト表示できた
- ローディング中・エラー時の表示を実装した
- モデルクラスを作ってJSONを変換できた
AIに聞いてみよう
「Flutterでローカルにデータを保存するにはどうすればいいですか?shared_preferencesとsqfliteの違いを教えてください」
次のステップ
Flutter の基礎はここで完了です!自分のアプリを作りましょう。
Last updated on