This is an interesting problem, I had some time to deal with this, and it appears the scroll controller is updated well as I see in the log. But, for some reason, it didn't work, so I put scroll view around the list and gave the ListView.builder no scroll. Only this to take account for is the last parts of the list, because you don't want to scroll more down if you are in the last parts because you cant go down more.
This probably isn't the best solution but it is what I managed to put together, hope this helps.
import 'dart:developer';
import 'package:flutter/material.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 Scroll Bug Demo',
home: MainScreen(),
);
}
}
class MainScreen extends StatefulWidget {
@override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
final _demoList = List.generate(100, (index) => 'List Item $index');
int _expandedIndex = -1;
final _scrollController = ScrollController();
bool _isExpanded = false;
bool _isInLastPart = false;
@override
Widget build(BuildContext context) {
_isInLastPart = (_expandedIndex > _demoList.length - 15);
final _size = MediaQuery.of(context).size;
return SafeArea(
child: Scaffold(
body: SingleChildScrollView(
controller: _scrollController,
child: SizedBox(
height: (48 * _demoList.length).toDouble() +
((_isExpanded && _isInLastPart) ? 80 : 0).toDouble(),
width: _size.width,
child: ListView.builder(
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return GestureDetector(
onTap: () => clickItem(index),
child: Container(
color: Colors.grey,
height: (_expandedIndex == index && _isExpanded) ? 120 : 40,
margin: EdgeInsets.only(bottom: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_demoList[index]),
if (_expandedIndex == index && _isExpanded)
Column(
children: <Widget>[
Text('text'),
Text('text'),
Text('text'),
Text('text'),
Text('text'),
],
),
],
),
),
);
},
itemCount: _demoList.length,
),
),
),
),
);
}
clickItem(int index) {
setState(() {
if (_expandedIndex == index)
_isExpanded = !_isExpanded;
else
_isExpanded = true;
_expandedIndex = index;
});
if (_expandedIndex < _demoList.length - 15) {
// log("expanded index_: $_expandedIndex, index: $index, jump to: ${index * 48.toDouble()}/${99 * 48.toDouble()}, scroll> ${_scrollController.position.pixels},isExpanded: $_isExpanded");
_scrollController.jumpTo(index * 48.toDouble());
} else {
if (_isExpanded) {
// log("expanded index_: $_expandedIndex, index: $index, jump to: ${83 * 48.toDouble() + 8}/${99 * 48.toDouble()}, scroll> ${_scrollController.offset},isExpanded: $_isExpanded");
_scrollController.jumpTo(83 * 48.toDouble() + 80 + 8);
} else {
// log("expanded index_: $_expandedIndex, index: $index, jump to: ${83 * 48.toDouble() + 80 + 8}/${99 * 48.toDouble()}, scroll> ${_scrollController.offset}, isExpanded: $_isExpanded");
_scrollController.jumpTo(83 * 48.toDouble() + 8);
}
}
}
}