Home > other >  CheckBox ui is not updated though the item value of checkbox is updated, Getx flutter
CheckBox ui is not updated though the item value of checkbox is updated, Getx flutter

Time:06-02

enter image description here

Get the working code sample here

I have an RxList of addOnProducts which contains product and selected attributes.

I am trying to implement the simple multiSelectable grid View, but on clicking the checkBox the selected attribute changes but it is not reflected back to the ui, If i refresh it will be updated.

I tried Obx()=> (); widget , It is still not updating

My ProductController

class ProductsController extends GetxController {
  late Worker worker;
  static ProductsController instance = Get.find();
  RxList<ProductModel> products = RxList<ProductModel>([]);
  RxList<CheckProduct> addOnProducts = <CheckProduct>[].obs;
  String collection = "products";
  @override
  void onReady() {
    super.onReady();
    products.bindStream(getAllProducts());
    worker = once(products, (List<ProductModel> value) {
      fillAddOnProducts(value);
    }, condition: () => products.isNotEmpty);
  }


  Stream<List<ProductModel>> getAllProducts() => FirebaseFirestore.instance
      .collection(collection)
      .snapshots()
      .map((query) => query.docs
          .map((item) => ProductModel.fromMap(item.data(), item.id))
          .toList());
 
  void fillAddOnProducts(List<ProductModel> products) => {
        products.forEach((element) {
          addOnProducts.add(CheckProduct(product: element, selected: false));
        })
      };
}

class CheckProduct {
  ProductModel product;
  bool selected;
  CheckProduct(
      {required ProductModel this.product, required bool this.selected});
}

My Grid View

class AddOns extends StatelessWidget {
  const AddOns({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          actions: [],
          title: Text("Select Addons"),
        ),
        body: Obx(() => GridView.count(
              crossAxisCount: 2,
              children: productsController.addOnProducts
                  .map((element) => ProductWidget(product: element))
                  .toList(),
            )));
  }
}

class ProductWidget extends StatelessWidget {
  final CheckProduct product;
  const ProductWidget({Key? key, required this.product}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
        color: Colors.yellow,
        margin: EdgeInsets.all(10),
        child: Stack(
          alignment: Alignment.center,
          children: [
            Positioned(
              top: 4,
              left: 4,
              child: Checkbox(
                value: product.selected,
                onChanged: (value) {
                  print("value of the value is : $value");
                  print("value of product selected before is: "  
                      product.selected.toString());
                  product.selected = value!;
                  print("value of product selected after is: "  
                      product.selected.toString());
                },
              ),
            ),
          ],
        ));
  }
}

Therefore in the console it is :

I/flutter (20067): value of the value is : true
I/flutter (20067): value of product selected before is: false
I/flutter (20067): value of product selected after is: true

But the checkBox is not updating, it updates only when i refresh, How to overCome this? Adding Obx() to the parent isn't helping..

Find the github link to code below here which has just the question and and the problem faced..

CodePudding user response:

After going through your code. I've implemented the following that will change state without hot reload:

In your main dart you do not need to put your product controller here as you are not using it

main.dart

import 'package:flutter/material.dart';

import 'grid.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: GridSelect(),
    );
  }
}

Next, I have changed your grid class to generate a list of product widget as the size of the addProduct list length. In my opinion this is a better way to write GridView counts children. Remove obx from your gridview and change your stateful widget to stateless as you are using Getx. It will manage your state even in a stateless widget. Add your product controller here as you will access addProduct list from the controller class.

grid.dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:test_project/controllers/productController.dart';
import 'package:test_project/productWidget.dart';

class GridSelect extends StatelessWidget {
  final _controller = Get.put(ProductController());
  GridSelect({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GridView.count(
        crossAxisCount: 2,
        children: List.generate(_controller.addOnProducts.length, (index) => ProductWidget(index: index))
      ),
    );
  }
}

In your product controller class, remove the instance as it is not important. That is the only change here:

ProductController.dart

import 'package:get/get.dart';
import 'package:test_project/models/productModel.dart';

class ProductController extends GetxController {
  RxList<CheckProduct> addOnProducts = <CheckProduct>[].obs;

  @override
  void onReady() {
    super.onReady();
    addOnProducts.add(CheckProduct(product: ProductModel('productOne', 20)));
    addOnProducts.add(CheckProduct(product: ProductModel('productTwo', 25)));
    addOnProducts.add(CheckProduct(product: ProductModel('productThree', 30)));
    addOnProducts.add(CheckProduct(product: ProductModel('productFour', 40)));
  }
}

class CheckProduct {
  ProductModel product;
  RxBool selected = false.obs;
  CheckProduct({
    required this.product,
  });
}

Lastly, your productWidget class needs a required value index. So, the widget knows which index in gridview the user is clicking and use Obx() here in checkbox as you have an observable value selected here. Remember to always use Obx() when you have an obs value. This will update the widget whenever an obs value changes. Here, if you notice we are using Get.find() instead of Put as Get.put is already inside the scope so all you need to do is find the controller that you will use. You can find or put multiple controllers and update values as much as you want.

productWidget.dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:test_project/controllers/productController.dart';

class ProductWidget extends StatelessWidget {
  final ProductController _controller = Get.find();
  final int index;
  ProductWidget({Key? key, required this.index}) : super(key: key);

  @override
  Widget build(BuildContext context) {

    return Container(
      color: Colors.yellow,
      margin: EdgeInsets.all(20),
      child: Stack(
        alignment: Alignment.center,
        children: [
          Positioned(
            top: 4,
            left: 4,
            child: Obx(()=>Checkbox(
              value: _controller.addOnProducts[index].selected.value,
              onChanged: (value) {
                print("value of the value is : $value");
                print("value of product selected before is: "  
                    _controller.addOnProducts[index].selected.toString());
                _controller.addOnProducts[index].selected.value = value!;
                print("value of product selected after is: "  
                    _controller.addOnProducts[index].selected.toString());
              },
            )),
          )
        ],
      ),
    );
  }
}

Go through GetX documentation for proper use of GetX. Even though I have 2 apps in Playstore with GetX, I still go through documentation from time to time. They have a clear documentation on how to manage state.

CodePudding user response:

In ProductWidget adding an additional Obx() solved my problem

class ProductWidget extends StatelessWidget {
  final CheckProduct product;
  const ProductWidget({Key? key, required this.product}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
        color: Colors.yellow,
        margin: EdgeInsets.all(10),
        child: Stack(
          alignment: Alignment.center,
          children: [
            Positioned(
              top: 4,
              left: 4,
             // Even the child needs Obx() ; The parent's Obx() is not reflected here
              child: Obx(()=>(Checkbox( 
                value: product.selected,
                onChanged: (value) {
                  print("value of the value is : $value");
                  print("value of product selected before is: "  
                      product.selected.toString());
                  product.selected = value!;
                  print("value of product selected after is: "  
                      product.selected.toString());
                },
              ),))
            ),
          ],
        ));
  }
  • Related