Home > Blockchain >  How do you create multiple buttons in a ListView that are independent of each other?
How do you create multiple buttons in a ListView that are independent of each other?

Time:07-13

I have setup a Firestore database in which I have a collection 'products'. I use a ListView builder to print them out on ListTiles. I have also created leading "checkmark" IconButtons that appear for all ListTiles. My goal is to be able to press whichever of these checkmark buttons and change their color independently. Currently, all of the checkmark buttons change color when you press one of them. I don't know how to achieve this and would appreciate some help.

class HomeScreenProductList extends StatefulWidget {
  const HomeScreenProductList({Key? key}) : super(key: key);

  @override
  State<HomeScreenProductList> createState() => _HomeScreenProductListState();
}

class _HomeScreenProductListState extends State<HomeScreenProductList> {
  final CollectionReference _productsCollection =
      FirebaseFirestore.instance.collection('products');

  final Stream<QuerySnapshot> _products = FirebaseFirestore.instance
      .collection('products')
      .orderBy('product-name')
      .snapshots();

  deleteProduct(id) async {
    await _productsCollection.doc(id).delete();
  }

  Color tileColor = const Color.fromARGB(100, 158, 158, 158);
  Color iconColor = Colors.red;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
        stream: _products,
        builder: (
          BuildContext context,
          AsyncSnapshot<QuerySnapshot> snapshot,
        ) {
          if (snapshot.hasError) {
            return const Text('Something went wrong');
          }
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Text('Loading data');
          }

          final productData = snapshot.requireData;

          return ListView.builder(
            padding: const EdgeInsets.only(top: 16.0),
            itemCount: productData.size,
            itemBuilder: (context, index) {
              return Card(
                child: ListTile(
                  leading: Row(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      IconButton(
                        icon: Icon(Icons.check, color: iconColor),
                        onPressed: () {
                          setState(() {
                            if (iconColor != Colors.green) {
                              iconColor = Colors.green;
                            } else {
                              iconColor = Colors.red;
                            }
                          });
                        },
                      )
                    ],
                  ),
                  tileColor: tileColor,
                  shape: const UnderlineInputBorder(
                      borderSide: BorderSide(
                          width: 1, color: Color.fromARGB(120, 220, 220, 220))),
                  title: Text('${productData.docs[index]['product-name']}'),
                  textColor: const Color.fromARGB(255, 0, 0, 0),
                  trailing: Row(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      IconButton(
                        icon: const Icon(Icons.delete),
                        color: Colors.red,
                        padding: EdgeInsets.zero,
                        constraints: const BoxConstraints(),
                        onPressed: () {
                          print("Delete Button Pressed");
                          deleteProduct(snapshot.data?.docs[index].id);
                        },
                      )
                    ],
                  ),
                ),
              );
            },
          );
        });
  }
}

Full Code:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(const ShoppingListApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomeScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return HomeScreenAppBar();
  }
}

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

  final CollectionReference _products =
      FirebaseFirestore.instance.collection('products');

  final _controller = TextEditingController();

  String? _productName;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: const Color.fromARGB(245, 244, 253, 255),
        appBar: AppBar(
          backgroundColor: Colors.amberAccent,
          leading: IconButton(
            icon: const Icon(Icons.settings),
            iconSize: 20,
            color: Colors.black,
            splashColor: Colors.white,
            onPressed: () {
              print("Settings Button Pressed");
            },
          ),
          title: TextFormField(
            style: const TextStyle(fontSize: 18, fontFamily: 'Raleway'),
            controller: _controller,
            decoration: const InputDecoration(
                enabledBorder: UnderlineInputBorder(
                    borderSide:
                        BorderSide(color: Color.fromARGB(245, 244, 253, 255))),
                focusedBorder: UnderlineInputBorder(
                    borderSide: BorderSide(color: Colors.black)),
                hintText: 'Enter product',
                hintStyle:
                    TextStyle(color: Color.fromARGB(200, 255, 255, 255))),
            onChanged: (value) {
              _productName = value;
            },
          ),
          actions: [
            IconButton(
              icon: const Icon(Icons.add),
              iconSize: 24,
              color: Colors.black,
              splashColor: Colors.white,
              onPressed: () {
                if (_controller.text == '') {
                  return;
                } else {
                  _products
                      .add({'product-name': _productName, 'isbought': false})
                      .then(
                          (value) => print('New Product "$_productName" Added'))
                      .catchError((error) => print(
                          'Failed To Add Product "$_productName": $error'));
                  _controller.clear();
                }
              },
            )
          ],
        ),
        body: const HomeScreenProductList());
  }
}

class HomeScreenProductList extends StatefulWidget {
  const HomeScreenProductList({Key? key}) : super(key: key);

  @override
  State<HomeScreenProductList> createState() => _HomeScreenProductListState();
}

class _HomeScreenProductListState extends State<HomeScreenProductList> {
  final CollectionReference _productsCollection =
      FirebaseFirestore.instance.collection('products');

  final Stream<QuerySnapshot> _products = FirebaseFirestore.instance
      .collection('products')
      .orderBy('product-name')
      .snapshots();

  deleteProduct(id) async {
    await _productsCollection.doc(id).delete();
  }

  Color tileColor = const Color.fromARGB(100, 158, 158, 158);
  Color iconColor = Colors.red;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
        stream: _products,
        builder: (
          BuildContext context,
          AsyncSnapshot<QuerySnapshot> snapshot,
        ) {
          if (snapshot.hasError) {
            return const Text('Something went wrong');
          }
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Text('Loading data');
          }

          final productData = snapshot.requireData;

          return ListView.builder(
            padding: const EdgeInsets.only(top: 16.0),
            itemCount: productData.size,
            itemBuilder: (context, index) {
              return Card(
                child: ListTile(
                  leading: Row(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      IconButton(
                        icon: Icon(Icons.check, color: iconColor),
                        onPressed: () {
                          setState(() {
                            if (iconColor != Colors.green) {
                              iconColor = Colors.green;
                            } else {
                              iconColor = Colors.red;
                            }
                          });
                        },
                      )
                    ],
                  ),
                  tileColor: tileColor,
                  shape: const UnderlineInputBorder(
                      borderSide: BorderSide(
                          width: 1, color: Color.fromARGB(120, 220, 220, 220))),
                  title: Text('${productData.docs[index]['product-name']}'),
                  textColor: const Color.fromARGB(255, 0, 0, 0),
                  trailing: Row(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      IconButton(
                        icon: const Icon(Icons.delete),
                        color: Colors.red,
                        padding: EdgeInsets.zero,
                        constraints: const BoxConstraints(),
                        onPressed: () {
                          print("Delete Button Pressed");
                          deleteProduct(snapshot.data?.docs[index].id);
                        },
                      )
                    ],
                  ),
                ),
              );
            },
          );
        });
  }
}

    

    

CodePudding user response:

This is because you are using IconColor as a singular global variable. What you could possible do instead is create a boolean list for every data in the ProductData

List<bool> iconColorList = List<bool>.filled(ProductData.size, false);

and in the listview builder

                      IconButton(
                        icon: Icon(Icons.check, color: iconColorList[index] ? Colors.green : Colors.false),
                        onPressed: () {
                          setState(() {
                            if (iconColorList[index]) {
                              iconColorList[index] = false;
                            } else {
                              iconColorList[index] = true;
                            }
                          });
                        },
                      )

CodePudding user response:

You are using single variable iconColor to change all items. That's why all items are getting effected by changing any of it. You can create a List<int> to hold selected index , List<YourModelClass> or on your model create another bool variable bool icChecked = false.

This approach is holding selected index on state class.

On state class(_HomeScreenProductListState) create

List<int> selectedIndex = []

And on item tap event

icon: Icon(Icons.check, color: selectedIndex.contains(index)?mySelectedColor:unSelectedColor)
onPressed: () {
  if (selectedIndex.contains(index)) {
    selectedIndex.remove(index);
  } else {
    selectedIndex.add(index);
  }

  setState(() {});
},

mySelectedColor and unSelectedColor are two Color objects based on your need.

  • Related