Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter load api data on scroll

I have created an API to get products data from the database like this:

 [
  {
   "id": 1,
   "product_name": "Name1",
   "color": "red"
  },
  {
   "id": 2,
   "product_name": "Name2",
   "color": "green"
  },
  {
   "id": 3,
   "product_name": "Name3",
   "color": "blue"
  }
 ]

So I'm fetching the data from the main.dart when the app lunch

/// Get Stocks data from database
Future<String> getData() async {
  var response = await http.get(url);

  if (response.statusCode == 200) {
// If server returns an OK response, parse the JSON.
    data = utf8.decode(response.bodyBytes);
    return data;

  } else {
// If that response was not OK, throw an error.
    throw Exception('Failed to load Stocks');
  }
}

How can I make the items load one by one when the user scroll to view more products?

like image 460
Wail Hayaly Avatar asked Aug 01 '26 17:08

Wail Hayaly


1 Answers

You can use package https://pub.dev/packages/incrementally_loading_listview
and set _numItemsPage = 1

Condtion 1: api return all data
After execute your api, you get full list of products
To display product to listview, the Future _loadMoreItems() add data in your case one by one to ListView items

Condition 2: api return one record each time when called
Put your api call in Future _loadMoreItems()

In demo you can see, it's load one by one, if your Product fit one screen, you can effect you want

full example code

    import 'dart:async';

import 'package:flutter/material.dart';
import 'package:incrementally_loading_listview/incrementally_loading_listview.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<Item> items;
  bool _loadingMore;
  bool _hasMoreItems;
  int _maxItems = 30;
  int _numItemsPage = 1;
  Future _initialLoad;

  Future _loadMoreItems() async {
    final totalItems = items.length;
    await Future.delayed(Duration(seconds: 3), () {
      for (var i = 0; i < _numItemsPage; i++) {
        items.add(Item('Item ${totalItems + i + 1}'));
      }
    });

    _hasMoreItems = items.length < _maxItems;
  }

  @override
  void initState() {
    super.initState();
    _initialLoad = Future.delayed(Duration(seconds: 3), () {
      items = List<Item>();
      for (var i = 0; i < _numItemsPage; i++) {
        items.add(Item('Item ${i + 1}'));
      }
      _hasMoreItems = true;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.yellow,
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: FutureBuilder(
          future: _initialLoad,
          builder: (context, snapshot) {
            switch (snapshot.connectionState) {
              case ConnectionState.waiting:
                return Center(child: CircularProgressIndicator());
              case ConnectionState.done:
                return IncrementallyLoadingListView(
                  hasMore: () => _hasMoreItems,
                  itemCount: () => items.length,
                  loadMore: () async {
                    // can shorten to "loadMore: _loadMoreItems" but this syntax is used to demonstrate that
                    // functions with parameters can also be invoked if needed
                    await _loadMoreItems();
                  },
                  onLoadMore: () {
                    setState(() {
                      _loadingMore = true;
                    });
                  },
                  onLoadMoreFinished: () {
                    setState(() {
                      _loadingMore = false;
                    });
                  },
                  loadMoreOffsetFromBottom: 0,
                  itemBuilder: (context, index) {
                    final item = items[index];
                    if ((_loadingMore ?? false) && index == items.length - 1) {
                      return Column(
                        children: <Widget>[
                          ItemCard(item: item),
                          Card(
                            child: Padding(
                              padding: const EdgeInsets.all(16.0),
                              child: Column(
                                children: <Widget>[
                                  Row(
                                    crossAxisAlignment:
                                    CrossAxisAlignment.start,
                                    children: <Widget>[
                                      Container(
                                        width: 60.0,
                                        height: 60.0,
                                        color: Colors.grey,
                                      ),
                                      Padding(
                                        padding: const EdgeInsets.fromLTRB(
                                            8.0, 0.0, 0.0, 0.0),
                                        child: Container(
                                          color: Colors.grey,
                                          child: Text(
                                            item.name,
                                            style: TextStyle(
                                                color: Colors.transparent),
                                          ),
                                        ),
                                      )
                                    ],
                                  ),
                                  Padding(
                                    padding: const EdgeInsets.fromLTRB(
                                        0.0, 8.0, 0.0, 0.0),
                                    child: Container(
                                      color: Colors.grey,
                                      child: Text(
                                        item.message,
                                        style: TextStyle(
                                            color: Colors.transparent),
                                      ),
                                    ),
                                  )
                                ],
                              ),
                            ),
                          ),
                        ],
                      );
                    }
                    return ItemCard(item: item);
                  },
                );
              default:
                return Text('Something went wrong');
            }
          }),
    );
  }
}

class ItemCard extends StatelessWidget {
  const ItemCard({
    Key key,
    @required this.item,
  }) : super(key: key);

  final Item item;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Card(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            children: <Widget>[
              Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  Image.network(item.avatarUrl),
                  Expanded(
                    child: Padding(
                      padding: const EdgeInsets.fromLTRB(8.0, 0.0, 0.0, 0.0),
                      child: Text(item.name),
                    ),
                  )
                ],
              ),
              Padding(
                padding: const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 0.0),
                child: Text(item.message),
              )
            ],
          ),
        ),
      ),
      onTap: () => Navigator.of(context).push(
        MaterialPageRoute(builder: (context) => ItemDetailsPage(item)),
      ),
    );
  }
}

class ItemDetailsPage extends StatelessWidget {
  final Item item;
  const ItemDetailsPage(this.item);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Colors.yellow,
        appBar: AppBar(
          title: Text(item.name),
        ),
        body: Container(
          child: Text(item.message),
        ));
  }
}

class Item {
  final String name;
  final String avatarUrl = 'http://via.placeholder.com/60x60';
  final String message =
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';

  Item(this.name);
}

enter image description here

like image 167
chunhunghan Avatar answered Aug 03 '26 10:08

chunhunghan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!