Created with <3 with dartpad.dev.
Last active
August 10, 2022 14:15
-
-
Save victor-carv/7badfad5cdcf3d6a4708aa2b21022d78 to your computer and use it in GitHub Desktop.
flutter huge lists
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'package:flutter/material.dart'; | |
| import 'package:flutter/foundation.dart'; | |
| import 'dart:convert'; | |
| import 'package:http/http.dart' as http; | |
| void main() { | |
| runApp(const MyApp()); | |
| } | |
| class MyApp extends StatelessWidget { | |
| const MyApp({Key? key}) : super(key: key); | |
| @override | |
| Widget build(BuildContext context) { | |
| return const MaterialApp( | |
| debugShowCheckedModeBanner: false, | |
| title: 'Flutter list sample', | |
| home: HomePage()); | |
| } | |
| } | |
| class HomePage extends StatefulWidget { | |
| const HomePage({Key? key}) : super(key: key); | |
| @override | |
| State<HomePage> createState() => _HomePageState(); | |
| } | |
| class _HomePageState extends State<HomePage> { | |
| // We will fetch data from this Rest api | |
| final _baseUrl = 'https://dry-ridge-81455.herokuapp.com/properties'; | |
| int _page = 1; | |
| final int _limit = 25; | |
| bool _hasNextPage = true; | |
| bool _isFirstLoadRunning = false; | |
| bool _isLoadMoreRunning = false; | |
| // This holds the posts fetched from the server | |
| List _properties = []; | |
| dynamic responseData; | |
| // This function will be called when the app launches (see the initState function) | |
| void _firstLoad() async { | |
| print('Start'); | |
| setState(() { | |
| _isFirstLoadRunning = true; | |
| }); | |
| try { | |
| final res = | |
| await http.get(Uri.parse("$_baseUrl?page=$_page&offset=$_limit")); | |
| setState(() { | |
| responseData = json.decode(res.body); | |
| _properties = responseData['data']; | |
| }); | |
| } catch (err) { | |
| if (kDebugMode) { | |
| print('Something went wrong'); | |
| } | |
| } | |
| setState(() { | |
| _isFirstLoadRunning = false; | |
| }); | |
| } | |
| // This function will be triggered whenver the user scroll | |
| // to near the bottom of the list view | |
| void _loadMore() async { | |
| if (_hasNextPage == true && | |
| _isFirstLoadRunning == false && | |
| _isLoadMoreRunning == false && | |
| _controller.position.extentAfter < 300) { | |
| print('Load more'); | |
| setState(() { | |
| _isLoadMoreRunning = true; // Display a progress indicator at the bottom | |
| }); | |
| _page += 1; // Increase _page by 1 | |
| try { | |
| final res = | |
| await http.get(Uri.parse("$_baseUrl?page=$_page&offset=$_limit")); | |
| dynamic newResponseData = json.decode(res.body); | |
| final List fetchedProperties = newResponseData['data']; | |
| if (fetchedProperties.isNotEmpty) { | |
| setState(() { | |
| responseData = newResponseData; | |
| _properties.addAll(fetchedProperties); | |
| }); | |
| } else { | |
| // This means there is no more data | |
| // and therefore, we will not send another GET request | |
| setState(() { | |
| _hasNextPage = false; | |
| }); | |
| } | |
| } catch (err) { | |
| if (kDebugMode) { | |
| print('Something went wrong!'); | |
| } | |
| } | |
| setState(() { | |
| _isLoadMoreRunning = false; | |
| }); | |
| } else { | |
| print('NOT Load more'); | |
| } | |
| } | |
| // The controller for the ListView | |
| late ScrollController _controller; | |
| @override | |
| void initState() { | |
| super.initState(); | |
| _firstLoad(); | |
| _controller = ScrollController()..addListener(_loadMore); | |
| } | |
| @override | |
| void dispose() { | |
| _controller.removeListener(_loadMore); | |
| super.dispose(); | |
| } | |
| @override | |
| Widget build(BuildContext context) { | |
| return Scaffold( | |
| appBar: AppBar( | |
| title: Text("Page: $_page | Items per page: 25 | Total items: 1,000,000"), | |
| ), | |
| body: _isFirstLoadRunning | |
| ? const Center( | |
| child: CircularProgressIndicator(), | |
| ) | |
| : Column( | |
| children: [ | |
| Expanded( | |
| child: ListView.builder( | |
| controller: _controller, | |
| itemCount: responseData != null | |
| ? responseData['total'] | |
| : _properties.length, | |
| itemBuilder: (_, index) => Card( | |
| child: index < _properties.length | |
| ? ListTile( | |
| title: Text(_properties[index]['zip']), | |
| subtitle: | |
| Text(_properties[index]['primaryAddress']), | |
| ) | |
| : const ListTile( | |
| title: Text('Loading'), | |
| subtitle: | |
| Text('...'), | |
| ) | |
| ), | |
| ), | |
| ), | |
| // when the _loadMore function is running | |
| if (_isLoadMoreRunning == true) | |
| const Padding( | |
| padding: EdgeInsets.only(top: 10, bottom: 40), | |
| child: Center( | |
| child: CircularProgressIndicator(), | |
| ), | |
| ), | |
| // When nothing else to load | |
| if (_hasNextPage == false) | |
| Container( | |
| padding: const EdgeInsets.only(top: 30, bottom: 40), | |
| color: Colors.amber, | |
| child: const Center( | |
| child: Text('You have fetched all of the content'), | |
| ), | |
| ), | |
| ], | |
| ), | |
| ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment