'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