How to prevent Flutter app from scrolling to top after calling setState?

Viewed 5380

I have an ExpansionPanelList inside a SingleChildScrollView. Whenever I (un)fold a panel and therefore call setState, the SingleChildScrollView scrolls back to the top. How can I prevent this?

@override
Widget build(BuildContext context) {
  final _scaffoldKey = new GlobalKey<ScaffoldState>();
  return new Scaffold(
    key: _scaffoldKey,
    appBar: new AppBar(
      title: new Text(widget.title),
    ),
    body: new SingleChildScrollView(
      child: new ExpansionPanelList(
        children: <ExpansionPanel>[
          // panels
        ],
        expansionCallback: (int index, bool isExpanded) {
          setState(() {
            // toggle expanded
          });
        },
      ), // ExpansionPanelList
    ), // SingleChildScrollView
  ); // Scaffold
}

This answer suggests using a custom ScrollController with keepScrollOffset set to true, however this is the default value and setting it explicitly to true therefore does not change anything.

2 Answers

That's because you are using a new Key every time you rebuild the widget (setState).

To fix your issue just move the code below outside the build method

 final _scaffoldKey = new GlobalKey<ScaffoldState>(); 

Like this :

 final _scaffoldKey = new GlobalKey<ScaffoldState>();

    @override
    Widget build(BuildContext context) {
      return new Scaffold(
        key: _scaffoldKey,
        appBar: new AppBar(
          title: new Text(widget.title),
        ),

I was having the same problem, and somehow the answer from @diegoveloper did not do the trick for me.

What i ended up doing was separating the SingleChildScrollView in an independent StatefulWidget: That also fixed the scroll animation.

My code then ended up being something like

Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: MyExpansionList(),
...

class MyExpansionListextends StatefulWidget {

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

class _MyExpansionListState extends State<MyExpansionList> {
  @override
  Widget build(BuildContext context) {

    return Scrollbar(
      isAlwaysShown: true,
      showTrackOnHover: true,
      child: SingleChildScrollView(
        child: Column(
          children: [
            ExpansionPanelList(animationDuration: Duration(seconds: 1),

In this way the setState() did update only the ExpansionPanelList/ScrollView and not the whole Scaffold.

I hope this also helps others facing same problem...

Related