[Flutter]httpを使ってAPIからデータを取得する
はじめに
ランダムなユーザーを生成してくれAPIサービスから、Flutterのhttpライブラリを使ってAPIからデータを取得する流れをまとめてみました。
環境
Flutter 3.10.1
Flutterをインストールされた上で進めていきます。
tl;dr
- Flutter アプリを作成する
-
http
を導入する - APIリクエストを作成する
- データクラスを作成する
- APIリクエストを呼び出す
- ユーザー一覧を作成する
- ユーザー詳細を作成する
Flutter アプリを作成する
flutter create
コマンドを実行し新規Flutterプロジェクトを作成します。
flutter create flutter_http
Creating project flutter_http...
Resolving dependencies in flutter_http... (1.4s)
Got dependencies in flutter_http.
Wrote 129 files.
All done!
You can find general documentation for Flutter at: https://docs.flutter.dev/
Detailed API documentation is available at: https://api.flutter.dev/
If you prefer video documentation, consider: https://www.youtube.com/c/flutterdev
In order to run your application, type:
$ cd flutter_http
$ flutter run
Your application code is in flutter_http/lib/main.dart.
プロジェクトディレクトリに移動し、flutter run
を実行します。
カウントアプリの画面表示されたら大丈夫です。
main.dart
にあるコードを削除し、ユーザー一覧だけを表示するようにします。
import 'package:flutter/material.dart';
import 'package:flutter_http/home_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'HTTP練習アプリ',
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(), // ユーザー一覧
);
}
}
http
を導入する
flutter pub add http
使用するファイルにインポートします。
import 'package:http/http.dart' as http;
APIリクエストを作成する
まずAPIエンドポイントを定義します。
APIにリクエストを送るメソッドを作成します。
class UserService {
Future<List<User>> getUsers() async {
try {
final response = await http.get(
Uri.parse("https://randomuser.me/api?results=50&seed=galaxies"),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
final List<User> userList = [];
for (var entry in data['results']) {
userList.add(User.fromJson(entry));
}
return userList;
} else {
throw Exception('Failed to load users. Status code: ${response.statusCode}');
}
} catch (e) {
throw Exception('Failed to connect to the server. Error: $e');
}
}
}
results
{
results: [
{
gender: "female",
name: {
title: "Miss",
first: "Chinmayee",
last: "Bansal"
},
location: {
street: {
number: 3624,
name: "Bannerghatta Rd"
},
city: "Shimla",
state: "Chhattisgarh",
country: "India",
postcode: 27471,
coordinates: {
latitude: "6.3302",
longitude: "47.7567"
},
timezone: {
offset: "-2:00",
description: "Mid-Atlantic"
}
},
email: "chinmayee.bansal@example.com",
login: {
uuid: "214650d3-3cf6-4991-9233-d1095def7f6b",
username: "bluesnake172",
password: "callie",
salt: "W9yLJeKi",
md5: "7ece6152b544a6717f326fff3eaeaa5f",
sha1: "1595b2249ba972fb42a29511d1b3827413711bb6",
sha256: "a4b2c38e7f35ba3adccfbb310c5bb5b0974947cdd8a2e9e2a5730c821b5262a5"
},
dob: {
date: "1947-06-28T12:59:58.388Z",
age: 76
},
registered: {
date: "2013-08-20T01:58:02.377Z",
age: 10
},
phone: "9894947466",
cell: "8545545763",
id: {
name: "UIDAI",
value: "371170947273"
},
picture: {
large: "https://randomuser.me/api/portraits/women/31.jpg",
medium: "https://randomuser.me/api/portraits/med/women/31.jpg",
thumbnail: "https://randomuser.me/api/portraits/thumb/women/31.jpg"
},
nat: "IN"
}
],
info: {
seed: "ab9ded7e61d93114",
results: 1,
page: 1,
version: "1.4"
}
}
データクラスを作成する
取得したデータの中から必要な部分をデータクラスに格納します。
データクラスに格納することによってデータの安全性と再利用性を向上します。
Userクラスを定義します。
import 'package:http/http.dart' as http;
import 'dart:convert';
// Nameはresultsにネストされているため別でクラスを作成する
class Name {
final String first;
final String last;
const Name ({
required this.first,
required this.last,
});
// jsonからNameオブジェクトを取得する
factory Name.fromJson(Map<String, dynamic> json) {
return Name(first: json['first'], last: json['last']);
}
}
// Userクラス
class User {
final String email;
final String picture;
final Name name;
const User ({
required this.email,
required this.picture,
required this.name,
});
// jsonからUserオブジェクトを取得する
factory User.fromJson(Map<String, dynamic> json) {
return User(
email: json['email'],
picture: json['picture']['medium'],
name: Name.fromJson(json['name']));
}
}
APIリクエストを作成しましたので呼び出します。
fstful
でStatefulWidgetを生成します。
ユーザー一覧を作成する
import 'package:flutter/material.dart';
import 'package:flutter_http/user_page.dart';
import 'package:flutter_http/user_service.dart';
class HomePage extends StatefulWidget {
const HomePage({ Key? key }) : super(key: key);
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late Future<List<User>> futureUsers;
void initState() {
super.initState();
futureUsers = UserService().getUsers();
}
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(title: const Text('ユーザー')),
body: Center(
child: FutureBuilder<List<User>>(
future: futureUsers,
builder: ((context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.separated(
itemBuilder: (context, index) {
User user = snapshot.data?[index];
return ListTile(
title: Text(user.email),
subtitle: Text('${user.name.first} ${user.name.last}'),
trailing: const Icon(Icons.chevron_right_outlined),
onTap: (() => openPage(context, user)), // 詳細ページを表示する
);
},
separatorBuilder: (context, index) {
return const Divider(color: Colors.black26);
},
itemCount: snapshot.data!.length,
);
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
return const CircularProgressIndicator(); // アニメーション
}),
),
)
);
}
// ビルドコンテキストとユーザーを渡す
openPage(context, User user) {
Navigator.push(
context, MaterialPageRoute(builder: (context) => UserPage(
user: user
)));
}
}
initState
メソッドはFlutterのStateライフサイクルの一部です。
ステートフルウィジェットが初めてウィジェットツリーに挿入されるときに呼び出されます。
UserService
クラスからgetUsers
メソッドを呼び出してfutureUsers
変数を初期化するために使われています。
FutureBuilder
はユーザーデータの非同期読み込みを処理するために使用されています。
データの読み込みを待っている間はCircularProgressIndicator
でアニメーションを表示します。
openPage
は、ユーザーがタップされたときにUserPage
にナビゲートするメソッドです。
選択したユーザーをパラメータとして渡します。
ユーザー詳細を作成する
import 'package:flutter/material.dart';
import 'package:flutter_http/user_service.dart';
class UserPage extends StatelessWidget {
final User user;
const UserPage({ Key? key, required this.user });
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(title: Text('${user.name.first} ${user.name.last}')),
body: Center(
child: Column(children: [
const SizedBox(
height: 30,
),
Image.network(user.picture),
const SizedBox(
height: 30,
),
Text(user.email)
],)
)
);
}
}
UserPage
クラスは、特定のユーザーの情報を表示する StatelessWidget
です。
Userオブジェクトを受け取り、そのユーザーの名前、写真、と電子メールを表示します。
MacOSでソケット通信が失敗したエラー
APIにリクエストを送る際にこちらのエラーが出る可能性があります。
Unhandled Exception: Exception: Failed to connect to the server. Error: ClientException with SocketException: Connection failed (OS Error: Operation not permitted, errno = 1)
macOSがネットワークにアクセスするためには、特定のエンタイトルメントを要求する必要があリマス。
解決方法はDebugProfile.entitlements
とRelease.entitlements
に以下キーと値のペアを追加します。
<!-- クライアント側の場合 -->
<key>com.apple.security.network.client</key>
<true/>
<!-- サーバー側の場合 -->
<key>com.apple.security.network.server</key>
<true/>
終わりに
簡単ですがhttpを使ってAPIからデータを取得してみました。
Discussion