Flutter

【Flutter】dioを使ってAPI通信をする!サンプルを用いて解説

こんにちは、テルプロです!

「dioを使ったAPI通信のやり方がわからない」とお悩みではないでしょうか?

テルプロ

本記事ではそんな悩みを解決していきます!

本記事を読むことで
  1. Riverpod + freezed + dioの使い方がわかるようになる
  2. サンプルを用いて解説するので理解しやすい
  3. コードを公開しているので、自分の環境で確かめることができる

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の効率的な勉強法」の具体的な手順を詳しく解説しています。よろしければ、ご参考にどうぞ!

最後までご覧いただきありがとうございました。ではまた!

参考文献
ABOUT ME
テルプロ
東京都在住のアプリエンジニア。大学では、ソフトウェア開発の研究に取り組む。長期のエンジニアインターンシップを経て、実務スキルを磨き、現在はフリーランスエンジニアとしても活動中。メインはモバイルアプリ開発。IT関連の記事監修も行い、技術の共有と普及に励んでいます。 監修実績(レバテックフリーランス
Flutter関連の書籍を出版しました!