You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中选中索引时如何动态更新Wrap子组件的边框样式?

问题:Flutter中无法动态更新Wrap组件内容器的边框样式

我写了一个生成Widget列表并存入变量,再作为Wrap子元素的函数。现在的问题是,想根据选中的索引动态更新对应容器的边框样式,但边框始终停留在列表生成时的初始选中状态,没法随索引变化更新。

现有代码

变量与生成函数

int selectedFeature = 0;
List<Widget> features = <Widget>[];

void generateFeatured() {
    var featured = <Widget>[];
    if (movies.length >= 5) {
      for (var i = 0; i < 5; i++) {
        featured.add(
          Container(
            height: 150,
            width: 220,
            decoration: BoxDecoration(
              border: selectedFeature == i
                  ? Border.all(color: const Color(0xff39a936), width: 2)
                  : Border.all(color: Colors.transparent),
              borderRadius: BorderRadius.circular(25),
              image: DecorationImage(
                fit: BoxFit.cover,
                alignment: Alignment.topCenter,
                image: NetworkImage(
                  movies[i]["cover_image"].toString(),
                ),
              ),
            ),
            child: Padding(
              padding: const EdgeInsets.only(
                left: 20,
                right: 20,
                bottom: 15,
                top: 15,
              ),
              child: Align(
                alignment: Alignment.bottomLeft,
                child: Container(
                  padding:
                      const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
                  child: Text(
                    movies[i]["title"].toString().toUpperCase(),
                    style: const TextStyle(
                      fontFamily: "MADE",
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ).asGlass(
                  frosted: false,
                  clipBorderRadius: BorderRadius.circular(4),
                  tintColor: const Color(0xff41535f),
                ),
              ),
            ),
          ),
        );
      }
    } else {
      for (var i = 0; i < movies.length; i++) {
        featured.add(
          Container(
            height: 150,
            width: 220,
            decoration: BoxDecoration(
              border: selectedFeature == i
                  ? Border.all(color: const Color(0xff39a936), width: 2)
                  : Border.all(color: Colors.transparent),
              borderRadius: BorderRadius.circular(25),
              image: DecorationImage(
                fit: BoxFit.cover,
                alignment: Alignment.topCenter,
                image: NetworkImage(
                  movies[i]["cover_image"].toString(),
                ),
              ),
            ),
            child: Padding(
              padding: const EdgeInsets.only(
                left: 20,
                right: 20,
                bottom: 15,
                top: 15,
              ),
              child: Align(
                alignment: Alignment.bottomLeft,
                child: Container(
                  padding:
                      const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
                  child: Text(
                    movies[i]["title"].toString().toUpperCase(),
                    style: const TextStyle(
                      fontFamily: "MADE",
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ).asGlass(
                  frosted: false,
                  clipBorderRadius: BorderRadius.circular(4),
                  tintColor: const Color(0xff41535f),
                ),
              ),
            ),
          ),
        );
      }
    }
    setState(() {
      features = featured;
    });
  }

Wrap组件使用

Wrap(
    alignment: WrapAlignment.start,
    spacing: 15,
    runSpacing: 15,
    children: features,
),

问题表现

边框始终保持列表生成时的初始选中状态,无法随selectedFeature的变化更新,错误的图片带有边框:
问题截图

解决方法

问题根源是你在generateFeatured函数中创建Container时,直接把当时的selectedFeature == i的结果固化到了Widget中,后续selectedFeature变化时,这些已经生成的Widget不会重新计算边框样式。

方式1:直接在build里生成列表(最简单高效)

把生成列表的逻辑移到build方法中,这样每次setState触发重建时,都会重新计算每个容器的边框样式:

@override
Widget build(BuildContext context) {
  return Wrap(
    alignment: WrapAlignment.start,
    spacing: 15,
    runSpacing: 15,
    children: _buildFeaturedItems(),
  );
}

List<Widget> _buildFeaturedItems() {
  var featured = <Widget>[];
  int count = movies.length >=5 ? 5 : movies.length;
  for (var i = 0; i < count; i++) {
    featured.add(
      GestureDetector(
        onTap: () {
          setState(() {
            selectedFeature = i;
          });
        },
        child: Container(
          height: 150,
          width: 220,
          decoration: BoxDecoration(
            border: selectedFeature == i
                ? Border.all(color: const Color(0xff39a936), width: 2)
                : Border.all(color: Colors.transparent),
            borderRadius: BorderRadius.circular(25),
            image: DecorationImage(
              fit: BoxFit.cover,
              alignment: Alignment.topCenter,
              image: NetworkImage(movies[i]["cover_image"].toString()),
            ),
          ),
          child: Padding(
            padding: const EdgeInsets.only(left:20, right:20, bottom:15, top:15),
            child: Align(
              alignment: Alignment.bottomLeft,
              child: Container(
                padding: const EdgeInsets.symmetric(horizontal:4, vertical:2),
                child: Text(
                  movies[i]["title"].toString().toUpperCase(),
                  style: const TextStyle(fontFamily: "MADE", fontWeight: FontWeight.bold),
                ),
              ).asGlass(
                frosted: false,
                clipBorderRadius: BorderRadius.circular(4),
                tintColor: const Color(0xff41535f),
              ),
            ),
          ),
        ),
      ),
    );
  }
  return featured;
}

方式2:封装独立的StatefulWidget(适合复杂子项)

如果子项逻辑复杂,不想每次重建所有子Widget,可以把电影卡片封装成独立的StatefulWidget,让它根据传入的选中状态自动更新样式:

class MovieCard extends StatefulWidget {
  final Map movie;
  final int index;
  final int selectedIndex;
  final VoidCallback onTap;

  const MovieCard({
    super.key,
    required this.movie,
    required this.index,
    required this.selectedIndex,
    required this.onTap,
  });

  @override
  State<MovieCard> createState() => _MovieCardState();
}

class _MovieCardState extends State<MovieCard> {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: widget.onTap,
      child: Container(
        height: 150,
        width: 220,
        decoration: BoxDecoration(
          border: widget.selectedIndex == widget.index
              ? Border.all(color: const Color(0xff39a936), width: 2)
              : Border.all(color: Colors.transparent),
          borderRadius: BorderRadius.circular(25),
          image: DecorationImage(
            fit: BoxFit.cover,
            alignment: Alignment.topCenter,
            image: NetworkImage(widget.movie["cover_image"].toString()),
          ),
        ),
        child: Padding(
          padding: const EdgeInsets.only(left:20, right:20, bottom:15, top:15),
          child: Align(
            alignment: Alignment.bottomLeft,
            child: Container(
              padding: const EdgeInsets.symmetric(horizontal:4, vertical:2),
              child: Text(
                widget.movie["title"].toString().toUpperCase(),
                style: const TextStyle(fontFamily: "MADE", fontWeight: FontWeight.bold),
              ),
            ).asGlass(
              frosted: false,
              clipBorderRadius: BorderRadius.circular(4),
              tintColor: const Color(0xff41535f),
            ),
          ),
        ),
      ),
    );
  }
}

父组件中使用:

Wrap(
  alignment: WrapAlignment.start,
  spacing: 15,
  runSpacing: 15,
  children: List.generate(
    movies.length >=5 ?5 : movies.length,
    (i) => MovieCard(
      movie: movies[i],
      index: i,
      selectedIndex: selectedFeature,
      onTap: () {
        setState(() {
          selectedFeature = i;
        });
      },
    ),
  ),
)

内容的提问来源于stack exchange,提问作者Alexander Roumeliotis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 04:15:39