Home > Net >  How manage state of different widgets in ListView flutter?
How manage state of different widgets in ListView flutter?

Time:12-15

I am building a Flutter App where a List is shown using ListView. Which returns a ListTile with User Information. The ListTile contains a leading, title and subtitle with trailing set as a ElevatedButton.

Here's how it looks like: enter image description here

I want to tap on the 'Invite Button' and change its color, text, and subtitle of the ListTile

After tapping, it should look like this. enter image description here

How can I do this? Here's the code that I wrote. But it's changing the state of every List Item.

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

  @override
  State<InviteFriends> createState() => _InviteFriendsState();
}

class _InviteFriendsState extends State<InviteFriends> {

  bool isSelected = false;

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }
}

UI Under ListView.builder:

                       ListTile(
                            title: const Text('Haris'),
                            subtitle: const Text(
                              isSelected ? 'Invitation Sent' : 'Not Invited Yet',
                            ),
                            leading: CircleAvatar(
                              backgroundImage: NetworkImage(
                                _userProfile!.profilePhoto.toString(),
                              ),
                            ),
                            trailing: ElevatedButton(
                              style: ElevatedButton.styleFrom(
                                elevation: 0.0,
                                primary: isSelected ? Colors.orange : Colors.green,
                                side: BorderSide.none,
                                shape: RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(10.0),
                                ),
                              ),
                              child: const Text(
                                isSelected ? 'Invited': 'Invite',
                              ),
                              onPressed: () {
                                setState(() {
                               isSelected = !isSelected;
                                });
                              },
                            ),
                          );

I also tried Keys here in ListTile by using ValueKey(index) but it didn't work either. What should I do? Thanks

CodePudding user response:

Bring out Tile as a separate StatefulWidget so that each has its own state. Do not modify State List.builder. And you use one isSelected field for all Tile, and you will all refer to this condition.

Please provide more code and I can help. So that I understand the full picture

CodePudding user response:

You need create different class for listtiles. Do as follows:

 ListView.builder(
        itemCount: 3,
        shrinkWrap: true,
        itemBuilder: (ctx, i) {
          return MyListItems();
        }))

then MyListItems.

    class MyListItems extends StatefulWidget {
  @override
  MyListState createState() => MyListState();
}

class MyListState extends State<MyListItems> {
  bool isSelected = false;

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: const Text('Haris'),
      subtitle: Text(
        isSelected ? "Invitation Sent" : 'Not Invited Yet',
      ),
      leading: const CircleAvatar(
        backgroundImage: NetworkImage(
            'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQYtfZRhbGQtq2BapB2MXJfWIO2QriO5Wx3qQ&usqp=CAU'),
      ),
     // use your image here
      trailing: ElevatedButton(
        style: ElevatedButton.styleFrom(
          elevation: 0.0,
          primary: isSelected ? Colors.orange : Colors.green,
          side: BorderSide.none,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(10.0),
          ),
        ),
        child: Text(
          isSelected ? 'Invited' : 'Invite',
        ),
        onPressed: () {
          setState(() {
            isSelected = !isSelected;
          });
        },
      ),
    );
  }}

CodePudding user response:

         return ListView.builder(
             itemCount: itemCount.length
              itemBuilder: (BuildContext context, int index) {
              return Friend(
              //arg if available
              );
    });

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

  @override
  _FriendState createState() => _FriendState();
}

class _FriendState extends State<Friend> {
  bool isSelected = false;
  @override
  Widget build(BuildContext context) {
//put ListTile detail here
    return Row(
      children: [
        FlatButton(//deprecated but other buttons work
               key: PageStorageKey('random num'),//if you are interested in keeping the state of the button while navigating
            onPressed: () {
              setState(() {
                isSelected = !isSelected;
              });
            },
            child: Text(isSelected ? "INVITED" : "INVITE"))
      ],
    );
  }
}

CodePudding user response:

You create a list say "i" (variable) to know the state of each tile and initilze all with false. On tap change there state to true.

final List<bool> selected = List.generate(20, (i) => false);

Pass the List "i" to the listview.builder like:-

            itemBuilder: (BuildContext context, i) 

See the full code

import 'package:flutter/material.dart';

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

  @override
  State<InviteFriends> createState() => _InviteFriendsState();
}

class _InviteFriendsState extends State<InviteFriends> {
  bool isSelected = false;

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  final List<bool> selected = List.generate(20, (i) => false);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.only(top: 60),
        child: ListView.builder(
            // itemCount: i,
            itemBuilder: (BuildContext context, i) {
          return ListTile(
            title: const Text('Haris'),
            subtitle: Text(
              selected[i] ? 'Invitation Sent' : 'Not Invited Yet',
            ),
            leading: const CircleAvatar(
              backgroundImage: AssetImage('assets/profile.gif'),
            ),
            trailing: ElevatedButton(
                style: ElevatedButton.styleFrom(
                  elevation: 0.0,
                  primary: selected[i] ? Colors.orange : Colors.green,
                  side: BorderSide.none,
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(10.0),
                  ),
                ),
                child: Text(
                  selected[i] ? 'Invited' : 'Invite',
                ),
                onPressed: () {
                  setState(() => selected[i] = !selected[i]);
                  // setState(() {
                  //   isSelected = !isSelected;
                  // });
                }),
          );
        }),
      ),
    );
  }
}

Thank you

Output

enter image description here

enter image description here

enter image description here

  • Related