開発者ドキュメント

DartとFlutterで先物とストリームを開始する方法

序章

多くの場合、アプリケーションは外部APIおよびデータベースと対話する機能を必要とします。 これに伴い、特定の要求と操作が完了するのを待っている間に、記述された方法とは異なる順序で実行されるコードを処理するという問題が発生します。

この記事では、 Dart 、特にFlutterが非同期リクエストでどのように機能するかを探ります。

前提条件

このチュートリアルを完了するには、次のものが必要です。

このチュートリアルは、Flutter v2.0.6、Android SDK v31.0.2、およびAndroidStudiov4.1で検証されました。

非同期コードを理解する

同期コードでは、外部APIに情報のリクエストを送信すると、応答が得られるまでに時間がかかります。 私たちのマシンはそれが完了するのを待って、最初の要求とは関係がないかもしれないことを停止します。 問題は、何かが時間がかかるたびにスクリプトの実行を停止したくないことですが、戻りデータに依存するものを時期尚早に実行したくないため、要求が成功したにもかかわらずエラーが発生する可能性があります。

両方の長所は、リクエストが返されるのを待っている間、そのリクエストに依存するコードが利用可能になったときにのみ実行できるように、マシンが先に進むようにロジックを設定することです。

私たちのアプリのデータは、それらがすでに利用可能であるかどうか、およびそれらが特異であるかどうかに応じて、4つの形式のいずれかになります。

この例では、先物とストリームについて説明します。

プロジェクトの設定

Flutter用に環境を設定したら、次のコマンドを実行して新しいアプリケーションを作成できます。

  1. flutter create flutter_futures_example

新しいプロジェクトディレクトリに移動します。

  1. cd flutter_futures_example

flutter createを使用すると、ボタンがクリックされた回数を表示するデモアプリケーションが作成されます。

この例の一部は、 REST CountriesAPIに依存しています。 国名を指定すると、このAPIは国に関する情報を返します。 たとえば、Canadaのリクエストは次のとおりです。

https://restcountries.eu/rest/v2/name/Canada

これには、httpパッケージも必要です。

コードエディタでpubspec.yamlを開き、次のプラグインを追加します。

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  
  http: 0.13.3

次に、コードエディタでmain.dartを開き、次のコード行を変更して、 GetCountryボタンを表示します。

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

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  void getCountry() {}

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: MaterialButton(
          onPressed: () => getCountry(),
          child: Container(
            color: Colors.blue,
            padding: EdgeInsets.all(15),
            child: Text('Get Country', style: TextStyle(color: Colors.white))
          ),
        ),
      ),
    );
  }
}

この時点で、httpパッケージを使用した新しいFlutterプロジェクトができました。

thenおよびcatchErrorを使用する

try…catchinJavaScript と非常によく似ており、Dartを使用するとメソッドをチェーン化できるため、戻りデータを次のデータに簡単に渡すことができ、Futuresと呼ばれるPromiseのようなデータ型も返すことができます。 先物は、文字列などの特異なタイプのデータであり、後で利用できるようになります。

この手法を使用するには、操作を実行してから、返されたデータをパラメーターとして渡して.thenをチェーンし、必要に応じて使用します。 その時点で、追加の.thenをチェーンし続けることができます。 エラー処理の場合は、最後に.catchErrorを使用して、渡されたものをすべてスローします。

コードエディタでmain.dartに戻り、.then.catchErrorを使用します。 まず、void GetCountry() {}Future GetCountry(country)に置き換えます。 次に、国名をonPressed: () => GetCountry()に追加します。

lib / main.dart
// ...

class MyHomePage extends StatelessWidget {
  Future getCountry(country) {
    Uri countryUrl = Uri.http('restcountries.eu', '/rest/v2/name/$country');

    http
      .get(countryUrl)
      .then((response) => jsonDecode(response.body)[0]['name'])
      .then((decoded) => print(decoded))
      .catchError((error) => throw(error));
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: MaterialButton(
          onPressed: () => getCountry('Canada'),
          child: Container(
            color: Colors.blue,
            padding: EdgeInsets.all(15),
            child: Text('Get Country', style: TextStyle(color: Colors.white))
          ),
        ),
      ),
    );
  }
}

変更を保存し、シミュレーターでアプリケーションを実行します。 次に、国を取得ボタンをクリックします。 コンソールは国の名前を記録します。

asyncおよびawaitを使用する

多くの人が読みやすいと思う代替構文はAsync/Awaitです。

Async / Awaitは、JavaScript とまったく同じで動作します。関数名の後にasyncキーワードを使用し、実行に時間がかかるものの前にawaitキーワードを追加します。 getリクエストのように。

コードエディタでmain.dartに戻り、asyncawaitを使用します。 これで、値が返されたときに、それ以降のすべてが実行されます。 エラー処理の場合、try/catchブロックでエラーをスローできます。

lib / main.dart
// ...

class MyHomePage extends StatelessWidget {
  Future getCountry(country) async {
    Uri countryUrl = Uri.http('restcountries.eu', '/rest/v2/name/$country');

    try {
      http.Response response = await http.get(countryUrl);
      Object decoded = jsonDecode(response.body)[0]['name'];
      print(decoded);
    } catch (e) { throw(e); }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: MaterialButton(
          onPressed: () => getCountry('Canada'),
          child: Container(
            color: Colors.blue,
            padding: EdgeInsets.all(15),
            child: Text('Get Country', style: TextStyle(color: Colors.white))
          ),
        ),
      ),
    );
  }
}

変更を保存し、シミュレーターでアプリケーションを実行します。 次に、国を取得ボタンをクリックします。 コンソールは国の名前を記録します。

ストリームの使用

Dartの特別な点は、非同期でロードされる多くの値がある場合にStreamsを使用することです。 GETリクエストのように一度接続を開く代わりに、接続を開いたままにして新しいデータの準備をすることができます。

この例では、 Firebase やGraphQLを使用するなど、Streamsを許可するバックエンドを設定すると少し複雑になるため、新しい「メッセージ」を発行してチャットアプリケーションデータベースの変更をシミュレートします。一秒ごと。

StreamControllerクラスを使用してストリームを作成できます。これは、先物のリストのように動作するため、Listと同様に機能します。

listencloseなどのstreamのプロパティを使用してストリームを制御し、ストリームを開始および停止できます。

警告:ウィジェットを削除するときは、常にclose()を使用することが重要です。 ストリームは、シャットオフされるまで継続的に実行され、元のウィジェットがなくなった場合でもコンピューティング能力を消費します。

次に、コードエディタでmain.dartを開き、次のコード行を置き換えてdart:asyncをインポートし、StatefulWidgetを使用します。

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

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  StreamController<String> streamController = StreamController();

  void newMessage(int number, String message) {
    final duration = Duration(seconds: number);

    Timer.periodic(duration, (Timer t) => streamController.add(message));
  }

  void initState() {
    super.initState();

    streamController.stream.listen((messages) => print(messages));

    newMessage(1, 'You got a message!');
  }

  void dispose() {
    streamController.close();

    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          padding: EdgeInsets.all(15),
          child: Text('Streams Example'),
        ),
      ),
    );
  }
}

このコードは、コンソールにYou got a messageを継続的に出力します。

標準ストリームは、一度に1人のリスナーしか許可しないという点で少し制限される可能性があります。 代わりに、StreamControllerクラスのbroadcastプロパティを使用して、複数のチャネルを開くことができます。

lib / main.dart
// ...

StreamController<String> streamController = StreamController.broadcast();

// ...

void initState() {
  super.initState();

  streamController.stream.listen((messages) => print('$messages - First'));
  streamController.stream.listen((messages) => print('$messages - Second'));

  newMessage(1, 'You got a message!');
}

// ...

このコードは、コンソールにYou got a message - FirstYou got a message - Secondを継続的に出力します。

結論

この記事では、Dart、特にFlutterが非同期リクエストでどのように機能するかを探りました。 Dartの非同期プログラミングにより、インテリジェントで動的なアプリの開発を開始できます。

Flutterについて詳しく知りたい場合は、Flutterトピックページで演習とプログラミングプロジェクトを確認してください。

モバイルバージョンを終了