今回はFlutter(Dart)でHTTPS通信をしてサーバのAPIを叩いてみましょう!
– Flutterのサンプルだけ見たい方は ここから 下部にとんでください –
スマートフォンアプリを作る中で避けて通れないのが通信ですね!
現在使われているアプリのほとんどが何かしらのデータをサーバとやり取りしているでしょう。サーバがなければユーザ情報も管理できませんし、新規コンテンツを配信することもできません。
お手元のスマホの適当なアプリを開いてみて、どのような部分に通信が使われているか想像してみるのも面白いです。そして、通信が使わている処理の多さに驚かれるかもしれません。
このようにアプリと通信は今や一体と言えるものでしょう。
ぜひこの記事を通してFlutterでの通信デビューをしてみてください!
余談ですが、アプリやシステムなどのUIや機能を意識して見る癖を付けるとエンジニアとしての知見が広がるのでオススメです笑
HTTP
HTTPは通信に使われるプロトコル(規約)です。サーバとクライアントの両方で共通のルールを用意し、通信するデータの形式や接続の確立方法を示し合わせます。
などと言っても???なのですが、お役所に提出する書類みたいなものです。
「〇〇の処理は××課で様式△△の書類をもらって□□の部分を記載して提出してください」といった決まり事です。
コンピュータは「いい感じにしといて」といった処理ができないので、「データは先頭××bitに△△の内容を□□の方法に従って記載する」のように厳密に処理の手順や形式を定義する必要があります。
それらの決まり事をまとめて共通の決まり事としたものがプロトコルです。
そしてHTTPは主に通信に用いられるプロトコルといったことです。
JSON
JSONはデータを表現する書式です。数字や文字列などの様々な形式の複数のデータをまとめて記述することができます。
こちらもHTTPと同じように、広く共通のルールとすることでOSやプログラミング言語に関わらずにデータをやり取りすることができます。データをJSONにしてHTTPで通信!これ魔法です。
例えば「山田太郎さんは20歳の男性で趣味が読書」といったユーザ情報があります。これは「山田太郎さん、20歳、男性、趣味:読書」などと書き換えても日本語なら同じ意味ですが、コンピュータでは同一のデータとして処理することができません…
そこでJSONに変換してしまうとこのようになります。
{
"名前": "山田太郎",
"年齢": 20,
"性別": "男性",
"趣味": "読書"
}
これはJSONというルールに基づいた変換なので、全員が同じく認識することができます。
連想配列やHash,Dictionary,Mapなどのデータ構造とよく似ていますね!
HTTPパッケージインストール
FutterでHTTP通信をするためのパッケージが公開されています。
コマンドプロンプトやターミナルを開いてプロジェクトのディレクトリに移動し、以下のコマンドでインストールしてください!
flutter pub add http
GET通信
HTTPではデータを取得するGET通信と、データを送信するPOST通信があります。
Dart/FlutterでのGET通信のメソッドはこのようになります。
Map<String, String> body がデータを受信する際に使用するパラメータです。
ユーザIDが3の人のデータがほしいなら {“user_id” : “3”} のようになります!
(この形式は例ですので、実際に使用できるパラメータや形式は使用するAPIのドキュメントやリファレンスを参照してください)
本記事では {JSON}Placeholder というAPIを練習するサイトを使用しています。
詳しい内容や使い方は下記サイトをご参照ください🙇
https://jsonplaceholder.typicode.com/
import 'package:http/http.dart' as http;
import 'dart:convert';
/// GET : サーバからデータを受信する
Future<Map<String, dynamic>> getApi() async {
// クエリパラメータ(今回は必要無いですが例として適当なデータ)
Map<String, String> body = {"name": "山田太郎", "age": "20"};
// 接続先
Uri uri = Uri.https("jsonplaceholder.typicode.com", "posts/1", body);
// ヘッダーの設定
Map<String, String> headers = {'content-type': 'application/json'};
// GET通信
http.Response resp = await http.get(uri, headers: headers);
// 受信したデータはJSON形式なのでデコードする
return jsonDecode(resp.body);
}
POST通信
POST通信のメソッドはこのようになります。
同様にMap<String, String> bodyに送りたいデータを記述してください。
こちらも例としてのデータを載せていますので、実際に送信するJSON形式はドキュメントやリファレンスを(ry
余談ですが公式ドキュメントは英語で書かれていたり、APIの様式がズラズラ並んでいたりで最初は見づらいかと思います😖
ですが安易に第三者の日本語解説などを頼らずに卍公式のドキュメント卍を見てください!!
APIもエンドポイントやパラメータなどが厳密に設定されているので、最も信頼できる公式の資料を読むことがレベルアップの近道です!!!
/// POST : サーバにデータを送信する
Future<Map<String, dynamic>> postApi() async {
// 接続先
Uri uri = Uri.https("jsonplaceholder.typicode.com", "posts");
// ヘッダーの設定
Map<String, String> headers = {'content-type': 'application/json'};
// 送信したいデータをbodyに書き込む
Map<String, String> body = {
"名前": "山田太郎",
"年齢": "20",
"性別": "男性",
"趣味": "読書",
};
// bodyをJSONにエンコードしてPOST通信
http.Response resp = await http.post(uri, headers: headers, body: jsonEncode(body));
print(resp.body);
// 送信結果もJSON形式で帰って来るのでデコードする
var json = jsonDecode(resp.body);
return json;
}
全体コード
Flutterのアプリで上記の通信を行ってみます!
受信ボタンを押すとサーバにある10個のJSONからランダムに1つを受信。
送信ボタンを押すと山田太郎さんのデータを送信します。
import 'package:http/http.dart' as http;
import "package:flutter/material.dart";
import 'dart:convert';
import "dart:math";
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: ApiExample(),
);
}
}
/// StateFulWidget
class ApiExample extends StatefulWidget {
const ApiExample({Key? super.key});
@override
State<ApiExample> createState() => ApiExampleView();
}
/// View
class ApiExampleView extends State<ApiExample> {
/// 受信したテキストデータ
String getText = "ボタンをタップしてデータを受信";
/// 登録したデータ番号
int? postId;
/// 受信ボタン
Widget createGetButton() {
return ElevatedButton(
onPressed: () async {
var ret = await getApi();
this.getText = ret.toString();
if (mounted) setState(() {});
},
child: const Text(
"データ受信",
style: TextStyle(fontSize: 16),
),
);
}
/// 送信ボタン
Widget createPostButton() {
return ElevatedButton(
onPressed: () async {
var ret = await postApi();
this.postId = ret["id"];
},
child: const Text(
"データ送信",
style: TextStyle(fontSize: 16),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
shrinkWrap: true,
children: [
// 受信データ表示
Container(
width: 500,
constraints: const BoxConstraints(minHeight: 100),
alignment: Alignment.center,
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(20),
child: Text(
getText,
style: const TextStyle(fontSize: 16),
),
),
),
),
// データ受信ボタン
this.createGetButton(),
const SizedBox(height: 10),
// データ送信ボタン
this.createPostButton(),
],
),
);
}
}
/// GET : サーバからデータを受信する
Future<Map<String, dynamic>> getApi() async {
// クエリパラメータ(今回は必要無いですが例として適当なデータ)
Map<String, String> body = {"name": "山田太郎", "age": "20"};
// 接続先
Uri uri = Uri.https("jsonplaceholder.typicode.com", "posts/${Random().nextInt(9) + 1}", body);
// ヘッダーの設定
Map<String, String> headers = {'content-type': 'application/json'};
// GET通信
http.Response resp = await http.get(uri, headers: headers);
print(resp.body);
// 受信したデータはJSON形式なのでデコードする
return jsonDecode(resp.body);
}
/// POST : サーバにデータを送信する
Future<Map<String, dynamic>> postApi() async {
// 接続先
Uri uri = Uri.https("jsonplaceholder.typicode.com", "posts");
// ヘッダーの設定
Map<String, String> headers = {'content-type': 'application/json'};
// 送信したいデータをbodyに書き込む
Map<String, String> body = {
"名前": "山田太郎",
"年齢": "20",
"性別": "男性",
"趣味": "読書",
};
// bodyをJSONにエンコードしてPOST通信
http.Response resp = await http.post(uri, headers: headers, body: jsonEncode(body));
print(resp.body);
// 送信結果もJSON形式で帰って来るのでデコードする
var json = jsonDecode(resp.body);
return json;
}
この記事が役に立ちましたらぜひ左下のGoodボタンをお願いします!
皆様のGoodが執筆の励みになります。
コメント