Search code examples
listviewflutterstate

How to maintain the state of widget in ListView?


I have a list of clickable widgets[i.e MarkWidget] when the widget is clicked the state of widget is changed. But when the list is scrolled to the bottom and scrolled back to the top all widget's state is reset to default.

How do I stop/force flutter to not redraw the existing widget in the list after scroll?

For Example : if I click on ITEM 1 is color changes from green to red but if scroll to the bottom and scroll back to top the ITEM 1 color changes back to green. I need the ITEM 1 color to be red if it is clicked irrespective of scrolling.

DEMO

Here is code :

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
      theme: ThemeData(
        platform: TargetPlatform.android,
      ),
    );
  }
}

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  List<Widget> _widgetList = List.generate(
    30,
    (index) => MarkWidget(
          key: Key('ITEM $index'),
          title: 'ITEM $index',
        ),
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("My List"),
      ),
      body: ListView.builder(
        key: new Key("my_list"), //new

        itemBuilder: (BuildContext context, int index) {
          return _widgetList[index];
        },
        itemCount: _widgetList.length,
      ),
    );
  }
}

class MarkWidget extends StatefulWidget {
  final String title;

  const MarkWidget({Key key, this.title}) : super(key: key);

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

class _MarkWidgetState extends State<MarkWidget> {
  bool _checked = false;

  @override
  Widget build(BuildContext context) {
    return FlatButton(
      onPressed: () {
        setState(() {
          _checked = !_checked;
        });
      },
      child: Container(
        padding: EdgeInsets.all(10.0),
        margin: EdgeInsets.all(10.0),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.all(
            Radius.circular(5.0),
          ),
          color: _checked ? Colors.red : Colors.green,
        ),
        child: Text(
          "${widget.title}",
          style: TextStyle(
            color: Colors.white,
            decoration: _checked ? TextDecoration.lineThrough : TextDecoration.none,
          ),
        ),
      ),
    );
  }
}


Solution

  • Add the AutomaticKeepAliveClientMixin mixin to your State of your StatefulWidget (Item widget), override the wantKeepAlive method and return true.

         class _MarkWidgetState extends State<MarkWidget> with AutomaticKeepAliveClientMixin{
           ...
    
             
        @override
        Widget build(BuildContext context) {
          // call this method.
          super.build(context);
          ...
        }
    
         @override
           bool get wantKeepAlive => true;
         }
    

    More info here: https://docs.flutter.io/flutter/widgets/AutomaticKeepAliveClientMixin-mixin.html