Home > Back-end >  How to notify a stream to update in Flutter
How to notify a stream to update in Flutter

Time:01-08

I am trying to implement pagination in my Flutter app but this is the first time I have done it. My idea was to create a Stream of data that updates each time the user reaches the bottom of a list. I have failed to get this working. My current code has the logic for getting the new data and adding it to the existing data, but right now it's not even returning the first range of data so the snapshots are empty. As for the pagination functionality, I tried to use a ChangeNotifier to notify the Stream to update, but I don't know if that is working. Please take a look at the code below and let me know what should be changed.

The DataProvider class:

class DataProvider extends ChangeNotifier{
  DataProvider() : super();

  static var changeController = ChangeNotifier();

  static void reload() {
    changeController.notifyListeners();
  }

  static int lastRange = 0;

  static List data = [];

  static Stream<List> paginatedUsersAndPosts() async* {
    List<UserSearchResult> usersList = data.first;
    List<Post> postsList = data.last;

    print('Notifier');

    changeController.addListener(() async {
      print('Change notified, getting data');

      List<int> range() {
        if (lastRange == 0) {
          lastRange = 10;
          return [0, 10];
        } else {
          // Example 0, 10 => 11, 20
          int newMin = lastRange   1;
          int newMax = lastRange   10;
          lastRange = newMax;
          return [newMin, newMax];
        }
      }

      List<Map<String, dynamic>> postsDocs = await Supabase.db
          .from('posts')
          .select()
          .order('date', ascending: false)
          .range(range().first, range().last);
      List<Post> newPostsList =
      postsDocs.map((postDoc) => Post.fromJson(postDoc)).toList();

      newPostsList.forEach((post) async {
        postsList.add(post);

        if (usersList.where((u) => u.uid == post.uid).isNotEmpty) {
          Map<String, dynamic> userDoc =
          await Supabase.db.from('profiles').select().single();

          ProfileInfoObject profileInfo = ProfileInfoObject.fromJson(userDoc);
          print('New profile: $profileInfo');
          Profile profile = Profile(profileInfo, []);
          profile.posts.add(post);
          List blockedUsers = userDoc['blockedUsers'] as List;
          UserSearchResult user = (UserSearchResult(
              profile, userDoc['uid'].toString(), blockedUsers));

          usersList.add(user);
        }
      });
    });

    yield [usersList, postsList];
  }
}

The main widget that uses the stream:

class FloatingTabBarView extends StatefulWidget {
  const FloatingTabBarView({Key? key}) : super(key: key);

  @override
  State<FloatingTabBarView> createState() => _FloatingTabBarViewState();
}

class _FloatingTabBarViewState extends State<FloatingTabBarView> {

  @override
  void initState() {
    PermissionsService.checkPermissions(context);

    DataProvider.reload();
    super.initState();
  }

    Stream<List> stream = DataProvider.paginatedUsersAndPosts();

    return StreamBuilder<List>(
        stream: stream,
        builder: (context, AsyncSnapshot<List<dynamic>> snapshot) {
          ...
        });
  }

  @override
  Widget build(BuildContext context) {
    return floatingTabBarPageView();
  }
}

CodePudding user response:

Please take a look at pull_to_refresh package. This allows to implement pull to refresh and incrementally load data. Use the RefreshController to update when the data is refreshed or loaded.

  1. Instantiate RefreshController
  2. Wrap the ListView with SmartRefresher
  3. Implement the onLoading callback to fetch data incrementally
  4. Update the RefreshController on completion or error.

The package has a good usage example, do check it out.

CodePudding user response:

The issue was the way I was creating the stream. Since I made the Stream a function, there was no way for me to call that function to reload. Instead, I moved the code to a static void that I can call from any page, created a StreamController, and used controller.add(data) at the end of the void which allows me to update the stream with the new data.

  • Related