こんにちは、テルプロです!
「dioを使ったAPI通信のやり方がわからない」とお悩みではないでしょうか?
本記事ではそんな悩みを解決していきます!
- Riverpod + freezed + dioの使い方がわかるようになる
- サンプルを用いて解説するので理解しやすい
- コードを公開しているので、自分の環境で確かめることができる
dioを使ってAPI通信をする【サンプル】
事前準備
パッケージをインストール
今回使用するパッケージは以下の通りです。
dependencies:
build_runner: ^2.1.8
dio: ^4.0.4
flutter:
sdk: flutter
flutter_riverpod: ^1.0.3
freezed: ^1.1.1
freezed_annotation: ^1.1.0
json_serializable: ^6.1.5
dev_dependencies:
flutter_lints: ^1.0.0
flutter_test:
sdk: flutter
プロジェクト構成
リポジトリ構成
完成イメージ
本アプリは1画面のみの構成になります。無料で公開されているサンプルAPIを取得し、Viewに反映させるというシンプルなアプリです。
それでは、Riverpod + freezed + dioを使って上記のアプリを作っていきましょう!
サンプルAPI:https://api.sampleapis.com/coffee/hot
ソースコード
main.dart
import 'package:coffee_list_dio/model/coffee.dart';
import 'package:coffee_list_dio/view/home_page.dart';
import 'package:coffee_list_dio/view_model/provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
title: 'Coffee List',
theme: ThemeData.dark(),
home: const HomePage(),
);
}
}
model / coffee.dart
まず初めに、APIから取得したい内容のクラスを作成していきましょう。今回はタイトルと説明文の2つを取得していきます。
クラスの作成にはfreezedを使用しています。freezedを使用することで、不変クラスを作成することができます。
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
// build_runnerを使うことで自動生成されるファイル
part 'coffee.freezed.dart';
part 'coffee.g.dart';
@freezed
class Coffee with _$Coffee {
factory Coffee({
String? title,
String? description,
}) = _Coffee;
factory Coffee.fromJson(Map<String, dynamic> json) => _$CoffeeFromJson(json);
}
freezedでクラスを作成したら、下記のコマンドを回してください。
flutter pub run build_runner watch --delete-conflicting-outputs
これにより、ファイルが自動生成されます。–delete-conflicting-outputsを加えることで、クラスに変更が加えられる度に再生成されます。
modelフォルダが上記のようになっていれば問題ありません。これで取得したいデータを保持するクラスを作成することができました。
service / coffee_api_client.dart
次に、APIを呼び出しデータを取得するためのクラスを作っていきます。
APIの取得にはdioを使用しています。
import 'package:coffee_list_dio/model/coffee.dart';
import 'package:dio/dio.dart';
class CoffeeApiClient {
Future<List<Coffee>?> fetchList() async {
final dio = Dio();
const url = 'https://api.sampleapis.com/coffee/hot';
final response = await dio.get(url);
if (response.statusCode == 200) {
try {
final datas = response.data as List<dynamic>;
final list = datas.map((e) => Coffee.fromJson(e)).toList();
return list;
} catch (e) {
throw e;
}
}
}
}
これで、APIを取得するためのクラスを作成することができました。
repository / repository.dart
APIを取得するためのメソッドをrepositoryから呼び出します。メソッドを切り分けることで、APIを取得するためのメソッドだと理解しやすくなります。
class Repository {
final api = CoffeeApiClient();
dynamic fetchList() async {
return await api.fetchList();
}
}
これで、APIを取得するためのメソッドをrepositoryに切り分けることができました。
view_model / provider.dart
次に、取得したAPIの状態を非同期で管理できるようにしていきます。状態管理にはRiverpodを使用しています。
今回は、非同期処理を行うのに適しているFutureProviderを使用していきます。
import 'package:coffee_list_dio/model/coffee.dart';
import 'package:coffee_list_dio/repository/repository.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// Repository(APIの取得)の状態を管理する
final repositoryProvider = Provider((ref) => Repository());
// 上記を非同期で管理する
final listProvider = FutureProvider<List<Coffee>>((ref) async {
final repository = ref.read(repositoryProvider);
return await repository.fetchList();
});
これにより、APIの取得を非同期で行えるようになりました。
home_page.dart
最後に、取得したAPIをViewに反映させる作業をしていきます。
FutureProviderのwhenメソッドを使用することで、Viewの反映、ローディング時、エラー時の3つの処理を書くことができるようになっています。
import 'package:coffee_list_dio/model/coffee.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:coffee_list_dio/view_model/provider.dart';
class HomePage extends ConsumerWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final asyncValue = ref.watch(listProvider); //取得したAPIデータの監視
return Scaffold(
appBar: AppBar(
title: const Text('Coffee List'),
),
body: Center(
child: asyncValue.when(
data: (data) {
return data.isNotEmpty
? ListView(
children: data
.map(
(Coffee coffee) => Card(
child: GestureDetector(
onTap: () {
showDialog(
context: context,
builder: (context) {
return SimpleDialog(
title: Text(coffee.title!),
children: [
SimpleDialogOption(
child: Text(coffee.description!),
),
],
);
},
);
},
child: ListTile(
title: Text(coffee.title!),
subtitle: Text(coffee.description!),
trailing: const Icon(Icons.more_vert),
),
),
),
)
.toList(),
)
: const Text('Data is empty.');
},
loading: () => const CircularProgressIndicator(),
error: (error, _) => Text(error.toString()),
),
),
);
}
}
大変お疲れ様でした!以上でアプリは完成です!
今回ご紹介したアプリ全体のソースコードはこちらです。
よろしければ、ご参考にどうぞ。
GitHub:https://github.com/terupro/coffee_list_dio
まとめ
今回は「dioを使ってAPI通信をする方法」をご紹介しました。
dioを使うことで、API通信を楽に実装することができます。ご紹介したサンプルを参考に、自分で色々と試してみてください。
▼以下では、私の実体験に基づいて「Flutterの効率的な勉強法」の具体的な手順を詳しく解説しています。よろしければ、ご参考にどうぞ!
最後までご覧いただきありがとうございました。ではまた!