Flutterでパッケージhttp
を使ってAPIと通信(GET
とかPOST
とか)したい!
概要
今回の記事では、Flutterでパッケージhttp
を使ってAPIとGET
やPOST
などの通信をする手順を掲載する。
仕様書
環境
- Android Studio Giraffe | 2023.2.1 Patch 2
- Flutter 3.19.6
- flutter_riverpod: 2.5.1
- http: 1.1.0
手順書
インストール編とコード編の2部構成です。
インストール編
ターミナルでコマンドを実行するか
flutter pub add flutter_riverpod http
pubspec.yaml
のdependencies:
に下記のような感じで追加して
dependencies:
flutter_riverpod: ^2.5.1
http: ^1.1.0
ターミナルでflutter pub get
する。
flutter pub get
コード編
今回はOpen-Meteoという天気予報のAPIと通信してみる。
このAPIはAPIキーなどの認証をしなくても使わせてもらえるので、楽にAPIとのやり取りを試せる。
パッケージflutter_riverpod
を使って非同期でAPIからデータを取得する。
東京の7日間分の最高気温、最低気温の予想をAPIから取得してTable
に表示する例。
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: const HttpSample1()
)
)
);
}
class WeatherInfo {
late List<double> temperature2mMax;
late List<double> temperature2mMin;
late List<String> time;
late List<int> weatherCode;
WeatherInfo(
{required this.temperature2mMax,
required this.temperature2mMin,
required this.time,
required this.weatherCode});
}
final weatherInfoProvider = FutureProvider<WeatherInfo>((ref) async {
return fetchWeatherInfo();
});
Future<WeatherInfo> fetchWeatherInfo() async {
try {
final response = await http.get(
Uri.parse(
'https://api.open-meteo.com/v1/forecast?latitude=35.6785&longitude=139.6823&daily=weather_code,temperature_2m_max,temperature_2m_min&timezone=Asia%2FTokyo'),
);
debugPrint('response.statusCode: ${response.statusCode}');
debugPrint('response.headers: ${response.headers}');
debugPrint('response.body: ${response.body}');
if (response.statusCode == 200) {
final Map<String, dynamic> responseJson = jsonDecode(response.body);
return WeatherInfo(
temperature2mMax:
List<double>.from(responseJson['daily']['temperature_2m_max']),
temperature2mMin:
List<double>.from(responseJson['daily']['temperature_2m_min']),
time: List<String>.from(responseJson['daily']['time']),
weatherCode: List<int>.from(responseJson['daily']['weather_code']),
);
}
} catch (e) {
debugPrint(e.toString());
throw Exception(e.toString());
}
throw Exception('Failed to load weather info');
}
class HttpSample1 extends StatelessWidget {
const HttpSample1({super.key});
@override
Widget build(BuildContext context) {
return ProviderScope(
child: SafeArea(
child: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Consumer(
builder: (BuildContext context, WidgetRef ref, Widget? child) {
List<String> headers = [
'Date',
'Max Temp (°C)',
'Min Temp (°C)',
'Weather Code'
];
final asyncValue = ref.watch(weatherInfoProvider);
return asyncValue.when(
data: (data) => Table(
border: TableBorder.all(),
columnWidths: {
for (int i = 0; i < headers.length; i++)
i: const FlexColumnWidth(),
},
children: [
TableRow(
decoration: BoxDecoration(color: Colors.grey[300]),
children: headers.map((header) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
header,
textAlign: TextAlign.center,
style: const TextStyle(fontWeight: FontWeight.bold),
),
);
}).toList(),
),
...List.generate(data.time.length, (index) {
return TableRow(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
data.time[index],
textAlign: TextAlign.center,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
data.temperature2mMax[index].toString(),
textAlign: TextAlign.right,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
data.temperature2mMin[index].toString(),
textAlign: TextAlign.right,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
data.weatherCode[index].toString(),
textAlign: TextAlign.right,
),
),
],
);
}),
],
),
loading: () => const CircularProgressIndicator(),
error: (error, stack) => Text(
'Error: $error',
style: const TextStyle(color: Color(0xFFFF0000)),
),
);
}),
),
),
),
);
}
}
APIにリクエストを送ってる部分。
final response = await http.get(
Uri.parse(
'https://api.open-meteo.com/v1/forecast?latitude=35.6785&longitude=139.6823&daily=weather_code,temperature_2m_max,temperature_2m_min&timezone=Asia%2FTokyo'),
今回は、経度緯度や受け取りたい情報の指定をクエリーパラーメーターで送ってるのとAPIキーなどの認証のための情報をこちらから送信する必要がないのでヘッダーやボディの設定はない。
ヘッダーやボディを送信する場合は下記のような感じになる。JSONを送信する例。
Map<String, String> headers = {
'Content-Type': 'application/json',
};
final response = await http.get(
Uri.parse(
'https://api.open-meteo.com/v1/forecast?latitude=35.6785&longitude=139.6823&daily=weather_code,temperature_2m_max,temperature_2m_min&timezone=Asia%2FTokyo'),
headers: headers,
body: jsonEncode({
'info': "xxxx",
}),
戻り値のプロパティstatusCode
を確認することで通信が成功したか失敗したか確認できる。
APIからデータの取得に成功してれば戻り値のプロパティbody
にJSON形式で保管される。convert.dart
のjsonDecode
を使ってオブジェクトに変換してデータにアクセスする感じ。
戻り値のプロパティheaders
にヘッダーがは保管される。例えば、クッキー認証した場合はクッキーがヘッダーの中に入ってたり。
まとめ(感想文)
APIサーバーと通信するアプリを作る時に使えるかもね!