简体   繁体   中英

Bloc Pattern to hide and unhide Widgets

I am trying to fetch some data from the internet and show it int a list.

Following is my bloc code

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

Following is my main code to hide unide Widgets

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

The buildList method is called in the body of Scaffold

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

Suppose the user has internet when app is opened then i am able to see circularprogressindicator and then the list of data is visible but suppose at the start when app is opened and the user does not have internet then i am showing the No Network Available Text and a button to retry, now if the user has connected to the internet and then click on button to retry i am directly seeing the list of data after few seconds instead of the circularprogressindicator .I am not able to understand why the NoInternetviews are not hiding and progressindicator is showing when retry button is clicked before showing list of data.

My stream is not getting updated on retry button called. Are there any caveats for StreamBuilder within StreamBuilder ?

I tried changing the StreamBuilder order as mentioned by @ivenxu in the answer but it still does not work. Following are the links of attached code https://drive.google.com/file/d/15Z8jXw1OpwTB1CxDS8sHz8jKyHhLwJp7/view?usp=sharing https://drive.google.com/open?id=1gIXV20S1o5jYRnno_NADabuIj4w163fF

in view layer you can use Visibility() widget and pass visible parameter true or false when load data from Internet. let's think about how to change the visible variable from bloc. The parent of Visibility() widget the StreamBuilder() to stream on changes data. for your case you need a PublishSubject inside your bloc to stream and add new data, and Observable to stream on this data on your widget.

let's show a snippet code to help you how you can implement it

The bloc contains PublishSubject and Observable to stream and add data

  //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);

Here you can change your state of your progress

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

Your progress widget is

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

It seems the cause is that CircularProgressIndicator is inside the students stream's update cycle. And the student steam only get next snapshot when the data returned from internet call in the case of retry.

Have a try on changing the order of stream builders, try putting the student streambuilder inside of the progress stream builder.

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

    );
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM