简体   繁体   中英

How to update UI or change state in a widget based on an action in another widget?

I'm trying my hands on a music app using flutter. When an item/music card is tapped, the icon changes to the pause icon to show that its playing. But when i tap card-1, the icon changes(as it should), but when i tap card-2 or any other card, that card also changes icon but card-1 still has the pause icon. How do i change the icon of card-1 to default icon when any other card is tapped?

Currently im using a ListView.builder() to list out all the music cards. the actual card is built with a stateful widget in another file. and the state management is done in that file.

ListView in main.dart

ListView.builder(
  shrinkWrap: true,
  controller: ScrollController(),
  itemCount: allPodcasts.length,
  itemBuilder: (BuildContext context, index){
    return LongCard(podcast: allPodcasts[index]);
  },
)

longcard.dart

class LongCard extends StatefulWidget {

  final Podcast podcast;

  LongCard({this.podcast});

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

class _LongCardState extends State<LongCard> {

  bool playingState;

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

    setState((){
      playingState = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.only(bottom: 15.0),
      child: InkWell(
        onTap: (){
          setState((){
            if(playingState){
              playingState = false;
            }else{
              playingState = true;
            }
          });
        },
        child: Card(
          margin: EdgeInsets.zero,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(10.0),
          ),
          elevation: 1.0,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              ClipRRect(
                borderRadius: BorderRadius.only(topLeft: Radius.circular(10.0), bottomLeft: Radius.circular(10.0) ),
                child: Image(
                  image: AssetImage(widget.podcast.image),
                  height: 100.0,
                  width: 100.0,
                  fit: BoxFit.fill
                ),
              ),
              Padding(
                padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
                child: Align(
                  alignment: Alignment.topLeft,
                                                    child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    mainAxisAlignment: MainAxisAlignment.start,
                    children: <Widget>[
                      Text(
                        widget.podcast.date,
                        style: Theme.of(context).textTheme.body1,
                        overflow: TextOverflow.ellipsis,
                      ),
                      SizedBox(height:5.0),
                      Text(
                        widget.podcast.title,
                        style: Theme.of(context).textTheme.display1,
                        overflow: TextOverflow.ellipsis,
                      ),
                    ],
                  ),
                )
              ),
              Expanded(
                child: SizedBox()
              ),
              Container(
                alignment: Alignment.centerRight,
                height: 100.0,
                width: 70.0,
                decoration: BoxDecoration(
                  color: lightCoral,
                  borderRadius: BorderRadius.only(topRight: Radius.circular(10.0), bottomRight: Radius.circular(10.0) ),
                ),
                child: Center(
                  child: Icon(
                    (playingState == true ) ? Icons.pause : Icons.headset,
                    size: 40.0
                  )
                )
              ),
            ],
          )
        ),
      ),
    );
}

I expect that on tap on one card the icons in the other cards are changed to the default icon and then the icon on the tapped card is changed to indicate that its active.

You can't dynamically change the icon like that because as it is already defined in the Icon() widget. The Icon() widget is not interactive as explained in the documentation.

https://api.flutter.dev/flutter/widgets/Icon-class.html

You should change Icon() to IconButton() and then you can change the icon inside dynamically using iconData similar to this:

How to change IconButton's icon from callback function in flutter

Or you should return two different Icon() types depending on the value of the boolean playingState

 child: Center(
    child: playingState ? Icon(Icons.pause, size: 40.0) : Icon(Icons.headset, size: 40.0)
 )

Manage the state of the list from the parent widget that contains the ListView.

Your LongCard widget should be a stateless widget that only displays the data, not manages it. It will only tell the parent widget to switch to another index on press.

class Page extends StatefulWidget {
  @override
  _PageState createState() => _PageState();
}

class _PageState extends State<Page> {
  // List index of the podcast that is playing right now
  int activeIndex;

  void _setActivePodcast(int index) {
    setState(() {
      activeIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: ListView.builder(
        shrinkWrap: true,
        controller: ScrollController(),
        itemCount: allPodcasts.length,
        itemBuilder: (BuildContext context, index) {
          return LongCard(
            podcast: allPodcasts[index],
            listIndex: index,
            isPlaying: activeIndex == index,
            onPress: _setActivePodcast,
          );
        },
      ),
    );
  }
}

class LongCard extends StatelessWidget {
  final Podcast podcast;
  final bool isPlaying;
  final int listIndex;
  final Function(int index) onPress;

  const LongCard({
    Key key,
    this.podcast,
    this.listIndex,
    this.onPress,
    this.isPlaying: false,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.only(bottom: 15.0),
      child: InkWell(
        onTap: () => onPress(listIndex),
        child: Card(
            margin: EdgeInsets.zero,
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(10.0),
            ),
            elevation: 1.0,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                ClipRRect(
                  borderRadius: BorderRadius.only(
                      topLeft: Radius.circular(10.0),
                      bottomLeft: Radius.circular(10.0)),
                  child: Image(
                      image: AssetImage(podcast.image),
                      height: 100.0,
                      width: 100.0,
                      fit: BoxFit.fill),
                ),
                Padding(
                    padding:
                        EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
                    child: Align(
                      alignment: Alignment.topLeft,
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        mainAxisAlignment: MainAxisAlignment.start,
                        children: <Widget>[
                          Text(
                            podcast.date,
                            style: Theme.of(context).textTheme.body1,
                            overflow: TextOverflow.ellipsis,
                          ),
                          SizedBox(height: 5.0),
                          Text(
                            podcast.title,
                            style: Theme.of(context).textTheme.display1,
                            overflow: TextOverflow.ellipsis,
                          ),
                        ],
                      ),
                    )),
                Expanded(child: SizedBox()),
                Container(
                  alignment: Alignment.centerRight,
                  height: 100.0,
                  width: 70.0,
                  decoration: BoxDecoration(
                    color: lightCoral,
                    borderRadius: BorderRadius.only(
                        topRight: Radius.circular(10.0),
                        bottomRight: Radius.circular(10.0)),
                  ),
                  child: Center(
                    child: Icon(
                      isPlaying ? Icons.pause : Icons.headset,
                      size: 40.0,
                    ),
                  ),
                ),
              ],
            )),
      ),
    );
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM