scrollcontroller не работает с futurebuilder

У меня есть вызов API, который загружает данные по смещению, моя цель - загрузить 10 на 10 при прокрутке пользователя вниз, проблема в том, что я не могу прокрутить вниз и показать больше данных: вот мои фрагменты:

class Body extends StatefulWidget {
  @override
  _BodyState createState() => _BodyState();
}

class _BodyState extends State<Body> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          HomeHeader(),
          ProductsGridViewInfiniteScroll(),
        ],
      ),
    );
  }
}

здесь контроллер прокрутки, кажется, не работает:

class ProductsGridViewInfiniteScroll extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return ProductsGridViewInfiniteScrollState();
  }
}

class ProductsGridViewInfiniteScrollState
    extends State<ProductsGridViewInfiniteScroll> {
  Future<ProductList> products;
  int offset;
  ScrollController _controller;

  _scrollListener() {
    if (_controller.offset >= _controller.position.maxScrollExtent &&
        !_controller.position.outOfRange) {
      setState(() {
        offset += 10;
        products = loadProductsByIdService(1, offset, 10);
      });
    }
  }

  @override
  void initState() {
    offset = 0;
    products = loadProductsByIdService(1, offset, 10);
    _controller = ScrollController();
    _controller.addListener(_scrollListener);
    super.initState();
  }

  Widget build(BuildContext context) {
    return FutureBuilder<ProductList>(
        future: products,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return GridView.builder(
                controller: _controller,
                itemCount: snapshot.data.products.length,
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 2,
                    crossAxisSpacing: 4.0,
                    mainAxisSpacing: 10.0),
                shrinkWrap: true,
                physics: ScrollPhysics(),
                itemBuilder: (BuildContext ctx, index) {
                  return Container(
                    alignment: Alignment.center,
                    child: ProductCard(product: snapshot.data.products[index]),
                  );
                });
          } else {
            return SizedBox();
          }
        });
  }
}
Future<ProductList> loadProductsByIdService(serviceid, offset, limit) async {
  var datamap = {'service_id': serviceid, 'offset': offset, 'limit': limit};
  var data = json.encode(datamap);
  ProductList products;

  final response = await http.post(Uri.parse(PRODUCTS),
      headers: {
        "Accept": "application/json",
        "Content-Type": "application/x-www-form-urlencoded"
      },
      body: data,
      encoding: Encoding.getByName("utf-8"));

  if (response.body.isNotEmpty) {
    if (response.statusCode == 200) {
      products = ProductList.fromJson(json.decode(response.body));
    }
  } else {
    throw Exception('echec de chargement des produits');
  }

  return products;
}

Я хочу перестроить функцию сборки и обновлять переменную продуктов каждый раз, когда прокрутка достигает нижней части экрана, любая помощь, пожалуйста;


person ELHIT Fatima    schedule 21.05.2021    source источник
comment
Вы перезапустили приложение? Когда мы определяем контроллеры, приложение требует перезапуска. Пожалуйста, проверьте, выполняется ли setState.   -  person Eduard Hasanaj    schedule 21.05.2021
comment
Я перезапустил приложение, как вы сказали, но при прокрутке вниз загружается только 10 продуктов, ничего не происходит, если вы не возражаете, как я могу проверить, выполняется ли setState или нет?   -  person ELHIT Fatima    schedule 21.05.2021
comment
сделать печать (что-то), которая должна что-то напечатать в терминале.   -  person Eduard Hasanaj    schedule 21.05.2021
comment
какой-либо продукт вообще показан?   -  person Eduard Hasanaj    schedule 21.05.2021
comment
да только первые 10 товаров   -  person ELHIT Fatima    schedule 21.05.2021
comment
setState тоже не работает   -  person ELHIT Fatima    schedule 21.05.2021
comment
Вызывается ли печать, если вы поместите ее в начало _scrollListener. Может быть, в условии if есть проблемы.   -  person Eduard Hasanaj    schedule 21.05.2021
comment
Я заменил указанное выше условие этим условием, но ничего не происходит if(_controller.position.maxScrollExtent == _controller.position.pixels)   -  person ELHIT Fatima    schedule 22.05.2021
comment
Если вы разместите отпечаток вне if, он будет напечатан?   -  person Eduard Hasanaj    schedule 22.05.2021


Ответы (2)


Похоже, что ClampingScrollPhysics является поведением физики прокрутки по умолчанию в Android и BouncingScrollPhysics. Скорее всего, вы работаете на IOS, поэтому по умолчанию вы используете BouncingScrollPhysics. В этом случае измените условие if на:

_scrollListener() {
    if (_controller.offset >= _controller.position.maxScrollExtent &&
        _controller.position.outOfRange) {
      _controller.jumpTo(0);
      setState(() {
        offset += 10;
        products = getProducts(offset, limit);
      });
    }
  }

Когда используется подпрыгивающая физика, прокрутка проходит максимальное расширение прокрутки, и outOfRange становится истинным. Вам нужно сбросить смещение контроллера обратно на 0, поскольку он будет продолжать вызывать слушателя несколько раз, который пропускает страницы, увеличивая смещение.

person Eduard Hasanaj    schedule 22.05.2021
comment
на самом деле я использую хром для отладки, спрашиваю, может ли он работать на реальном устройстве? ваше решение тоже не работает - person ELHIT Fatima; 25.05.2021
comment
@ELHITFatima Мне очень жаль, но я протестировал его на своем устройстве Android. Оно работает! - person Eduard Hasanaj; 25.05.2021
comment
да, я работаю на компьютере, который не поддерживает виртуализацию, поэтому я не могу запустить приложение на эмуляторе, спасибо, я собираюсь протестировать на другом компьютере - person ELHIT Fatima; 25.05.2021
comment
@ELHITFatima, вы можете попробовать установить физику для BouncingScrollPhysics, потому что мой ответ адаптирован для такого рода физики. - person Eduard Hasanaj; 25.05.2021
comment
@eduardHasanag на самом деле работает `физика: BouncingScrollPhysics (parent: AlwaysScrollableScrollPhysics ()),` с параметром shrinkwrap равным true, я также исправил проблему без контроллера прокрутки с помощью условия индекса в itembuilder if (index == snapshot.data.products.length - 1 ){ products = loadProductsByIdService(serviceId, offset += 10, limit); } - person ELHIT Fatima; 27.05.2021

Если у кого-то есть такая же проблема, это окончательное решение, которое сработало для меня без использования FutureBuilder:

class Home extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new HomeState();
}

class HomeState extends State<Home> {
  static int offset = 0;
  ScrollController _sc = new ScrollController();
  bool isLoading = false;
  ProductList products = new ProductList(products: []);

  @override
  void initState() {
    this._getMoreData(1, offset, 10);
    super.initState();
    _sc.addListener(() {
      if (_sc.position.pixels == _sc.position.maxScrollExtent) {
        _getMoreData(1, offset += 10, 10);
      }
    });
  }

  @override
  void dispose() {
    _sc.dispose();
    super.dispose();
  }

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

  Widget __buildGrid() {
    return GridView.builder(
        controller: _sc,
        itemCount: products.products.length + 1,
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
        ),
        shrinkWrap: true,
        physics: BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
        itemBuilder: (BuildContext ctx, index) {
          if (index == products.products.length) {
            return Padding(
              padding: const EdgeInsets.all(8.0),
              child: new Center(
                child: new Opacity(
                  opacity: isLoading ? 1.0 : 00,
                  child: new CircularProgressIndicator(),
                ),
              ),
            );
          } else {
            return Container(
              alignment: Alignment.topLeft,
              // child: Flexible(
              child: ProductCard(product: products.products[index]),
              // ),
            );
          }
        });

   
  }

  void _getMoreData(serviceid, offset, limit) async {
    if (!isLoading) {
      setState(() {
        isLoading = true;
      });
      var datamap = {'service_id': serviceid, 'offset': offset, 'limit': limit};
      var data = json.encode(datamap);
      ProductList ps;

      final response = await http.post(Uri.parse(PRODUCTS),
          headers: {
            "Accept": "application/json",
            "Content-Type": "application/x-www-form-urlencoded"
          },
          body: data,
          encoding: Encoding.getByName("utf-8"));

      if (response.statusCode == 200) {
        ps = ProductList.fromJson(json.decode(response.body));
        setState(() {
          isLoading = false;
          products.products.addAll(ps.products);
          // offset += 10;
        });
      }
    }
  }
}
person ELHIT Fatima    schedule 11.06.2021