Closed6

FlutterのREST APIクライアントを使ってみる

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Chopper

準備コマンド

dart create hello_chopper
cd hello_chopper
dart pub get chopper
dart pub get --dev build_runner chopper_generator
dart pub get
touch api.js

ソースコード

hello_chopper/bin/hello_chopper.dart
import 'package:chopper/chopper.dart';

part "hello_chopper.chopper.dart";

(baseUrl: "/todos")
abstract class TodosListService extends ChopperService {
  static TodosListService create([ChopperClient? client]) =>
      _$TodosListService(client);

  ()
  Future<Response> getTodos();
}

Future<void> main() async {
  final chopper = ChopperClient(
    baseUrl: "http://localhost:8000",
    services: [
      TodosListService.create(),
    ],
  );

  final todosService = chopper.getService<TodosListService>();

  final response = await todosService.getTodos();

  if (response.isSuccessful) {
    final body = response.body;
    print(body);
  } else {
    final code = response.statusCode;
    final error = response.error;
    print("code = $code, error = $error");
  }
}
api.js
const http = require('http');
const server = http.createServer();

server.on('request', (req, res) => {
  const content = JSON.stringify([
    {title: 'Todo 1', completed: true},
    {title: 'Todo 2', completed: false},
    {title: 'Todo 3', completed: false},
  ], null, 2);

  res.writeHead(200, {
    'Content-Type': 'application/json',
    'Content-Length': '' + content.length,
  });

  res.write(content);
  res.end();
})

const port = parseInt(process.env.PORT || '8000', 10);
server.listen(port, () => console.info(`Listening on ${port}`));

実行コマンド

dart pub run build_runner build
dart run

実行結果

[
  {
    "title": "Todo 1",
    "completed": true
  },
  {
    "title": "Todo 2",
    "completed": false
  },
  {
    "title": "Todo 3",
    "completed": false
  }
]

コメント

Chopperは公式ドキュメントが充実してなくて初心者にはつらい

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

dio

準備コマンド

dart create hello_dio
cd hello_dio
dart pub add dio
dart pub get
touch api.js

コード

hello_dio/bin/hello_dio.dart
import 'package:dio/dio.dart';

Future<void> main() async {
  try {
    final response = await Dio().get("http://localhost:8000/todos");
    print(response);
    print(response.data[0]["title"]);
  } catch (err) {
    print(err);
  }
}

api.js
const http = require('http');
const server = http.createServer();

server.on('request', (req, res) => {
  const content = JSON.stringify([
    {title: 'Todo 1', completed: true},
    {title: 'Todo 2', completed: false},
    {title: 'Todo 3', completed: false},
  ], null, 2);

  res.writeHead(200, {
    'Content-Type': 'application/json',
    'Content-Length': '' + content.length,
  });

  res.write(content);
  res.end();
})

const port = parseInt(process.env.PORT || '8000', 10);
server.listen(port, () => console.info(`Listening on ${port}`));

実行コマンド

dart run

実行結果

[{title: Todo 1, completed: true}, {title: Todo 2, completed: false}, {title: Todo 3, completed: false}]
Todo 1

コメント

dioはJavaScriptのfetch並みに使いやすい

でもJSONデコードなどはどうすれば良いのだろうか...

調べたところなんとJSONはデフォルトでMapにエンコードされる、すごい!

あとはjson_serializableやfreezedなどのパーサーを使えば良さそう

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

json_serializable

準備コマンド

dart create hello_json_serializable
cd hello_json_serializable
hello_json_serializable/pubspec.yaml
dependencies:
  json_annotation: ^4.7.0

dev_dependencies:
  build_runner: ^2.0.0
  json_serializable: ^6.0.0

コード

hello_json_serializable/bin/hello_json_serializable.dart
import 'dart:convert';
import 'package:json_annotation/json_annotation.dart';

part 'hello_json_serializable.g.dart';

()
class Person {
  final String firstName, lastName;
  final DateTime? dateOfBirth;

  Person({
    required this.firstName,
    required this.lastName,
    this.dateOfBirth,
  });

  factory Person.fromJson(Map<String, dynamic> json) {
    return _$PersonFromJson(json);
  }

  Map<String, dynamic> toJson() {
    return _$PersonToJson(this);
  }
}

void main() {
  final person = Person(
    firstName: "Tatsuya",
    lastName: "Susukida",
    dateOfBirth: DateTime(1987, 8, 27),
  );

  final encoded = jsonEncode(person.toJson());
  print(encoded);

  final decoded = Person.fromJson(jsonDecode(encoded));
  print(decoded.firstName);
  print(decoded.lastName);
  print(decoded.dateOfBirth);
}

実行コマンド

dart run build_runner build
dart run

実行結果

{"firstName":"Tatsuya","lastName":"Susukida","dateOfBirth":"1987-08-27T00:00:00.000"}
Tatsuya
Susukida
1987-08-27 00:00:00.000

メモ

Flutter公式ドキュメントにJSONエンコード/デコードに関するページがある

https://docs.flutter.dev/development/data-and-backend/json

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

freezed

準備コマンド

dart create hello_freezed
cd hello_freezed
dart pub add freeze_annotation json_annotation
dart pub add --dev build_runner freezed json_serializable

コード

import 'dart:convert';

import 'package:freezed_annotation/freezed_annotation.dart';

part 'hello_freezed.freezed.dart';
part 'hello_freezed.g.dart';

@freezed
class Person with _$Person {
  const factory Person({
    required String firstName,
    required String lastName,
    required int age,
  }) = _Person;

  factory Person.fromJson(Map<String, Object?> json) => _$PersonFromJson(json);
}

void main() {
  final person = Person(
    firstName: 'Tatsuya',
    lastName: 'lastName',
    age: 35,
  );

  final copied = person.copyWith();
  final encoded = jsonEncode(person.toJson());
  final decoded = Person.fromJson(jsonDecode(encoded));

  print(person);
  print(person == copied);
  print(encoded);
  print(decoded);
}

実行コマンド

dart run build_runner build
dart run

実行結果

Person(firstName: Tatsuya, lastName: lastName, age: 35)
true
{"firstName":"Tatsuya","lastName":"lastName","age":35}
Person(firstName: Tatsuya, lastName: lastName, age: 35)

メモ

公式ドキュメントの内容が充実していて素晴らしい

fromJsonには=>を使用する必要がある

Freezed will only generate a fromJson if the factory is using =>.

このスクラップは2023/01/10にクローズされました