5

I have some widgets in a SliverList that I don't want to lose state when I scroll offscreen. According to the docs, "...children in lazy list are wrapped in AutomaticKeepAlive widgets so that children can use KeepAliveNotifications to preserve their state when they would otherwise be garbage collected off-screen.", and my question is, how do I send a KeepAliveNotification? I've failed to understand the docs and have been unable to find any examples of its usage.

Where do I get or create the Listenable? In my app, when a user taps the section header it expands and changes alignment from horizontal to vertical. The state is lost when the user scrolls further down the page. Example of code below (removed some code that was irrelevant to the question to reduce clutter).

class Page extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new DefaultTabController(
        length: choices.length,
        child: new Scaffold(
          body: new CustomScrollView(slivers: <Widget>[
            new SliverAppBar(
            title: new Container(
              child: new TabBar(
                tabs: choices.map((Choice choice) {
                  return new Tab(
                    text: choice.title,
                    icon: new Image.asset(choice.icon, height:22.0),
                  );
                }).toList(),
              )),
            ),
            new SliverList(delegate:new SliverChildListDelegate([
              // Widget that I want to KeepAlive
              new TrendingArticles(),
              //Other widgets                                                     
            ])),
          ]),
        ),
      ),
    );
  }
}
//Widget that I want to KeepAlive
class TrendingArticles extends StatefulWidget{
  const TrendingArticles({ Key key }) : super(key: key, );

  @override
  _TrendingArticlesState createState() => new _TrendingArticlesState();
}

class _TrendingArticlesState extends State<TrendingArticles> {
  List<Article> articles = [];
  _getArticles() async {
    //Code for getting articles
  } 
  @override
  initState(){
    super.initState();
    _getArticles();   
  }
  void _handleSize(){
    //Handles changing orientation and size of widget
  }
  @override
  Widget build(BuildContext context) {
    return new Container(
      height:sectionHeight,
      child: new Column(children: <Widget>[
      new SectionHeader(title: "Articles", onTap:_handleSize,alignment:headerAlignment),
      new Container(
        height:sectionHeight - 55,
        child: new ListView(
          scrollDirection: scrollDirection,
          children: articles.map((Article article) {
            return new ArticleCard(
              height: cardHeight,
              article: article,
              onBannerTap: (Article article) {
                setState(() {
                  article.isFavorite = !article.isFavorite;
                });
              }
            );
          }).toList(),
        ),
      ),
    ]));   
  }
}
Christopher Thompson
  • 3,293
  • 1
  • 10
  • 16

1 Answers1

1

As mentioned here, you must add AutomaticKeepAliveClientMixin to the State class of your sliver list delegate child, you want to keep that state. In your example it is _TrendingArticlesState

Remember to call super.build(context); in the build method of your _TrendingArticlesState class.

  • Hi can you help at all? I'm facing a similar issue with CustomScrollView and SliverList... I have a WebView that keeps reloading when the page scrolls... I have cacheExtent high enough on my CustomScrollView. I also have all the prerequisites such as: ```super.build(context);``` and the ```bool get wantKeepAlive``` override... I also have the ```AutomaticKeepAliveClientMixin``` set... Not sure what's going on, but the WebView definitely doesn't reload in something like a SingleChildScrollView.. SO I'm guessing it's the CustomScrollView/SliverList that's destroying it.. – Chris Feb 20 '20 at 17:17
  • This answer must be accepted! It really worked for me! – Alexey Apr 16 '21 at 07:55