I have a very interesting technique to show you how you can achieve your desired result in a best possible way.
Disclaimers
- I have used my data set to show how the list being appended with the new data, not the duplicate one
 
- I have demonstrated the data append when you click on a button, to see the upcoming data at the bottom, you can use your 
scrollController. You can do the operation in _loadMore() 
Idea
- We maintain 
two lists, one is original, and one which keep track of adding new data. Names are originalList and items. 
- We use two variables, one is 
perPageItems to show case the items based upon our will and presentItems to maintain how much data loaded till now. It starts with 0 
- While creating an item, we copy it to the 
items as well 
Follow the code, and I have added most of the comments for your ease. Hope you get the end result
class _MyHomePageState extends State<MyHomePage> {
  int perPageItems  = 15;
  int presentItems = 0;
  
  List<String> items = List<String>();
  List<String> originalItems = new List.generate(100, (index) => 'Hello $index');
  @override
  void initState() {
    super.initState();
    
    // copying the data source to the other list
    items.addAll(originalItems.getRange(presentItems, presentItems + perPageItems));
    // updating the present Items now, since it has perPageItems now in the page
    presentItems = presentItems + perPageItems;
  }
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Scrollbar(
        child: new ListView.builder(
          itemBuilder: (context, index) {
            // to check whether we have reached to the
            // the last page, which is the last item
            // as per the page count which is 15
            return (index == items.length ) ?
              Container(
                  color: Colors.greenAccent,
                  child: FlatButton(
                      child: Text("Load More"),
                      onPressed: () => _loadMore(),
                  ),
              ) : ListTile(
                  title: Text('${items[index]}'),
              );
          },
          // Here in the code itemCount if present i.e., 
          // no of items loaded is lessthan or equal to total no of items 
          // in original list then we return +1 so we can add LoadMore 
          // else we return the size of the list
          itemCount: (presentItems <= originalItems.length) ? items.length + 1 : items.length
        )
      ),
    );
  }
  // Here in the code if present + perPage will become new final no of 
  // list to be loaded is greater then out complete original list 
  // length then we pass originalItems.length in the getRange else we 
  // pass present + perPage in getRange
  void _loadMore() {
    setState(() {
        if((presentItems + perPageItems) > originalItems.length) {
            items.addAll(
                originalItems.getRange(presentItems, originalItems.length));
        } else {
            items.addAll(
                originalItems.getRange(presentItems, presentItems + perPageItems));
        }
        presentItems = presentItems + perPageItems;
    });
  }
}
Result
