Flutterアプリを開発していますが、UIから非同期コードを呼び出すときはどうすればよいのでしょうか。UIウィジェットのビルドメソッドについて考えてみましょう。
たとえば、私のアプリは、Async-Awaitスタイルを使用してFirebaseからレコードを取得するサービスクラスを介してFirebaseに接続します。 awaitを使用すると、サービスクラスのメソッドがUI側に戻る前にレコードの取得を完了します。
ただし、サービスクラスのメソッドは非同期としてマークされているため、呼び出し側のUIウィジェットにすぐにFutureを返し、サービスクラスのメソッドが完了する前にUIコードが実行され続けます。
はい、 "then()"コールバック用のコードを記述して、取得したレコードを処理できますが、ウィジェット内の非同期呼び出しの結果に依存する他のコードはどうでしょうか。 「then()」句内にすべてを埋め込み、Futureを返すメソッド呼び出しの後に実行ステップを追加しないようにする必要があるということですか?そのような使用モデルの推奨パターンはありますか?
@override
Widget build(BuildContext context) {
RealtimeDatabase.getData() // my service class
.then((onValue) {
..... // do something after the async call is completed
}
..... // do something immediately before the async call is done
class RealtimeDatabase {
Future<String> getData() async {
DataSnapshot dataSnapshot = await FirebaseDatabase.instance.reference().orderByKey().once(); // will wait until completed
.....
.....
ここでの説明が明確でない場合は申し訳ありませんが、アドバイスは大歓迎です。
ジミー
Flutterには、これをシームレスに行うのに役立つウィジェットがあります(たとえば FutureBuilder
、 StreamBuilder
)、何をするかを制御できます解像度に基づいてレンダリングされます。
FutureBuilder
の例:
Widget build(BuildContext context) {
return new FutureBuilder(
future: FirebaseDatabase.instance.reference().child("node"),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return snapshot.hasData? new Scaffold(
///start building your widget tree
):new CircularProgressIndicator(); ///load until snapshot.hasData resolves to true
},);
}
StreamBuilder
の例:
class Database {
DatabaseReference _refProfile = FirebaseDatabase.instance.reference().child(
"profiles");
getProfiles() => _refProfile.onValue; }
.............
Widget build(BuildContext context) {
return new StreamBuilder<Event>(
stream: _database.getProfiles(), //_database = new Database()
builder: (BuildContext context, AsyncSnapshot<Event> event) {
return event.hasData?new Scaffold(
///build your widget tree
):new CircularProgressIndicator();
/// place holder
}
言及する価値があります
FutureBuilder
は、fetch一部のデータonceを取得し、一貫性のある接続を必要としない場合、またはデータの変更を追跡します。
一方、StreamBuilder
を使用すると、データをリッスンし続けることができ、データの更新に応じてUIの状態を更新できます。
@azizaが言及している方法は間違いなく進むべき道であると思いますが、「独自にロールする」こともできます。
したがって、たとえば、initState()
では、次のようなことができます。
@override
void initState() {
super.initState();
_myItems = // some sane default
FirebaseDatabase.instance.reference().child("node")
.then((items) {
if (widget.mounted) setState(() { _myItems = items; }
});
}
Widget build(BuildContext context) {
return new Scaffold(
///start building your widget tree
);
}
ここではFutureBuilder
を使用することをお勧めしますが、これは別の方法です(必ずしもベストプラクティスではない場合でも)。