如何根据另一个窗口小部件中的操作更新窗口小部件中的UI或更改状态?

时间:2019-07-03 07:00:27

标签: android ios flutter

我正在尝试使用Flutter开发音乐应用。轻按项目/音乐卡时,图标变为暂停图标以显示其正在播放。但是,当我点击card-1时,图标会更改(应更改),但是当我点击card-2或任何其他卡时,该卡也会更改图标,但card-1仍具有暂停图标。轻按任何其他卡时,如何将卡1的图标更改为默认图标?

当前,我正在使用ListView.builder()列出所有音乐卡。实际的卡是使用另一个文件中的有状态小部件构建的。状态管理在该文件中完成。

main.dart中的ListView

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
                  )
                )
              ),
            ],
          )
        ),
      ),
    );
}

我希望在点击一张卡时,将另一张卡上的图标更改为默认图标,然后将已轻按的卡上的图标更改为指示其活动状态。

2 个答案:

答案 0 :(得分:0)

您不能像这样动态更改图标,因为它已经在Icon()小部件中定义了。如文档中所述,Icon()小部件不是交互式的。

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

您应该将Icon()更改为IconButton(),然后可以使用类似于以下内容的iconData动态更改内部图标:

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

或者您应该返回两个不同的Icon()类型,具体取决于布尔状态的值

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

答案 1 :(得分:0)

通过包含ListView的父窗口小部件管理列表的状态。

您的 LongCard 小部件应该是仅显示数据而不管理数据的无状态小部件。它只会告诉父窗口小部件在按下时切换到另一个索引。

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,
                    ),
                  ),
                ),
              ],
            )),
      ),
    );
  }
}