[Flutter]http通信を確立する


dartのhttpというライブラリを用いて、Flutterのアプリでhttpの通信ができるようにします。
これによってAPIサーバーからデータを取得することができます。


![20210509_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333161/1e8ab85a-469a-a8d2-9a61-b5b600e11161.jpeg)

pubspec.yamlのdependenciesに今回使うライブラリhttpを追加します。バージョンは自分の環境に合わせて変更してください。今回は記事執筆時最新のバージョンを適用します。


```yaml:pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.3 # この行を追加
```

flutter pub getコマンドで適用します。

```
flutter pub get
```

それではhttp通信を実装していきます。flutter createで作成したプロジェクトのコメント部分を削除したmain.dartを用意します。

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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

  final String title;

  @override
  _MyHomePageState 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>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
```

最初にライブラリをインポートします。httpと一緒に、json形式のデータを操作するdart:convertもインポートしておきます。

```dart:main.dart
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
```

次にhttp通信を実現する関数を用意します。
関数はFutureで定義します。戻り値は指定しないのでFuture<void>とします。変数urlでAPIサーバーのurlを指定します。
無事値を取得できたら、if文内のresponse.statusCodeが200となり、今回のコードではitemCountに値を代入します。
そうでなければ、printで情報が取得できなかったことを出力します。

```dart:main.dart
 Future<void> _handleHttp() async {
   var url = Uri.https('www.googleapis.com', '/books/v1/volumes', {'q': 'Flutter'});

   var response = await http.get(url);
   if (response.statusCode == 200) {
     var jsonResponse = convert.jsonDecode(response.body) as Map<String, dynamic>;
     var itemCount = jsonResponse['totalItems'];
     print('Number of books about http: $itemCount.');
   } else {
     print('Request failed with status: ${response.statusCode}.');
   }
 }
```

それでは作成した関数の動作確認を行います。
main.dartのfloatingActionButtonを書き換えます。onPressedの値を_handleHttpにして、ボタンを押したらAPIサーバーに情報を受け取りにいくようにします。

```dart:main.dart
floatingActionButton: FloatingActionButton(
  onPressed: _handleHttp,
  tooltip: 'Increment',
  child: Icon(Icons.add),
), 
```

以下、書き換えたコードの全体を記します。

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

import 'dart:convert' as convert;
import 'package:http/http.dart' as http;

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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

  final String title;

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

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

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

  Future<void> _handleHttp() async {
    // This example uses the Google Books API to search for books about http.
    // https://developers.google.com/books/docs/overview
    var url = Uri.https('www.googleapis.com', '/books/v1/volumes', {'q': 'Flutter'});

    // Await the http get response, then decode the json-formatted response.
    var response = await http.get(url);
    if (response.statusCode == 200) {
      var jsonResponse = convert.jsonDecode(response.body) as Map<String, dynamic>;
      var itemCount = jsonResponse['totalItems'];
      print('Number of books about http: $itemCount.');
    } else {
      print('Request failed with status: ${response.statusCode}.');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _handleHttp,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
```

ボタンを押したら、コンソールにAPIサーバーから取得した値が表示されます。

```
I/flutter ( 1572): Number of books about http: 669.
```

せっかくなので、APIサーバーから取得した値をアプリ上で表示できるようにします。_MyHomePageStateに_wordを追加し、ボタンを押してら_wordの値が書き変わるようにしました。

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

import 'dart:convert' as convert;
import 'package:http/http.dart' as http;

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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

  final String title;

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

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

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

  Future<void> _handleHttp() async {
    var url = Uri.https('www.googleapis.com', '/books/v1/volumes', {'q': 'Flutter'});

    var response = await http.get(url);
    if (response.statusCode == 200) {
      var jsonResponse = convert.jsonDecode(response.body) as Map<String, dynamic>;
      var itemCount = jsonResponse['totalItems'];
      print('Number of books about http: $itemCount.');
      setState(() {
        _counter = itemCount;
        _word = 'Flutter';
      });
    } else {
      print('Request failed with status: ${response.statusCode}.');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              '「$_word」の検索結果:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _handleHttp,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
```

以下、実際に動かしたアプリの画面です。

ボタンを押す前

![20210509_01.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333161/392791e1-3400-f948-d048-21ef140eeea1.jpeg)

ボタンを押した後

![20210509_02.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333161/14262384-b0b1-f1af-f506-f0daa0c4bbae.jpeg)

以上、dartのライブラリhttpを用いて、APIサーバーから値を取得するコードでした。

APIの通信にはDioというライブラリを使う方法もあるようなので、いずれDioも試してみたいと思います。