【问题标题】:Bloc Pattern to hide and unhide Widgets用于隐藏和取消隐藏小部件的 Bloc 模式
【发布时间】:2019-06-25 19:47:18
【问题描述】:

我正在尝试从互联网上获取一些数据并将其显示在一个列表中。

以下是我的bloc 代码

class StudentsBloc {
  final _repository = Repository();
  final _students = BehaviorSubject<StudentModel>();

  final BehaviorSubject<bool> _showProgress = BehaviorSubject<bool>();
  final BehaviorSubject<bool> _showNoInternetViews = BehaviorSubject<bool>();

  Observable<StudentModel> get students => _students.stream;
  Observable<bool> get showProgress => _showProgress.stream;
  Observable<bool> get showNoInternetViews => _showNoInternetViews.stream;

  //FetchStudent from my Api
  fetchStudents(String disciplineId, String schoolId, String year_id,
      String lastIndex) async {
    final student = await _repository.fetchStudents(
        disciplineId, schoolId, year_id, lastIndex);
    _students.sink.add(student);
  }

  //Check to see if user has internet or not
  isNetworkAvailable(String disciplineId, String schoolId, String year_id,
      String lastIndex) async {
    checkInternetConnection().then((isAvailable) {
      if (isAvailable) {
        fetchStudents(disciplineId, schoolId, year_id, lastIndex);
      } else {
        _students.sink.addError(NO_NETWORK_AVAILABLE);
      }
    });
  }

  Function(bool) get changeVisibilityOfProgress => _showProgress.sink.add;
  Function(bool) get changeVisibilityOfNoInternetViews =>
      _showNoInternetViews.sink.add;

  dispose() {
    _students.close();
    _showProgress.close();
    _showNoInternetViews.close();
  }
}

以下是我隐藏统一小部件的主要代码

Widget buildList(StudentsBloc bloc) {
    return StreamBuilder(
      stream: bloc.students,
      builder: (context, AsyncSnapshot<StudentModel> snapshot) {
        if (snapshot.hasError) {
          bloc.changeVisibilityOfProgress(false);
          bloc.changeVisibilityOfNoInternetViews(true);

          return StreamBuilder(
            stream: bloc.showNoInternetViews,
            builder: (context, snapshot) {
              bool showNoInternetView = snapshot.hasData ?? false;

              return Visibility(
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      Text("No Network Available"),
                      RaisedButton(
                        onPressed: () {
                          fetchStudents();
                        },
                        child: Text("Retry"),
                      )
                    ],
                  ),
                ),
                visible: showNoInternetView ? true : false,
              );
            },
          );
        }

        if (snapshot.hasData) {
          bloc.changeVisibilityOfProgress(false);
          bloc.changeVisibilityOfNoInternetViews(false);

          return Refresh(
            year_id: "2",
            schoolId: "1",
            lastIndex: "0",
            disciplineId: "1",
            child: ListView.builder(
              itemBuilder: (context, int index) {
                return buildTile(
                    snapshot.data.toBuilder().data.studentData[index]);
              },
              itemCount: snapshot.data.toBuilder().data.studentData.length,
            ),
          );
        }

        if (!snapshot.hasData) {
          return StreamBuilder(
            builder: (context, snapshot) {
              bool showProgressIndicator = snapshot.data ?? false;

              return Visibility(
                child: Center(
                  child: CircularProgressIndicator(),
                ),
                visible: showProgressIndicator ? true : false,
              );
            },
            stream: bloc.showProgress,
          );
        }
      },
    );
  }

Scaffoldbody中调用了buildList方法

void fetchStudents() {
    bloc?.changeVisibilityOfNoInternetViews(false);
    bloc?.changeVisibilityOfProgress(true);
    bloc?.isNetworkAvailable("1", "1", "2", "0");
  }

假设打开应用程序时用户有互联网,然后我可以看到circularprogressindicator,然后数据列表可见 但假设一开始当应用程序打开并且用户没有互联网然后我显示无网络可用文本和一个重试按钮, 现在,如果用户已连接到 Internet,然后单击按钮重试,我将在几秒钟后直接看到数据列表,而不是 circularprogressindicator。我无法理解为什么 NoInternetviews 没有隐藏和 @在显示数据列表之前单击重试按钮时会显示 987654334@。

我的流没有在调用重试按钮时得到更新。 StreamBuilder 中的 StreamBuilder 是否有任何警告?

我尝试更改@ivenxu 在答案中提到的StreamBuilder 订单,但它仍然不起作用。 以下是附加代码的链接 https://drive.google.com/file/d/15Z8jXw1OpwTB1CxDS8sHz8jKyHhLwJp7/view?usp=sharing https://drive.google.com/open?id=1gIXV20S1o5jYRnno_NADabuIj4w163fF

【问题讨论】:

  • 尝试根据connectionState 而不是hasData 属性来执行您的逻辑。
  • @JordanDavies 实际上我已经尝试过这种方法,但对于 StreamBuilder 来说是不可行的,因为无论您是否连接到 Internet,您总是先等待然后再激活。我已经发布了一个关于此的问题,请查看stackoverflow.com/questions/54268534/…

标签: dart flutter bloc


【解决方案1】:

在视图层中,您可以使用 Visibility() 小部件并在从 Internet 加载数据时传递可见参数 true 或 false。 让我们考虑如何从 bloc 更改可见变量。 Visibility() 小部件的父级 StreamBuilder() 用于流式传输更改数据。 对于您的情况,您需要在您的 bloc 中使用 PublishSubject 来流式传输和添加新数据,并且需要 Observable 在您的小部件上流式传输这些数据。

让我们展示一个 sn-p 代码来帮助您如何实现它

bloc 包含 PublishSubjectObservable 用于流式传输和添加数据

  //this Subject allows sending data, error and done events to the listener
  final PublishSubject<bool> _progressStateSubject = new PublishSubject();

  //the listener are streaming on changes
  Observable<bool> get progressStateStream => _progressStateSubject.stream;

  //to change your progress state
  void changeProgressState({bool state}) => _progressStateSubject.sink.add(state);

在这里你可以改变你的进度状态

void callWebService() async {
    //when you call your func to fetch your data change your state to true
    changeProgressState(state: true);
    .
    .
    .
    if(response != null){
      //when your call is done change the state to false
      changeProgressState(state: false);
    }
  }

您的进度小部件是

// Loading Widget
  Widget _buildLoadingWidget(Bloc bloc) {
    return StreamBuilder<bool>(
        stream: bloc.progressStateStream,//streaming on changes
        builder: (context, snapshot) {
          return Visibility(
            visible: snapshot.data ?? false,//calling when data changes
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text("Loading data from API...",
                      textDirection: TextDirection.ltr),
                  CircularProgressIndicator()
                ],
              ),
            ),
          );
        });
  }

【讨论】:

  • 按照你的例子我得到一个错误I/flutter ( 679): The getter 'progressStateStream' was called on null.
  • 你必须在第一个 build 方法或 initState 中初始化你的 bloc
【解决方案2】:

似乎原因是 CircularProgressIndicator 在学生流的更新周期内。在重试的情况下,学生steam只有在互联网调用返回的数据时才会得到下一个快照。

尝试更改流构建器的顺序,尝试将学生流构建器放在进度流构建器中。

Widget buildList(StudentsBloc bloc) {
    return StreamBuilder(
      stream: bloc.showProgress,
      builder: (context, snapshot) {
        bool showProgressIndicator = snapshot.data ?? false;
        if (!showProgressIndicator) {
             StreamBuilder(
            stream: bloc.students,
            builder: (context, AsyncSnapshot<StudentModel> snapshot) {
               ....
               //your original code without progress StreamBuilder
            }
        }
        return Visibility(
        child: Center(
            child: CircularProgressIndicator(),
        ),
        visible: showProgressIndicator ? true : false,
        );
    },

    );
  }

【讨论】:

  • 不起作用。这里是代码drive.google.com/open?id=1gIXV20S1o5jYRnno_NADabuIj4w163fF,点击重试按钮时进度不可见
  • @Nudge 还是一样的行为?
  • 在"bool showProgressIndicator = snapshot.data ?? false;"这一行尝试断点,看是否在bloc?.changeVisibilityOfProgress(true);之后触发
  • 触发了