Flutter 如何刷新 StreamBuilder?

标签 flutter dart stream listener onchange

考虑以下代码:

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

我希望 StreamBuilder 在 _myLimit 时刷新可变的变化。
可以这样做:
void _incrementLimit(){
    setState(() =>_myLimit++);
}

我的问题是,除了 setState((){}); 是否还有其他更快的方法一。
因为我不想记忆整个build() _myLimit 时的方法可变的变化。

我想出了另一种方法,但我觉得有更好的解决方案,因为我认为我没有使用 .periodic功能,我有一个嵌套的 Stream 我不确定这有多常见:
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());
  },
),

最佳答案

声明 StreamControllerbroadcast ,然后将友好名称设置为 Stream这个StreamController ,然后每次你想重建包装的小部件(StreamBuilder 的子级,只需使用 sinkStreamController 属性到 add 一个将触发 StreamBuilder 的新值。

您可以使用 StreamBuilderAsyncSnapshot不设置类型。

但是如果你使用 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());
  },
)

这样您就可以使用 StatelessWidgetrefresh just a single sub-widget (例如,不同颜色的图标)without using setState (重建整个页面)。

对于性能,流是最好的方法。

编辑:
我正在使用 BLoC architecture方法,因此最好在 homePageBloc.dart 中声明变量(具有具有所有业务逻辑的普通 Controller 类)并在 homePage.dart 中创建 StreamBuilder(具有扩展无状态/有状态小部件的类并负责用户界面)。

编辑:我的UserModel.dart , 你可以使用 DocumentSnapshot而不是 Map<String, dynamic>如果您使用 Firebase 中的 Cloud Firestore 数据库。
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; ";
  }
}

关于Flutter 如何刷新 StreamBuilder?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60663818/

相关文章:

Flutter- GestureDetector 检测水平和垂直拖动的方向

java - 为 android 和 ios 创建 flutter 插件会在 java 文件 android studio 中出现错误

android - Flutter 文本按钮波纹效果

C++ 流 : Insert string after newline

c# - 计算大字符串的散列时如何避免分配大 Byte[]

android - 为什么gradle无法为flutter插件打开cp_proj重新映射的类缓存

Flutter:什么时候应该使用工厂构造函数?

firebase - 迅速获得Firebase中的所有注册用户

dart - 如何在Flutter中自定义带有翻转动画的AnimatedCrossFade?

Scala:将文件读取为无限/异步行流