简体   繁体   中英

how to make a bloc builder build a widget when its not on screen?

Okay so Its kinda tough for me to explain but I will try my best. I am trying to make an app that follows Bloc state management. Right now, I have a bloc class that controls a bottom navigation bar and the widgets/fragments that it displays, one of those screens has a list view builder, now the state of the list view is also managed by a bloc builder. The bloc builder of the list is responsible for showing the loading state of the list (in the beginning) while the bloc class of this list view is responsible for fetching the data from the server by communicating with the API handler. my issue is that while the data is loading if the user clicks on the bottom navigation bar to show another screen and comes back to the original screen (the one that has the list view) then the data never gets loaded and the list view builder never creates the list items.

What I'm trying to do is when the user clicks on the bottom navigation bar to switch the current widget being viewed, I want the data to be loaded and the list to be built even when the tab/bottom navigation is not the one that has the list

The feed list widget class

// ignore: must_be_immutable
class FeedList extends StatelessWidget {
  final ScrollController _scrollController = new ScrollController();
  final int _incrementValue = 5;
  final FeedListBloc _feedListBloc = FeedListBloc();
  int _start = 0;
  int _end = 10;
  bool fromHome;

  int get start => _start;

  int get end => _end;
  List<Tournament> tournaments = new List<Tournament>();

   FeedList({this.fromHome:false}) {
    print("feed list constructor");
    _feedListBloc.fetchTournaments(start, end, isHomeFeed: fromHome);
    _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        _feedListBloc.fetchTournaments(start, end, isHomeFeed: true);
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return  BlocListener(
      bloc: _feedListBloc,
      listener: (context, FeedListState state) {
        if (state is ErrorState)
          Scaffold.of(context).showSnackBar(SnackBar(
            action: SnackBarAction(
              textColor: Theme.of(context).accentColor,
              label: "dismiss",
              onPressed: () {
                Scaffold.of(context).hideCurrentSnackBar();
              },
            ),
            duration: Duration(seconds: 3),
            content: Text(
              state.errorMSG,
              style:
                  Theme.of(context).textTheme.headline1.copyWith(fontSize: 12),
            ),
            elevation: 0,
            backgroundColor: Color(0xff363636),
            behavior: SnackBarBehavior.floating,
          ));
        else if (state is DataLoadedState) {
          if (state.tournaments.length > 0) {
            _incrementStartEnd();
            tournaments.addAll(state.tournaments);
          }
        }
      },
      child: BlocBuilder(
        bloc: _feedListBloc,
        builder: (context, FeedListState state) {
          if (state is ShimmerState) {
            return ListView.builder(
                itemCount: 3,
                itemBuilder: (context, index) => Shimmer.fromColors(
                    child: FeedListItem(null),
                    baseColor: Color(0xFF1D1D1D),
                    highlightColor: Color(0xff202020)));
          } else {
            return RefreshIndicator(
              onRefresh: () async {
                await refreshFeed();
              },
              child: ListView.builder(
                physics: AlwaysScrollableScrollPhysics(),
                controller: _scrollController,
                itemCount: tournaments.length + 1,
                itemBuilder: (context, index) => _getListItems(context, index),
              ),
            );
          }
        },
      ),
    );
  }

  _getListItems(BuildContext context, int index) {
    if (index >= tournaments.length && tournaments.isNotEmpty)
      return Padding(
        padding: const EdgeInsets.only(bottom: 35),
        child: Center(child: CircularProgressIndicator()),
      );
    else if (index == tournaments.length - 1)
      return Padding(
        padding: const EdgeInsets.only(bottom: 30),
        child: FeedListItem(tournaments[index]),
      );
    else if (tournaments.isNotEmpty) return FeedListItem(tournaments[index]);
  }

  void dispose() {
    _feedListBloc.close();
  }

  void _incrementStartEnd() {
    print("incremented");
    _start = _end;
    _end += _incrementValue;
  }

  refreshFeed() async {
    tournaments.clear();
    _start = 0;
    _end = 10;
    await _feedListBloc.fetchTournaments(start, end, isHomeFeed: fromHome);
  }
}

the feed list bloc class

class FeedListBloc extends Bloc<FeedListEvent, FeedListState> {
  Future<void> fetchTournaments(int start, int end,
      {bool isHomeFeed: false}) async {
    await Future.delayed(Duration(seconds: 3));
    dynamic reply =
        await API.getHomeFeedTournaments(start, end, isHomeFeed: isHomeFeed);
    if (reply == null)
      this.add(ErrorEvent("could not load "));
    else if (reply is String)
      this.add(ErrorEvent(reply));
    else
      this.add(DataLoadedEvent(reply as List<Tournament>));
  }

  @override
  FeedListState get initialState => FeedListState.initState();

  @override
  Stream<FeedListState> mapEventToState(FeedListEvent event) async* {
    if (event is ErrorEvent)
      yield FeedListState.errorState(event.errorMsg);
    else if (event is DataLoadedEvent)
      yield FeedListState.dataLoaded(event.tournaments);
  }
}

please tell me if what I'm trying to do is wrong or if there is a better way to do it. essentially I am doing it like this because I don't want an API call to be made every time the user switches screens but I do want the list to be built even if the user switches screens, the 3 seconds delay in the bloc fetch more method was for me to see understand what was exactly happening

Okay so Its kinda tough for me to explain but I will try my best. I am trying to make an app that follows Bloc state management. Right now, I have a bloc class that controls a bottom navigation bar and the widgets/fragments that it displays, one of those screens has a list view builder, now the state of the list view is also managed by a bloc builder. The bloc builder of the list is responsible for showing the loading state of the list (in the beginning) while the bloc class of this list view is responsible for fetching the data from the server by communicating with the API handler. my issue is that while the data is loading if the user clicks on the bottom navigation bar to show another screen and comes back to the original screen (the one that has the list view) then the data never gets loaded and the list view builder never creates the list items.

What I'm trying to do is when the user clicks on the bottom navigation bar to switch the current widget being viewed, I want the data to be loaded and the list to be built even when the tab/bottom navigation is not the one that has the list

The feed list widget class

// ignore: must_be_immutable
class FeedList extends StatelessWidget {
  final ScrollController _scrollController = new ScrollController();
  final int _incrementValue = 5;
  final FeedListBloc _feedListBloc = FeedListBloc();
  int _start = 0;
  int _end = 10;
  bool fromHome;

  int get start => _start;

  int get end => _end;
  List<Tournament> tournaments = new List<Tournament>();

   FeedList({this.fromHome:false}) {
    print("feed list constructor");
    _feedListBloc.fetchTournaments(start, end, isHomeFeed: fromHome);
    _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        _feedListBloc.fetchTournaments(start, end, isHomeFeed: true);
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return  BlocListener(
      bloc: _feedListBloc,
      listener: (context, FeedListState state) {
        if (state is ErrorState)
          Scaffold.of(context).showSnackBar(SnackBar(
            action: SnackBarAction(
              textColor: Theme.of(context).accentColor,
              label: "dismiss",
              onPressed: () {
                Scaffold.of(context).hideCurrentSnackBar();
              },
            ),
            duration: Duration(seconds: 3),
            content: Text(
              state.errorMSG,
              style:
                  Theme.of(context).textTheme.headline1.copyWith(fontSize: 12),
            ),
            elevation: 0,
            backgroundColor: Color(0xff363636),
            behavior: SnackBarBehavior.floating,
          ));
        else if (state is DataLoadedState) {
          if (state.tournaments.length > 0) {
            _incrementStartEnd();
            tournaments.addAll(state.tournaments);
          }
        }
      },
      child: BlocBuilder(
        bloc: _feedListBloc,
        builder: (context, FeedListState state) {
          if (state is ShimmerState) {
            return ListView.builder(
                itemCount: 3,
                itemBuilder: (context, index) => Shimmer.fromColors(
                    child: FeedListItem(null),
                    baseColor: Color(0xFF1D1D1D),
                    highlightColor: Color(0xff202020)));
          } else {
            return RefreshIndicator(
              onRefresh: () async {
                await refreshFeed();
              },
              child: ListView.builder(
                physics: AlwaysScrollableScrollPhysics(),
                controller: _scrollController,
                itemCount: tournaments.length + 1,
                itemBuilder: (context, index) => _getListItems(context, index),
              ),
            );
          }
        },
      ),
    );
  }

  _getListItems(BuildContext context, int index) {
    if (index >= tournaments.length && tournaments.isNotEmpty)
      return Padding(
        padding: const EdgeInsets.only(bottom: 35),
        child: Center(child: CircularProgressIndicator()),
      );
    else if (index == tournaments.length - 1)
      return Padding(
        padding: const EdgeInsets.only(bottom: 30),
        child: FeedListItem(tournaments[index]),
      );
    else if (tournaments.isNotEmpty) return FeedListItem(tournaments[index]);
  }

  void dispose() {
    _feedListBloc.close();
  }

  void _incrementStartEnd() {
    print("incremented");
    _start = _end;
    _end += _incrementValue;
  }

  refreshFeed() async {
    tournaments.clear();
    _start = 0;
    _end = 10;
    await _feedListBloc.fetchTournaments(start, end, isHomeFeed: fromHome);
  }
}

the feed list bloc class

class FeedListBloc extends Bloc<FeedListEvent, FeedListState> {
  Future<void> fetchTournaments(int start, int end,
      {bool isHomeFeed: false}) async {
    await Future.delayed(Duration(seconds: 3));
    dynamic reply =
        await API.getHomeFeedTournaments(start, end, isHomeFeed: isHomeFeed);
    if (reply == null)
      this.add(ErrorEvent("could not load "));
    else if (reply is String)
      this.add(ErrorEvent(reply));
    else
      this.add(DataLoadedEvent(reply as List<Tournament>));
  }

  @override
  FeedListState get initialState => FeedListState.initState();

  @override
  Stream<FeedListState> mapEventToState(FeedListEvent event) async* {
    if (event is ErrorEvent)
      yield FeedListState.errorState(event.errorMsg);
    else if (event is DataLoadedEvent)
      yield FeedListState.dataLoaded(event.tournaments);
  }
}

please tell me if what I'm trying to do is wrong or if there is a better way to do it. essentially I am doing it like this because I don't want an API call to be made every time the user switches screens but I do want the list to be built even if the user switches screens, the 3 seconds delay in the bloc fetch more method was for me to see understand what was exactly happening

If you're just trying to update the data displayed on your widgets using flutter_bloc , you can either use BlocListener or BlocBuilder . Once the Bloc has been updated from your action, changes on data can be handled inside those widgets.

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