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

Flutter中Swipeable Card Stack无法加载API JSON卡片问题

Flutter swipeable_card_stack 无法加载API返回的多张卡片问题修复

问题现象

使用swipeable_card_stack插件展示用户资料卡片时,静态数据能正常显示多张卡片,但传入API返回的JSON列表仅能显示单张卡片。

问题根源分析

  1. UI未触发刷新:API请求完成后向cardItems添加数据,但未调用setState()通知Flutter重建UI,导致初始空列表的UI状态一直保留。
  2. 重复创建CardView实例:SwipeableCardsSection的items参数中,对已存在的cardItems列表再次调用map创建新的CardView实例,导致插件无法正确识别卡片栈结构。
  3. CardView状态管理错误:_CardViewState通过构造函数保存数据,未使用widget对象的属性,后续数据更新无法同步到UI。
  4. 控制器状态未同步:在未刷新UI的情况下直接给_cardController添加Item,插件内部状态未与数据同步。

修复方案

1. 触发UI刷新

在API请求成功获取数据后,调用setState()让Flutter重新构建UI。

2. 简化卡片列表传递

直接将cardItems列表传入SwipeableCardsSection的items参数,避免重复创建实例。

3. 修正CardView数据获取方式

_CardViewState直接使用widget.name和widget.profileImage获取数据,确保数据更新能同步到UI。

4. 同步控制器状态

在setState()后重新初始化控制器并添加卡片,保证插件内部状态与数据一致。

修改后的代码

Dashboard 组件

class Dashboard extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return DashboardStateHolder();
  }
}

class DashboardStateHolder extends State<Dashboard> {
  int counter = 20;

  late QuadCardsResponse quadCardsResponseNew;
  late SharedPreferences sharedPreferences;
  late String? id = "",
      name = "",
      email = "",
      mobNum = "",
      colgName = "",
      colgShort = "",
      primaryColor = "",
      secondaryColor = "",
      accessToken = "",
      latitude = "",
      longitude = "";

  int cardsLength = 0;

  List<CardView> cardItems = <CardView>[];
  SwipeableCardSectionController _cardController = SwipeableCardSectionController();

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

  Future<void> getRequiredData() async {
    sharedPreferences = await SharedPreferences.getInstance();
    // 省略原有SharedPreferences读取逻辑

    await getSwipeCardsWS();
  }

  Future<void> getSwipeCardsWS() async {
    DesignConstants.showLoaderDialog(context, "Hold on ... !");

    SharedPreferences sharedPreferences = await SharedPreferences.getInstance();

    Response response;
    final Dio dio = Dio();
    dio.options.headers['content-Type'] = 'application/json';
    dio.options.headers["Authorization"] =
        "${sharedPreferences.getString('api_access_token')}";
    response = await dio.post(apiConfig.getSwipeCards, queryParameters: {
      'latitude': sharedPreferences.getString("lat"),
      'longitude': sharedPreferences.getString("long"),
    });
    print(response.data.toString());

    if (response.data['status'] == "success") {
      print("cards-length");
      print(response.data['data'].length);
      cardsLength = response.data['data'].length;
      Navigator.pop(context);
      quadCardsResponseNew = quadCardsResponseFromJson(response.toString());
      print("data-coming-here-now");
      print(quadCardsResponseNew.data.toString());

      cardItems.clear();
      quadCardsResponseNew.data.forEach((element) {
        cardItems.add(CardView(element.name, element.profileImages[0]));
      });

      print("_________LENGTH_________");
      print(cardItems.length);
      print(cardItems.toString());

      setState(() {});

      _cardController = SwipeableCardSectionController();
      for (var card in cardItems) {
        _cardController.addItem(card);
      }
    } else {
      Navigator.pop(context);
      print('Error in API response');
    }
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        Navigator.pushReplacement(
            context, MaterialPageRoute(builder: (context) => QuadHome()));
        return Future.value(false);
      },
      child: Scaffold(
        backgroundColor: ColorConstant.otherWhiteShade,
        floatingActionButton: Padding(
            padding: EdgeInsets.fromLTRB(0, 0, 0, 30),
            child: MaterialButton(
              child: Padding(
                padding: EdgeInsets.all(20),
                child: Text(
                  "quad",
                  style: TextStyle(
                    fontSize: 18.0,
                    fontFamily: 'comfortaa',
                    fontWeight: FontWeight.bold,
                    letterSpacing: 00,
                    color: ColorConstant.quadTextSwipeCard,
                  ),
                ),
              ),
              onPressed: () {
                setState(() {});
              },
              color: Colors.white,
              textColor: Colors.deepPurpleAccent,
              padding: EdgeInsets.all(16),
              shape: CircleBorder(
                  side: BorderSide(
                      color: ColorConstant.quadFloatingButtonColorShoadow
                          .withOpacity(0.3),
                      width: 3,
                      style: BorderStyle.solid)),
            )),
        floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
        body: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Center(
              child: Padding(
                padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
                child: Image.asset(
                  'images/quadheadbar.png',
                  height: 100,
                  width: 250,
                ),
              ),
            ),
            SwipeableCardsSection(
              cardController: _cardController,
              context: context,
              items: cardItems,
              onCardSwiped: (dir, index, widget) {},
              enableSwipeUp: true,
              enableSwipeDown: true,
            ),
            Container(
              margin: const EdgeInsets.symmetric(vertical: 20.0),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Padding(
                      padding: const EdgeInsets.fromLTRB(30, 0, 0, 0),
                      child: MaterialButton(
                        onPressed: () {},
                        color: ColorConstant.cancelOptionSwipeCard,
                        textColor: Colors.white,
                        child: Icon(
                          CupertinoIcons.clear,
                          size: 48,
                        ),
                        padding: EdgeInsets.all(16),
                        shape: CircleBorder(),
                      )),
                  Padding(
                      padding: const EdgeInsets.fromLTRB(0, 0, 30, 0),
                      child: MaterialButton(
                        onPressed: () {},
                        color: ColorConstant.likeOptionSwipeCard,
                        textColor: Colors.white,
                        child: Icon(
                          CupertinoIcons.heart_fill,
                          size: 48,
                        ),
                        padding: EdgeInsets.all(16),
                        shape: CircleBorder(),
                      ))
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

CardView 组件

class CardView extends StatefulWidget {
  final String name;
  final String profileImage;

  const CardView(this.name, this.profileImage, {super.key});

  @override
  State<CardView> createState() => _CardViewState();
}

class _CardViewState extends State<CardView> {
  TextEditingController graduationYearController = TextEditingController();
  String _chosenValue = '';
  int selectedValue = 0;
  late List<dynamic> streamList = [];
  List<String> streamData = <String>[];

  late QuadCardsResponse quadCardsResponse;

  @override
  void initState() {
    super.initState();
    print("profile-image");
    print(widget.profileImage);
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(50),
      ),
      child: Stack(
        children: <Widget>[
          widget.profileImage.isNotEmpty
              ? Container(
                  foregroundDecoration: BoxDecoration(
                    borderRadius: BorderRadius.all(Radius.circular(50)),
                    image: DecorationImage(
                        image: NetworkImage(widget.profileImage),
                        fit: BoxFit.cover),
                  ),
                )
              : Container(
                  height: 200,
                  child: Image.asset(
                    'images/quad_placeholder.png',
                  ),
                ),
          Align(
            alignment: Alignment.bottomCenter,
            child: Container(
              height: 250,
              width: double.infinity,
              foregroundDecoration: BoxDecoration(
                borderRadius: const BorderRadius.all(Radius.circular(50)),
                gradient: LinearGradient(
                  end: const Alignment(0.0, -1),
                  begin: const Alignment(0.0, 0.4),
                  colors: <Color>[
                    const Color(0x8A000000),
                    ColorConstant.quadTextSwipeCard.withOpacity(0.0)
                  ],
                ),
              ),
            ),
          ),
          Align(
            alignment: Alignment.topCenter,
            child: Container(
                padding: const EdgeInsets.symmetric(
                    vertical: 16.0, horizontal: 16.0),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.start,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: const <Widget>[
                    Text(
                      "SDSU",
                      textAlign: TextAlign.start,
                      style: TextStyle(
                          color: Colors.white,
                          fontFamily: 'comfortaa',
                          fontWeight: FontWeight.w900,
                          letterSpacing: 2),
                    ),
                  ],
                )),
          ),
          Row(
            children: const [
              Align(
                alignment: Alignment.bottomLeft,
                child: Padding(
                  padding: EdgeInsets.fromLTRB(20, 00, 00, 60),
                  child: Chip(
                    backgroundColor: ColorConstant.chipColor,
                    label: Text('Business',
                      style: TextStyle(
                          color: ColorConstant.otherWhiteShade,
                          fontSize: 14.0,
                          fontFamily: 'comfortaa'),
                    ),
                  ),
                ),
              ),
              Align(
                alignment: Alignment.bottomLeft,
                child: Padding(
                  padding: EdgeInsets.fromLTRB(20, 00, 00, 60),
                  child: Chip(
                    backgroundColor: ColorConstant.chipColor,
                    label: Text('2024',
                        style: TextStyle(
                            color: ColorConstant.otherWhiteShade,
                            fontSize: 14.0,
                            fontFamily: 'comfortaa')
                    ),
                  ),
                ),
              ),
            ],
          ),
          Align(
            alignment: Alignment.bottomLeft,
            child: Container(
              padding:
                  const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.end,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  Padding(
                    padding: const EdgeInsets.fromLTRB(00, 00, 00, 100),
                    child: Text(
                      widget.name,
                      style: TextStyle(
                          color: Colors.white,
                          fontSize: 40.0,
                          fontFamily: 'comfortaa',
                          fontWeight: FontWeight.w900),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

内容的提问来源于stack exchange,提问作者Robert S

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 03:05:28