Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

onEndOfPage called more than once #1

Closed
GregorySech opened this issue Apr 1, 2019 · 6 comments
Closed

onEndOfPage called more than once #1

GregorySech opened this issue Apr 1, 2019 · 6 comments

Comments

@GregorySech
Copy link

Hi,
I've used this plugin inside of a StreamBuilder without issues however when I try to use it inside a custom StatefulWidget the onEndOfPage callback gets called more than once.

Here the example app,

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:lazy_load_scrollview/lazy_load_scrollview.dart';

main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(
        title: Text('LazyLoading'),
      ),
      body: Home(),
    ),
  ));
}

class Home extends StatefulWidget {
  final int pageSize;

  Home({this.pageSize = 10});

  @override
  State<StatefulWidget> createState() => HomeState();
}

class HomeState extends State<Home> {
  List<Map> documents;
  bool loading;
  int currentPage;
  Client client;

  @override
  void initState() {
    super.initState();
    documents = [];
    loading = false;
    currentPage = 0;
    client = Client();
    () async {
      await _loadMore();
    }();
  }

// 'http://monster6.disco.unimib.it/API/documents/search/?s=informatica&paging=${widget.pageSize}&offset=${currentPage > 0 ? currentPage * widget.pageSize : ''}'
  Future<void> _loadMore() async {
    print('loading more!');
    setState(() {
      loading = true;
    });
    final response = await client.get(
      Uri.http(
        'monster6.disco.unimib.it',
        '/API/documents/search',
        {
          's': 'informatica',
          'paging': '${widget.pageSize}',
          'offset':
              currentPage > 0 ? '${currentPage * widget.pageSize + 1}' : null
        },
      ),
    );
    print(jsonDecode(response.body));
    final results = List<Map>.from(
        jsonDecode(response.body)['documentsAleph']['documents']);
    if (mounted) {
      setState(() {
        loading = false;
        documents.addAll(results);
        currentPage++;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Positioned.fill(
          child: LazyLoadScrollView(
            child: ListView.builder(
              itemCount: documents.length,
              itemBuilder: (context, index) {
                return AspectRatio(
                  aspectRatio: 2,
                  child: Card(
                    child: Text(documents[index]['title'] ?? 'null'),
                  ),
                );
              },
            ),
            onEndOfPage: () {
              _loadMore();
            },
            scrollOffset: 600,
          ),
        ),
        Positioned(
          top: 0,
          left: 0,
          right: 0,
          child: loading ? LinearProgressIndicator() : Container(),
        ),
      ],
    );
  }
}
@QuirijnGB
Copy link
Owner

Hi! Thanks for reaching out. I've had a look, and it seems that you calling

    setState(() {
      loading = true;
    });

is causing to update the widget. If you remove that it seems to work.
Now that probably doesn't help because you want to display some loading indicator.

I'll need a way to get notified that new data has finished load so I'm thinking to add an extra parameter to the LazyLoadScrollView, maybe isLoading to check whether the list has been populated with new data.
Thoughts?

@GregorySech
Copy link
Author

Sorry was a little busy.
In the end I just provided an empty callback if I was loading.
It does feel like a workaround so maybe having a parameter that "ignores" the callback on build might be a good idea.
If the callback returned a future maybe you could set the loadMoreStatus as stable after it completes instead of relying on didUpdateWidget

@GregorySech
Copy link
Author

Another idea might be using the LazyLoadScrollView's child's (Value)Key to understand if that has been updated and the LazyLoadScrollView is stable again.
Something like this:


class LazyLoadScrollViewState extends State<LazyLoadScrollView> {
  LoadingStatus loadMoreStatus = LoadingStatus.STABLE;

  @override
  void didUpdateWidget(LazyLoadScrollView oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.child.key == null || oldWidget.child.key != widget.child.key) {
      loadMoreStatus = LoadingStatus.STABLE;
    }
  }
...

and on the "client" side it would be kind of like this:

LazyLoadScrollView(
  child: ListView.builder(
    key: ValueKey(documents.length),
    itemCount: ...

@QuirijnGB
Copy link
Owner

Heya! Thanks for the feedback. I feel like the key idea would be a bit brittle, and harder to debug.
Using an explicit parameter, is probably clearer to the user and easier to document too.
I'll see if i can make a PR for it tonight

@QuirijnGB
Copy link
Owner

@GregorySech I added a new parameter to LazyLoadScrollView named isLoading. If you set a boolean to that it should make it work as you expect. Update your pubspec.yaml to use version 0.0.3

@QuirijnGB
Copy link
Owner

Thanks for raising this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants