flutterアプリからAPIをPOSTで叩いてパラメータを渡す（サーバに保存する）

# やりたいこと

* アプリからPOSTでAPIを呼び出す。
* POSTパラメータをJSONで渡して、API側で受け取ってサーバに保存する。
* APIからのレスポンスメッセージをページに表示する。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/ab26a4c4-646a-d22d-96b9-b048c720a367.png)

上記「api success. This message from API.」は、APIからレスポンスしている文字列である。


# 前提

APIは任意の環境で作成済とする。
ここでは、PHPで自作したAPIを、下記URLでアクセスできるようにしてある（ローカルでのみ稼働。ネットには公開していない）。

```
http://172.16.0.223/creator/api/flutter_post_test
```
これをPOSTで呼び出す。API側では、渡されたパラメータをサーバのログに吐き出すようにしてあるので、
パラメータが確実に渡ったことをログで確認する。
IPアドレスは、自PCに起動したVirtualBox（仮想サーバ）のアドレスである。

# APIのソース

今回はCakePHP3で実装した。id, name を受け取る。

```php
    public function flutterPostTestApi()
    {
        $id = $this->request->getData('id') ?? null;
        $name = $this->request->getData('name') ?? null;
        if (empty($id)) {
            $this->badRequest('id is empty. This message from API.');
        }
        $response = [];
        $response['id'] = $id;
        $response['message'] = 'api success. This message from API.';
        $this->httpNormalResponse($response);
    }
```

# [flutter] httpの許可 

https://qiita.com/kenichiro-yamato/items/ce7d77e9c733c24024d8

を参照。

# [flutter] main.dart に リクエスト用のデータクラスを実装する。

下記クラスをnewする際に、
idとname（例えば、会員ID（数値）と会員名（文字列）を登録するようなAPIを想定）を、
コンストラクタで受け取ってメンバ変数に保持する。

```dart
class SampleRequest {
  final int id;
  final String name;
  SampleRequest({
    this.id,
    this.name,
  });
  Map<String, dynamic> toJson() => {
    'id': id,
    'name': name,
  };
}
```

# [flutter] main.dart に APIを呼び出す処理を実装する。

APIに渡すパラメータは

```
id: 1234
name: 大和賢一郎
```
とした。

APIからは message が文字列で返ってくるので、
それを画面に表示させる処理も含める。　

```dart
Future<ApiResults> fetchApiResults() async {
  var url = "http://172.16.0.223/creator/api/flutter_post_test";
  var request = new SampleRequest(id: 1234, name: '大和賢一郎');
  final response = await http.post(url,
      body: json.encode(request.toJson()),
      headers: {"Content-Type": "application/json"});
  if (response.statusCode == 200) {
    return ApiResults.fromJson(json.decode(response.body));
  } else {
    throw Exception('Failed');
  }
}

```

# [flutter] main.dart に APIからのレスポンスを受け取るデータクラスを実装する。

APIレスポンスの仕様に合わせて、受け取る変数を実装する。
今回は message を返すように実装した。

```dart
class ApiResults {
  final String message;
  ApiResults({
    this.message,
  });
  factory ApiResults.fromJson(Map<String, dynamic> json) {
    return ApiResults(
      message: json['message'],
    );
  }
}
```

# [flutter] main.dart に画面表示などを実装する。

```dart
class _MyAppState extends State<MyApp> {
  Future<ApiResults> res;
  @override
  void initState() {
    super.initState();
    res = fetchApiResults();
  }
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'APIをPOSTで呼び出しJSONパラメータを渡す',
      theme: ThemeData(
        primarySwatch: Colors.yellow,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Api JSON Post Sample'),
        ),
        body: Center(
          child: FutureBuilder<ApiResults>(
            future: res,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text(
                  snapshot.data.message.toString() 
                  );
              } else if (snapshot.hasError) {
                return Text("${snapshot.error}");
              }
              return CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }
}

```

# 完成形 main.dart

```dart
import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  MyApp({Key key}) : super(key: key);

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Future<ApiResults> res;
  @override
  void initState() {
    super.initState();
    res = fetchApiResults();
  }
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'APIをPOSTで呼び出しJSONパラメータを渡す',
      theme: ThemeData(
        primarySwatch: Colors.yellow,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Api JSON Post Sample'),
        ),
        body: Center(
          child: FutureBuilder<ApiResults>(
            future: res,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text(
                  snapshot.data.message.toString() 
                  );
              } else if (snapshot.hasError) {
                return Text("${snapshot.error}");
              }
              return CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }
}

class ApiResults {
  final String message;
  ApiResults({
    this.message,
  });
  factory ApiResults.fromJson(Map<String, dynamic> json) {
    return ApiResults(
      message: json['message'],
    );
  }
}

Future<ApiResults> fetchApiResults() async {
  var url = "http://172.16.0.223/creator/api/flutter_post_test";
  var request = new SampleRequest(id: 1234, name: '大和賢一郎');
  final response = await http.post(url,
      body: json.encode(request.toJson()),
      headers: {"Content-Type": "application/json"});
  if (response.statusCode == 200) {
    return ApiResults.fromJson(json.decode(response.body));
  } else {
    throw Exception('Failed');
  }
}

class SampleRequest {
  final int id;
  final String name;
  SampleRequest({
    this.id,
    this.name,
  });
  Map<String, dynamic> toJson() => {
    'id': id,
    'name': name,
  };
}
```