I have created an example where I can add element to flutter list view dynamically. Each list view element is having button and text. when i add new element incorrect text is shown. I am adding incremental data and expected that each item will have value 1 , 2 , 3 , 4 .... But value of each item is 1 only. Also state of button pressed on a particular item is not maintained , after I scroll the list then only correct values are shown.
Can we not use stateful widgets inside listView? . Issue and Code is as follows
import 'dart:async';
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 Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> europeanCountries = [];
int _counter = 0;
int _perPage = 50;
ScrollController _myScrollController = ScrollController();
void _incrementCounter() async {
const ThreeSec = const Duration(seconds: 1);
this._counter++;
europeanCountries.insert(0, this._counter.toString());
print(europeanCountries);
setState(() {});
}
void getMoreData() {
print('adding More Product ');
europeanCountries.add("New Product");
setState(() {});
}
@override
void initState() {
// TODO: implement initState
super.initState();
_myScrollController.addListener(() {
double maxscroll = _myScrollController.position.maxScrollExtent;
double currentScroll = _myScrollController.position.pixels;
double delta = MediaQuery.of(context).size.height * 0.25;
print("mac Scroll Controller - " + maxscroll.toString());
print("Current Scroll Controller - " + currentScroll.toString());
print("delta Scroll Controller - " + delta.toString());
if ((maxscroll - currentScroll) < delta) {
getMoreData();
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _myListView(context),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Widget _myListView(BuildContext context) {
// backing data
return Container(
child: europeanCountries.length == 0
? Center(
child: Text('No Product to Display'),
)
: ListView.builder(
controller: _myScrollController,
itemCount: europeanCountries.length,
reverse: false,
itemBuilder: (context, index) {
return myContainer(
mytext: europeanCountries[index],
);
},
),
);
}
}
class myContainer extends StatefulWidget {
final String mytext;
const myContainer({Key key, this.mytext}) : super(key: key);
@override
_myContainerState createState() => _myContainerState(mytext);
}
class _myContainerState extends State<myContainer> {
final String mytext;
String _buttontext = "inprogress";
Color myClr = Colors.blue;
_changeText() {
setState(() {
_buttontext = "Done";
myClr = Colors.green;
});
}
_myContainerState(this.mytext);
@override
Widget build(BuildContext context) {
return Container(
height: 100,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue[700]),
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(8)),
),
margin: EdgeInsets.all(20),
child: Column(
children: <Widget>[
Text(mytext),
RaisedButton(
child: Text(_buttontext),
onPressed: _changeText,
color: myClr,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
splashColor: Colors.black,
)
],
),
);
}
}


keyissue. I'd checkout Emily Fortuna's great article about the matter. It basically boils down to adding a unique key value on list items. Also, yes, if you use thebuilderconstructor of theListView, the items will get built on demand and not hold state I think.