web-dev-qa-db-ja.com

Flutter StreamBuilderを更新するには?

次のコードを検討してください。

_StreamBuilder<QuerySnapshot> _createDataStream(){   
    return StreamBuilder<QuerySnapshot>(
          stream: Firestore.instance.collection("data").limit.(_myLimit).snapshots(),
          builder: (context, snapshot){
              return Text(_myLimit.toString);   
            }
        );
}
_

__myLimit_変数が変更されたときにStreamBuilderが更新されるようにしたい。次のようにそれを行うことが可能です:

_void _incrementLimit(){
    setState(() =>_myLimit++);
}
_

私の質問は、setState((){});以外のより高速な方法があるかどうかです。 __myLimit_変数が変更されたときにbuild()メソッド全体を呼び戻したくないので。

私は別の方法を見つけましたが、_.periodic_機能を利用していないと思い、ネストされたストリームを取得したため、さらに良い解決策があるように感じます。

_Stream<int> myStream = Stream.periodic(Duration(), (_) => _myLimit);
...
@override
Widget build(BuildContext context){
...
return StreamBuilder<int>(
                    stream: myStream,
                    builder: (context, snapshot){
                      return _createDataStream;
                    },
                  ),
...
}
_

ソリューション

_import 'Dart:async';
import 'package:flutter/material.Dart';

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

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new _MyAppState();
  }
}

class _MyAppState extends State<MyApp> {

  int myNum = 0;

  final StreamController _myStreamCtrl = StreamController.broadcast();
  Stream get onVariableChanged => _myStreamCtrl.stream;
  void updateMyUI() => _myStreamCtrl.sink.add(myNum);

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    _myStreamCtrl.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child:
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
            StreamBuilder(
              stream: onVariableChanged,
              builder: (context, snapshot){
                if(snapshot.connectionState == ConnectionState.waiting){
                  updateMyUI();
                  return Text(". . .");
                }
                return Text(snapshot.data.toString());
              },
            ),
            RaisedButton(
              child: Text("Increment"),
              onPressed: (){
                myNum++;
                updateMyUI();
                },
        )
        ],
      ),
    )));
  }
}

_

他のいくつかのアイデア、StreamBuilderもどのように見えるか:

_StreamBuilder(
  stream: onVariableChanged,
  builder: (context, snapshot){
    if(snapshot.connectionState == ConnectionState.waiting){
      return Text(myNum.toString());
    }
    return Text(snapshot.data.toString());
  },
),
_
_StreamBuilder(
  stream: onVariableChanged,
  initialData: myNum,
  builder: (BuildContext context, AsyncSnapshot snapshot){
    if(snapshot.data == null){
      return Text("...");
    }
    return Text(snapshot.data.toString());
  },
),
_
1
tonik

StreamControllerbroadcastで宣言し、わかりやすい名前をこのStreamStreamControllerに設定します。その後、ラップされたウィジェット(子)を再構築するたびにStreamBuildersinkからStreamControllerをトリガーする新しい値をaddStreamBuilderプロパティを使用するだけです。

タイプを設定せずにStreamBuilderおよびAsyncSnapshotを使用できます。

ただし、_StreamBuilder<UserModel>_を入力するときに_AsyncSnapshot<UserModel>_および_snapshot.data._を使用すると、UserModelのすべての変数とメソッドが表示されます。

_final StreamController<UserModel> _currentUserStreamCtrl = StreamController<UserModel>.broadcast();
Stream<UserModel> get onCurrentUserChanged => _currentUserStreamCtrl.stream;
void updateCurrentUserUI() => _currentUserStreamCtrl.sink.add(_currentUser);

StreamBuilder<UserModel>(
  stream: onCurrentUserChanged,
  builder: (BuildContext context, AsyncSnapshot<UserModel> snapshot) {
    if (snapshot.data != null) {
      print('build signed screen, logged as: ' + snapshot.data.displayName);
      return blocs.pageView.pagesView; //pageView containing signed page 
    }
    print('build login screen');
    return LoginPage(); 

    //print('loading');
    //return Center(child: CircularProgressIndicator());
  },
)
_

このようにして、StatelessWidgetおよび_refresh just a single sub-widget_(たとえば、異なる色のアイコン)_without using setState_(ページ全体を再構築する)を使用できます。

パフォーマンスに関しては、ストリームが最良のアプローチです。

編集: 私は_BLoC architecture_アプローチを使用しているので、homePageBloc.Dart(すべてのビジネスロジックを持つ通常のコントローラークラスを持つ)で変数を宣言し、homePage.Dart(クラスを持つ)でStreamBuilderを作成する方がはるかに優れていますステートレス/ステートフルウィジェットを拡張し、UIを担当します)。

編集: 私の_UserModel.Dart_、FirebaseのCloud Firestoreデータベースを使用している場合、_Map<String, dynamic>_の代わりにDocumentSnapshotを使用できます。

_class UserModel {

  /// Document ID of the user on database
  String _firebaseId = ""; 
  String get firebaseId => _firebaseId;
  set firebaseId(newValue) => _firebaseId = newValue;

  DateTime _creationDate = DateTime.now();
  DateTime get creationDate => _creationDate;

  DateTime _lastUpdate = DateTime.now();
  DateTime get lastUpdate => _lastUpdate;

  String _displayName = "";
  String get displayName => _displayName;
  set displayName(newValue) => _displayName = newValue;

  String _username = "";
  String get username => _username;
  set username(newValue) => _username  = newValue;

  String _photoUrl = "";
  String get photoUrl => _photoUrl;
  set photoUrl(newValue) => _photoUrl = newValue;

  String _phoneNumber = "";
  String get phoneNumber => _phoneNumber;
  set phoneNumber(newValue) => _phoneNumber = newValue;

  String _email = "";
  String get email => _email;
  set email(newValue) => _email = newValue;

  String _address = "";
  String get address => _address;
  set address(newValue) => _address = newValue;

  bool _isAdmin = false;
  bool get isAdmin => _isAdmin;
  set isAdmin(newValue) => _isAdmin = newValue;

  /// Used on first login
  UserModel.fromFirstLogin() {
    _creationDate     = DateTime.now();
    _lastUpdate       = DateTime.now();
    _username         = "";
    _address          = "";
    _isAdmin          = false;
  }

  /// Used on any login that isn't the first
  UserModel.fromDocument(Map<String, String> userDoc) {
    _firebaseId           = userDoc['firebaseId']  ?? '';
    _displayName          = userDoc['displayName'] ?? '';
    _photoUrl             = userDoc['photoUrl'] ?? '';
    _phoneNumber          = userDoc['phoneNumber'] ?? '';
    _email                = userDoc['email'] ?? '';
    _address              = userDoc['address'] ?? '';
    _isAdmin              = userDoc['isAdmin'] ?? false;
    _username             = userDoc['username'] ?? '';
    //_lastUpdate           = userDoc['lastUpdate'] != null ? userDoc['lastUpdate'].toDate() : DateTime.now();
    //_creationDate         = userDoc['creationDate'] != null ? userDoc['creationDate'].toDate() : DateTime.now();
  }

  void showOnConsole(String header) { 

    print('''

      $header

      currentUser.firebaseId: $_firebaseId
      currentUser.username: $_username
      currentUser.displayName: $_displayName
      currentUser.phoneNumber: $_phoneNumber
      currentUser.email: $_email
      currentUser.address: $_address
      currentUser.isAdmin: $_isAdmin
      '''
    );
  }

  String toReadableString() {
    return  
      "displayName: $_displayName; "
      "firebaseId: $_firebaseId; "
      "email: $_email; "
      "address: $_address; "
      "photoUrl: $_photoUrl; "
      "phoneNumber: $_phoneNumber; "
      "isAdmin: $_isAdmin; ";
  }
}
_
2