skip to Main Content

Using signals I have a ListView that is backed by a ListSignal.

Adding and removing from this list is correctly rebuilding the ListView and adding new elements as expected. However I’m struggling to find a way to update the contents of one of these elements when the backing object is updated.

class ScannedProduct {
  final String name;
  final int quantity;

  ScannedProduct({required this.name, required this.quantity});
}

class MyCard extends StatelessWidget {
  final ScannedProduct product;

  MyCard({required this.product});

  Widget build(BuildContext context) {
    return Card(
      child: Row(
        children: [
          Text(product.name),
          Text(product.quantity.toString())
        ]
      )
    );
  }
}

class MyScreen extends StatefulWidget<_MyScreenState> {
  //omitted for brevity
}

class _MyScreenState extends State<MyScreenState> {
    ListSignal<ScannedProduct> products = ListSignal([]);

    Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: products.length,
      itemBuilder: (context, index) => MyCard(
        product: products[index]
      )
    );
  }
}

Given the above setup, the below triggers a rebuild as expected
products.add(ScannedProduct('SomeProduct', 1));

How can I trigger a similar update when running the following
products[0].quantity++;

2

Answers


  1. I think it’s easier to create a controller and then mutate the list in the controller. You can then call the method from your UI.
    Something like this below:

    class ProductController {
      ListSignal<ScannedProduct> products = ListSignal([]);
    
      void addProduct(ScannedProduct product) {
        products.value = [...products.value, product];
      }
    
      void incrementQuantity(int index) {
        if (index >= 0 && index < products.value.length) {
          products.value[index].quantity.value++;
        }
      }
    }
    
    Login or Signup to reply.
  2. you can use flutter built in class ChangeNotifier and wrap the widget that listen to any operation you specify with ListenableBuilder. all you need to do is call notifyListeners().

    lets take look at example below :

    class ProductsNotifier extends ChangeNotifier {
      ProductsNotifier({List<ScannedProduct> products}) : _products = products ;
      
      List<ScannedProduct> _products;
    
      List<ScannedProduct> get products => _products;
      set products(List<ScannedProduct> products){
        _products = products;
        notifyListeners();
      }
    
      void updateProductQuantityAt(int index){
        if(index > _products.length -1 || index < 0){
          // this index is not valid
          return;
        }
        _products[index].quantity += 1;
        // all you need to trigger the rebuild is [notifyListeners]
        notifyListeners();
      }
    
      // you even can trigger the update based on another list operation like replaceAt, replaceRange, etc
      
    
    
    }
    
    class MyScreen extends StatefulWidget<_MyScreenState> {
      //omitted for brevity
    }
    
    class _MyScreenState extends State<MyScreenState> {
        // add your list to this notifier
        final ProductsNotifier controller = ProductsNotifier(products: products);
        
        // dont forget to dispose it
        @override
        void dispose() {
          controller.dispose();
          super.dispose();
        }
    
        Widget build(BuildContext context) {
        //wrap the widget that needs to rebuild based on changes in this controller. This prevent any unnecessary updates to other widgets, == performance gain
        return ListenableBuilder(
          listenable: controller,
          child: ListView.builder(
            itemCount: products.length,
            itemBuilder: (context, index) => MyCard(
              product: products[index]
            )
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search