'How to update a single item in flutter list, as a best way
How to update a single item in flutter list?
I need best way.
Why I am saying best way?
I need to update a single item and dont want when update a single item on that all items exists in the list get rendered.
I mean when update a single item, then just that single item should be refreshed not the entire list.
because if the entire list gets render for updating just one item, on that time our app may does not have a good performance.
So is there a way to overcome this?
Solution 1:[1]
This is my way
foods[foods.indexWhere((element) => element.uid == food.uid)] = food;
some validation should be applied to avoid any null reference issues
Solution 2:[2]
You can do so by nesting a stream builder in each item builder.
This works by using the initialData
argument of the stream builder.
final Stream<Item> updateItemsSteram = ...;
final List<Item> initialList = [...];
ListView.builder(
itemBuilder: (context, idx) {
return StreamBuilder<Item>(
initialData: initialList[idx],
stream: updateItemsSteram.where((item) => item.id == initialList[idx].id),
builder: (ctx, snapshot) => MyCustomWidget(snapshot.data)
);
}
)
Though, you may want to benchmark this and compare it to updating the whole list, because flutter reuses the elements in all cases and the streams might just bring more overhead.
Solution 3:[3]
If you have a large number of items, it is fast to use setState() with ListView.builder(). For example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<int> ids = [];
initState() {
super.initState();
for (var i = 0; i < 100; i++) {
ids.add(i);
}
new Future.delayed(Duration(seconds: 3)).then((_) {
setState(() {
ids[4] = 1000;
});
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: ListView.builder(
itemCount: ids.length,
itemBuilder: (context, idx) {
return ListTile(title: Text('${ids[idx]}'));
},
),
),
);
}
}
Check out the ListView.builder docs for more details.
Solution 4:[4]
I solved this problem with a statefull widget to represent a item in ListView. If you want manipulate just a item data, you do it by Item widget. If you add or remove a item on the tail of list, the list will be not rerendered.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
import 'package:random_color/random_color.dart';
RandomColor randomColor = RandomColor();
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final wordPair = WordPair.random(); // Add this line.
return MaterialApp(
title: 'Welcome to Flutter',
home: Numbers(),
debugShowCheckedModeBanner: false,
);
}
}
/**
* View
*/
class Numbers extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return NumbersState();
}
}
/**
* Item
*/
class Item extends StatefulWidget {
int value;
Item({this.value});
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return ItemState();
}
}
class ItemState extends State<Item> {
@override
Widget build(BuildContext context) {
return Container(
color: randomColor.randomColor(colorBrightness: ColorBrightness.light),
child: ListTile(
title: Text(widget.value.toString()),
onTap: () {
setState(() {
widget.value++;
});
},
),
);
}
}
/**
* List
*/
class NumbersState extends State<Numbers> {
List<Item> numbers = new List<Item>();
String message = "Teste";
void addItem(int v) {
Item i = Item(value: v,);
numbers.add(i);
setState(() {});
}
Stream generateNumbers() async* {
for (int i in List.generate(5, (i) => i)) {
yield i;
}
}
@override
void initState() {
// super.initState();
generateNumbers().listen((dynamic i) {
print(i);
Timer(Duration(seconds: 0), () {
addItem(i);
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
// Add from here...
appBar: AppBar(
title: Text('ListView'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.ac_unit),
onPressed: () {
setState(() {
message = "Teste";
});
},
),
IconButton(
icon: Icon(Icons.arrow_upward),
onPressed: () {
setState(() {
message = "Teste";
});
},
),
IconButton(
icon: Icon(Icons.arrow_downward),
onPressed: () {
setState(() {
message = "Teste";
});
},
),
IconButton(
icon: Icon(Icons.add),
onPressed: () {
addItem(1);
},
),
IconButton(
icon: Icon(Icons.remove),
onPressed: () {
numbers.removeAt(numbers.length-1);
setState(() {});
},
)
],
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.all(16),
child: Text("Teste de ListView: \n 1. Atualizar item\n 2. Navegação\n 3. Movimento"),
),
Expanded(
child: ListView.builder(
reverse: true,
itemCount: numbers.length,
itemBuilder: (context, int i) {
return numbers[i];
})
)
],
),
); // ... to here.
}
}
Solution 5:[5]
If you the know the index then you easily change the value of its corresponding item.. If you don't know the index then first get the index from the value by using indexOf(E element, [int start = 0])
method. Then change the value as follows..
List<int> list = [1, 2];
growableList[0] = 87;
Solution 6:[6]
you can use this easy to read optimized way of accessing an object:
foods.firstWhere((element) => element.id == searchedId) = food;
instead of this
foods[foods.indexWhere((element) => element.uid == food.uid)] = food;
Solution 7:[7]
simple way is list[index]=updatevalue
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | David Buck |
Solution 2 | motia |
Solution 3 | John Ryan |
Solution 4 | Talison Costa |
Solution 5 | abhi |
Solution 6 | iliyass |
Solution 7 | Rizz wann |