FlutterでKintoneにデータを記録してみる

Flutterのデフォルトのカウンターアプリで増えた数だけKintoneにデータを送って記録してみました。
まずは環境構築からしていきます。FlutterのSDKをインストールしPATHを通したらflutterコマンドを入力できるか確認します。
```
flutter --version

Flutter 3.3.1 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 4f9d92fbbd (5 days ago) • 2022-09-06 17:54:53 -0700
Engine • revision 3efdf03e73
Tools • Dart 2.18.0 • DevTools 2.15.0
```
VSCodeのターミナルから、`command + shift + P`でFlutter: New Projectを選択。
![スクリーンショット 2022-09-12 17.56.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/9d53643b-4710-1504-af1c-1ed0e0a9f15a.png)
Applicationを選択して
![スクリーンショット 2022-09-12 17.57.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/f7e0677e-9ef3-18ee-5cb9-11320b056d22.png)
プロジェクトネームを入力して`Enter`
![スクリーンショット 2022-09-12 18.05.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/bc619812-07d7-190a-ae27-b4649727f09e.png)
generatorが走ってデフォルトのアプリが立ち上がります。
![スクリーンショット 2022-09-12 20.24.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/19c30245-41a6-44a2-778b-76a4523131ce.png)
iOSのシュミレーターを設定して、
![スクリーンショット 2022-09-12 20.27.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/adc37a08-48dc-3999-5708-8e60fa23d45c.png)
ターミナルのコマンドで`flutter run`しましょう。
![スクリーンショット 2022-09-12 20.32.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/104ec48a-0267-e2cc-4906-2ad8e85fa332.png)
上手くビルドされれば、下記のようなメッセージと共にアプリが起動します。
![スクリーンショット 2022-09-12 20.34.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/aeac02a4-40b9-ad21-acef-9febc9cf6430.png)
![スクリーンショット 2022-09-13 17.23.12 小.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/99912a82-d546-1d4a-0c1a-806464635d9a.png)
<details><summary>デフォルトの`main.dart`のコードはこちらになります。</summary><div>

```lib/main.dart
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}
```
</div></details>
書き換える前にまずは、

![スクリーンショット 2022-09-13 8.29.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/6d9b763b-fbf5-75dd-f7ef-c45394f56d70.png)
`.env`を使用するのでGitHubに上がらないよう`.gitignore`に`.env`を追加します。
![スクリーンショット 2022-09-13 8.38.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/b2f2b26a-374e-5ade-b45c-5a8e770cbfb4.png)
ルート直下に`.env`を作成し、Kintoneよりドメインなどを取得してきて`XXXXXX`の部分を修正して下さい。
![スクリーンショット 2022-09-13 17.37.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/7690e233-7f30-d497-b959-d54e641df337.png)
この`XXXXXX`がドメインとURLに入ります。
[APIトークンはこちらの取得方法をご確認ください。](https://developer.cybozu.io/hc/ja/articles/202463840-API%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86)ちなみにAPIトークンでREST APIを実行する際にはユーザIDとパスワードは必要ありません。
![スクリーンショット 2022-09-13 17.48.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/7e956a0a-10ad-fe04-2bec-82e73bfd0fcc.png)
`ID`は`URL`の最後の数字になります。`lib内`に`enb.dart`ファイルを作成して`.env`から`main.dart`に繋ぐ実装を書きます。

```lib/env.dart
import 'package:flutter_dotenv/flutter_dotenv.dart';

String? domain = dotenv.env['KINTONE_DOMAIN'];
String? baseUrl = dotenv.env['KINTONE_BASE_URL'];
String? apiToken = dotenv.env['KINTONE_APITOKEN'];
String? id = dotenv.env['KINTONE_ID'];
```


<details><summary>そして`main.dart`にKintoneにデータを送るAPIの実装を追加します。</summary><div>

```lib/main.dart
import 'package:flutter/material.dart';
import 'package:kintone_flutter/env.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

Future main() async {
  runApp(const MyApp());
  await dotenv.load(fileName: ".env");
}

final Map<String, String> postHeader = {
  'X-Cybozu-API-Token': apiToken!,
  'Content-Type': 'application/json'
};

final Map<String, String> getHeader = {
  'X-Cybozu-API-Token': apiToken!,
};

Future<Map<String, dynamic>> postRecord(int number) async {
  final Map<String, dynamic> postRecord = {
    'app': id,
    'record': {
      'number': {'value': number},
    }
  };

  return await http
      .post(Uri.parse('$baseUrl/record.json'),
          headers: postHeader, body: jsonEncode(postRecord))
      .then(((response) {
    Map<String, dynamic> rec = jsonDecode(response.body);
    return rec;
  }));
}

Future<Map<String, dynamic>> fetchRecords(String query) async {
  String encodedQuery = Uri.encodeFull(query);
  return await http
      .get(
          Uri.parse(
            '$baseUrl/records.json?app=$id&query=$encodedQuery',
          ),
          headers: getHeader)
      .then(((response) {
    Map<String, dynamic> rec = jsonDecode(response.body);
    return rec;
  }));
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
      fetchRecords('');
      postRecord(_counter);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}
```
</div></details>

![スクリーンショット 2022-09-13 10.22.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/9d49b554-47c7-312f-789e-0ff0604ef5a2.png)
pubspec.yaml内に[http:](https://pub.dev/packages/http)と[flutter_dotenv:](https://pub.dev/packages/flutter_dotenv)を最新版で追記して、`flutter pub get`すれば、`main.dart`で`import`が呼ばれるようになりerrorが消えます。

![スクリーンショット 2022-09-13 10.35.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/4e65587a-725a-00af-efdc-befaef59dc6c.png)
Kintone側では、カウンターを受ける数値を入れる変数(文字列)を用意します。
![スクリーンショット 2022-09-13 10.35.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/1d73f2d4-495a-1947-459e-105a19fcc5bc.png)
フィールド名を`数値`にして`フィールドコード(変数名）`を`number`にしてアプリを作成したら、`iOSシュミレーター`を立ち上げて、カウンターを増やしてみましょう。
![スクリーンショット 2022-09-13 10.38.51 小.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/25bad884-6787-f6bc-7f7b-91db0b8c899e.png)
![スクリーンショット 2022-09-13 10.34.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/6d3cfdfa-ad2f-b724-942f-649f352203b6.png)
Kintoneにカウントした数だけデータが記録されているのを確認できました。