【问题标题】:How to update UI or change state in a widget based on an action in another widget?如何根据另一个小部件中的操作更新 UI 或更改小部件中的状态?
【发布时间】:2019-11-13 19:37:02
【问题描述】:

我正在尝试使用 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
                  )
                )
              ),
            ],
          )
        ),
      ),
    );
}

我希望在点击一张卡片时,其他卡片中的图标会更改为默认图标,然后点击卡片上的图标会更改以指示其处于活动状态。

【问题讨论】:

    标签: android ios flutter


    【解决方案1】:

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

    【讨论】:

    • 将尝试此操作并回复您。同时,您为什么将播客声明为动态播客?
    • 我只是没有注意到你有一个模型。修复了上面的代码。
    【解决方案2】:

    您不能像那样动态更改图标,因为它已经在 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

    或者你应该根据布尔型playingState的值返回两种不同的Icon()类型

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

    【讨论】:

    • 更改图标的工作方式与现在一样。我需要帮助的是,当 cardA 正在播放并点击 cardB 时,cardA 将图标从暂停图标更改,而 cardB 更改为暂停图标
    • 如果是这种情况,我建议您创建两个布尔值。因为现在因为两张牌都共享同一个布尔“playingstate”,所以很难控制两个图标的状态。你只有两张卡还是会有很多?
    猜你喜欢
    • 2019-12-17
    • 2021-07-20
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 2021-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多