|
|
@@ -0,0 +1,105 @@ |
|
|
import 'dart:async'; |
|
|
|
|
|
import 'package:flutter/material.dart'; |
|
|
|
|
|
void main() => runApp(new MyApp()); |
|
|
|
|
|
class MyApp extends StatelessWidget { |
|
|
@override |
|
|
Widget build(BuildContext context) { |
|
|
return new MaterialApp( |
|
|
theme: new ThemeData(primarySwatch: Colors.blue), |
|
|
home: new MyHomePage(), |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
class MyHomePage extends StatefulWidget { |
|
|
@override |
|
|
_MyHomePageState createState() => _MyHomePageState(); |
|
|
} |
|
|
|
|
|
class _MyHomePageState extends State<MyHomePage> { |
|
|
List<int> items = List.generate(10, (i) => i); |
|
|
ScrollController _scrollController = new ScrollController(); |
|
|
bool isPerformingRequest = false; |
|
|
|
|
|
@override |
|
|
void initState() { |
|
|
super.initState(); |
|
|
_scrollController.addListener(() { |
|
|
if (_scrollController.position.pixels == |
|
|
_scrollController.position.maxScrollExtent) { |
|
|
_getMoreData(); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
@override |
|
|
void dispose() { |
|
|
_scrollController.dispose(); |
|
|
super.dispose(); |
|
|
} |
|
|
|
|
|
_getMoreData() async { |
|
|
if (!isPerformingRequest) { |
|
|
setState(() => isPerformingRequest = true); |
|
|
List<int> newEntries = await fakeRequest( |
|
|
items.length, items.length + 10); //returns empty list |
|
|
if (newEntries.isEmpty) { |
|
|
double edge = 50.0; |
|
|
double offsetFromBottom = _scrollController.position.maxScrollExtent - |
|
|
_scrollController.position.pixels; |
|
|
if (offsetFromBottom < edge) { |
|
|
_scrollController.animateTo( |
|
|
_scrollController.offset - (edge - offsetFromBottom), |
|
|
duration: new Duration(milliseconds: 500), |
|
|
curve: Curves.easeOut); |
|
|
} |
|
|
} |
|
|
setState(() { |
|
|
items.addAll(newEntries); |
|
|
isPerformingRequest = false; |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
Widget _buildProgressIndicator() { |
|
|
return new Padding( |
|
|
padding: const EdgeInsets.all(8.0), |
|
|
child: new Center( |
|
|
child: new Opacity( |
|
|
opacity: isPerformingRequest ? 1.0 : 0.0, |
|
|
child: new CircularProgressIndicator(), |
|
|
), |
|
|
), |
|
|
); |
|
|
} |
|
|
|
|
|
@override |
|
|
Widget build(BuildContext context) { |
|
|
return new Scaffold( |
|
|
appBar: AppBar( |
|
|
title: Text("Infinite ListView"), |
|
|
), |
|
|
body: ListView.builder( |
|
|
itemCount: items.length + 1, |
|
|
itemBuilder: (context, index) { |
|
|
if (index == items.length) { |
|
|
return _buildProgressIndicator(); |
|
|
} else { |
|
|
return ListTile(title: new Text("Number $index")); |
|
|
} |
|
|
}, |
|
|
controller: _scrollController, |
|
|
), |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
/// from - inclusive, to - exclusive |
|
|
Future<List<int>> fakeRequest(int from, int to) async { |
|
|
return Future.delayed(Duration(seconds: 2), () { |
|
|
return List.generate(to - from, (i) => i + from); |
|
|
}); |
|
|
} |