こんにちは、テルプロです!
「Chopperを使ったAPI通信のやり方がわからない」とお悩みではないでしょうか?
本記事ではそんな悩みを解決していきます!
- Riverpod + freezed + Chopperの使い方がわかるようになる
- サンプルを用いて解説しているので理解しやすい
- コードを公開しているので、自分の環境で確かめることができる
Chopperを使ってAPI通信をする【サンプル】
事前準備
パッケージをインストール
今回使用するパッケージは以下の通りです。
dependencies:
build_runner: ^2.1.8
chopper: ^4.0.5
chopper_generator: ^4.0.5
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に反映させるというシンプルなアプリです。
それでは、Chopperを使って上記のアプリを作っていきましょう!
サンプルAPI:https://jsonplaceholder.typicode.com/posts
ソースコード
main.dart
import 'package:news_list_chopper/view/home_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'News List',
theme: ThemeData(primarySwatch: Colors.blueGrey),
home: const HomePage(),
);
}
}
model /news.dart
まず初めに、APIから取得したい内容のクラスを作成していきましょう。今回はタイトルと説明文の2つを取得していきます。
クラスの作成にはfreezedを使用しています。freezedを使用することで、不変クラスを作成することができます。
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
// 自動生成されるファイル
part 'news.freezed.dart';
part 'news.g.dart';
@freezed
class News with _$News {
factory News({
String? title,
String? body,
}) = _News;
factory News.fromJson(Map<String, dynamic> json) => _$NewsFromJson(json);
}
freezedでクラスを作成したら、下記のコマンドを回してください。
flutter pub run build_runner watch --delete-conflicting-outputs
これにより、ファイルが自動生成されます。–delete-conflicting-outputsを加えることで、クラスに変更が加えられる度に再生成されます。
modelフォルダが上記のようになっていれば問題ありません。これで取得したいデータを保持するクラスを作成することができました。
service / api_client.dart
次に、APIを呼び出しデータを取得するためのクラスを作っていきます。
APIの取得にはChopperを使用しています。
import 'package:chopper/chopper.dart';
import 'package:news_list_chopper/model/news.dart';
// 自動生成されるファイル
part 'api_client.chopper.dart';
@ChopperApi(baseUrl: 'https://jsonplaceholder.typicode.com')
abstract class ApiClient extends ChopperService {
static ApiClient create() => _$ApiClient(ChopperClient());
@Get(path: '/posts')
Future<Response> getNews();
}
上記のクラスを作成したら、先ほどと同じように下記のコマンドを回してください。
flutter pub run build_runner watch --delete-conflicting-outputs
これにより新しいファイルが生成され、Chopperを用いたAPIの取得が可能になります。
serviceフォルダが上記のようになっていれば問題ありません。これで、APIを取得するためのクラスを作成することができました。
repository / repository.dart
APIを取得するためのメソッドをrepositoryから呼び出します。メソッドを切り分けることで、APIを取得するためのメソッドだと理解しやすくなります。
import 'dart:convert';
import 'package:news_list_chopper/model/news.dart';
import 'package:news_list_chopper/service/api_client.dart';
class Repository {
Future<List<News>> getNews() {
return ApiClient.create().getNews().then((value) {
final list = json.decode(value.body) as List<dynamic>;
final result = List<News>.from(list.map((e) => News.fromJson(e)));
return result;
});
}
}
これで、APIを取得するためのメソッドをrepositoryに切り分けることができました。
view_model / provider.dart
次に、取得したAPIの状態を非同期で管理できるようにしていきます。状態管理にはRiverpodを使用しています。
今回は、非同期処理を行うのに適しているFutureProviderを使用していきます。
import 'package:news_list_chopper/model/news.dart';
import 'package:news_list_chopper/repository/repository.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// APIの取得を状態管理する
final repositoryProvider = Provider((ref) => Repository());
// APIの取得を非同期で管理する
final listProvider = FutureProvider<List<News>>((ref) async {
final repository = ref.read(repositoryProvider);
return await repository.getNews();
});
これにより、APIの取得を非同期で行えるようになりました。
home_page.dart
最後に、取得したAPIをViewに反映させる作業をしていきます。
FutureProviderのwhenメソッドを使用することで、Viewの反映、ローディング時、エラー時の3つの処理を書くことができるようになっています。
import 'package:news_list_chopper/model/news.dart';
import 'package:news_list_chopper/view_model/provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class HomePage extends ConsumerWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final asyncValue = ref.watch(listProvider);
return Scaffold(
appBar: AppBar(title: Text('News List')),
body: Center(
child: asyncValue.when(
data: (data) {
return data.isNotEmpty
? ListView(
children: data
.map(
(News news) => Card(
child: GestureDetector(
onTap: () {
showDialog(
context: context,
builder: (context) {
return SimpleDialog(
title: Text(news.title!),
children: [
SimpleDialogOption(
child: Text(news.body!),
),
],
);
},
);
},
child: ListTile(
title: Text(news.title!),
subtitle: Text(news.body!),
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/chopper_sample
まとめ
今回はChopperでAPI通信を実装する方法をご紹介しました。
Chopperを使うことで、API通信を楽に実装することができます。ご紹介したサンプルを参考に、自分で色々と試してみてください。
▼以下では、私の実体験に基づいて「Flutterの効率的な勉強法」の具体的な手順を詳しく解説しています。よろしければ、ご参考にどうぞ!
最後までご覧いただきありがとうございました。ではまた!