Flutter如何刷新StreamBuilder? [英] Flutter How to refresh StreamBuilder?

查看:73
本文介绍了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刷新.可能是这样的:

I want that the StreamBuilder refreshes when the _myLimit Variable changes. It's possible doing it like this:

void _incrementLimit(){
    setState(() =>_myLimit++);
}

我的问题是,除了 setState((){}); 之外,还有没有其他更快的方法.因为我不想在 _myLimit 变量更改时重新调用整个 build()方法.

My Question is if there is another, faster way, except the setState((){}); one. Because I don't want to recall the whole build() method when the _myLimit Variable changes.

我想出了另一种方法,但是我觉得有一个更好的解决方案,因为我认为我没有使用 .periodic 功能,并且我不确定嵌套的Stream这是多么平常:

I figured out another Way but I feel like there is a even better solution because I think I don't make use of the .periodic functionality and I got a nested Stream I'm not sure how usual this is:

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的外观也可能是这样:

Some other ideas, how the StreamBuilder also could look like:

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

推荐答案

使用 broadcast 声明一个 StreamController ,然后将一个友好名称设置为 Stream StreamController 的code>,然后每次您要重建包装的小部件时( StreamBuilder 的子代只需使用 StreamController 添加一个新值,该新值将触发 StreamBuilder .

Declare a StreamController with broadcast, then set a friendly name to the Stream of this StreamController, then everytime you want to rebuild the wraped widget (the child of the StreamBuilder just use the sink property of the StreamController to add a new value that will trigger the StreamBuilder.

您可以使用 StreamBuilder AsyncSnapshot 而不设置类型.

You can use StreamBuilder and AsyncSnapshot without setting the type.

但是,如果在键入 snapshot.data 时使用 StreamBuilder< UserModel> AsyncSnapshot< UserModel> .您将看到所有变量和 UserModel 中的方法.

But if you use StreamBuilder<UserModel> and AsyncSnapshot<UserModel> when you type snapshot.data. you will see all variables and methods from the 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 刷新单个子窗口小部件(例如,具有不同颜色的图标),而无需使用setState (可重建整个页面).

This way you can use a StatelessWidget and refresh just a single sub-widget (an icon with a different color, for example) without using setState (that rebuilds the entire page).

对于性能而言,流是最好的方法.

For performance, streams are the best approach.

我正在使用 BLoC体系结构方法,因此最好在homePageBloc.dart(具有包含所有业务逻辑的常规控制器类)中声明变量,并在homePage.dart中创建StreamBuilder(具有扩展无状态/有状态小部件并负责UI的类.

I'm using BLoC architecture approach, so it's much better to declare the variables in a homePageBloc.dart (that has a normal controller class with all business logic) and create the StreamBuilder in the homePage.dart (that has a class that extends Stateless/Stateful widget and is responsible for the UI).

我的 UserModel.dart ,您可以使用 DocumentSnapshot 代替 Map< String,dynamic> 如果您正在使用Firebase中的Cloud Firestore数据库.

My UserModel.dart, you can use DocumentSnapshot instead of Map<String, dynamic> if you are using Cloud Firestore database from Firebase.

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?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆