iOS・Android

Flutter初回起動画面から簡単なAPIを叩くまで

iOS・Android
この記事は約11分で読めます。

はじめに

どうも。株式会社implのkashimaです。

ここ1ヶ月ほどでFlutterを触る機会が少しずつ増えてきまして、学習整理のため簡単に記事にしてみたいと思います。

前提

Flutterの環境構築が終わっていることが前提です。念の為載せておきます。

公式よりこちらの方がぶっちゃけわかりやすかったのでこちらのリンクを貼っておきます。

Flutterの環境構築(Mac編)|Flutter基礎入門 by Flutter大学

プロジェクト作成

まずはターミナルの任意のディレクトリから自由な名前でプロジェクトを作成します。

flutter create connectapi

プロジェクトのディレクトリを開き、適当なiOSのシミュレータを立ち上げた上でVSコード左のデバッグボタンを押すとiOSのシミュレータが立ち上がって初期画面が表示されると思います。

これで下準備は完了です。

実装する

初期コードのリファクタリング

  1. main.dartのコメントアウトを全て消します。
  2. libディレクトリ配下に別ファイルを作成し、MyHomePage以降を別ファイルに移動します。home.dartとか適当な名前でOKです。

lib/main.dart

import 'package:connectapi/home.dart';
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Sample',
      home: MyHomePage(title: 'Flutter Sample'),
    );
  }
}

lib/home.dart

import 'package:flutter/material.dart';

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(() {});
  }

  @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.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

APIを取得するための関数を作成する

使用するAPI

今回使用するAPIはこちらです。

ランダムでyesかnoの返答と何かしらの画像が出てくる謎APIです。

Yes No API - PublicAPI
Yes No API provides Generate yes or no randomly

リクエストを送るための非同期処理

リクエストを送るのに必要なHTTPパッケージをターミナルからインストールします。

flutter pub add http

Futureで非同期処理にして、データを更新、描画するようにしてみます。

非同期処理とは時間のかかるような処理を実行中でも別の処理を行うことができる処理の方法です。

パスタを茹でてる間に野菜と肉を切っとくみたいなイメージです。

dynamic responseState;
  Future<void> getData() async {
    final response = await http.get(Uri.https('yesno.wtf', '/api'));
    final jsonResponse = jsonDecode(response.body);
    setState(() {
      responseState = jsonResponse;
    });
  }

上のコードをざっくり説明すると

  1. responseStateというdynamic型の変数を用意しておく(こいつに最終的にデータが入ってくる)
  2. http.getというメソッドでAPIのエンドポイントを叩く -> final response
  3. レスポンスをJSON型に変換する -> final jsonResponse
  4. setStateでデータを更新する

今こんな感じ

いったんエラー無視で進めます。

lib/home.dart

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; // HTTPリクエスト用パッケージ
import 'dart:convert'; // JSONへの変換用パッケージ

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  dynamic responseState;
  Future<void> getData() async {
    final response = await http.get(Uri.https('yesno.wtf', '/api'));
    final jsonResponse = jsonDecode(response.body);
    setState(() {
      responseState = jsonResponse;
    });
  }

  @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.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

実際に画面に表示する

Columnウィジェット内にウィジェットを追加し、実際に画面に表示してみましょう

responseStateの中に入ってきたデータを表示するためにText , Image , ElevatedButtonを追加します。

lib/home.dart

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; // HTTPリクエスト用パッケージ
import 'dart:convert'; // JSONへの変換用パッケージ

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  dynamic responseState;
  Future<void> getData() async {
    final response = await http.get(Uri.https('yesno.wtf', '/api'));
    final jsonResponse = jsonDecode(response.body);
    setState(() {
      responseState = jsonResponse;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Sample'),
      ),
      body: Center(
        child: Column(
          children: [
            Text(
              responseState == null ? 'Yes or No' : responseState['answer'],
              style: const TextStyle(fontSize: 30),
            ),
            SizedBox(
              width: 250,
              height: 250,
              child: Image.network(responseState == null
                  ? 'https://yesno.wtf/assets/yes/11-a23cbde4ae018bbda812d2d8b2b8fc6c.gif'
                  : responseState['image']),
            ),
            ElevatedButton(
              onPressed: getData,
              child: const Text('APIを叩くよ'),
            )
          ],
        ),
      ),
    );
  }
}

responseStateの初期値がnullの状態になっているので初期値を適当に設定し、ElevatedButtonを押下すると、レスポンスがTextとImageに表示されるようになると思います。

動作確認

最後に

まだまだdartとflutterの記法に慣れませんね。。

Null Safetyを活かしてもっと綺麗な書き方ができるように勉強しときます。