Reputation: 16290
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.
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,
),
),
),
);
}
}
Upvotes: 35
Views: 11417
Reputation: 206
Sometimes just increase the cacheExtent value
ListView(
cacheExtent: 2000,
...
Upvotes: 1
Reputation: 11
Now, how about PageStorageKey?
ListView.builder(
key: const PageStorageKey<String>("my_list"), //must be unique
itemBuilder: (BuildContext context, int index) {
return _widgetList[index];
},
itemCount: _widgetList.length,
),
The PageStorageKey will preserve the state of each item when scrolling up and down.
look the PageStorage class at official Flutter website
Upvotes: 1
Reputation: 4395
If you don't explicitly need a ListView.builder
, an option would be to change your ListView
for a Column
and wrap it inside a SingleChildScrollView
. The Column
will preserve the state of your widgets (because it does not load them lazily, like the ListView
does). That way you can avoid having to make your widgets make use of AutomaticKeepAliveClientMixin
, which is also correct, but a different approach. It just depends on what you need.
Upvotes: 5
Reputation: 103351
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
Upvotes: 64