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

Flutter/Dart提取CSV列表指定行实现交互功能方法

Flutter CSV行交互功能实现方案

首先统一约定CSV列索引(可根据你的实际表结构调整):

  • 索引0:需要展示的文本内容
  • 索引1:背景图资源文件名(如01.jpg)
  • 索引2:问题文本
  • 索引3、4:问题对应的两个选项

核心状态定义

在承载交互的CSVActionsRoute/YetAnotherRoute页面对应的State类中,定义状态变量即可:

// 解析好的CSV全局数据,可通过路由传参拿到
late List<List<dynamic>> csvData;
// 当前展示的行索引,初始为0对应第一行,有表头则改为1
int currentRowIndex = 0;
// 当前生效的背景图,为空时展示默认背景
String? currentBgImage;
// 当前展示的文本内容
String currentDisplayText = '';

在initState生命周期中完成初始值赋值:

@override
void initState() {
  super.initState();
  // 初始加载第一行内容
  final firstRow = csvData[currentRowIndex];
  currentDisplayText = firstRow[0].toString().trim();
  // 初始行背景判断
  final firstRowImg = firstRow[1].toString().trim();
  currentBgImage = firstRowImg.isEmpty ? null : firstRowImg;
  // 初始行如果有问题,首帧加载完弹框
  final firstRowQuestion = firstRow[2].toString().trim();
  if (firstRowQuestion.isNotEmpty) {
    WidgetsBinding.instance.addPostFrameCallback((_) => showQuestionDialog(firstRow));
  }
}

1. 按钮点击行切换逻辑

给交互按钮绑定如下点击回调,每次触发行号+1,加边界判断防止数组越界:

void onNextRowClick() {
  // 已经到最后一行直接返回,不做处理
  if (currentRowIndex >= csvData.length - 1) return;

  setState(() {
    currentRowIndex += 1;
    final currentRow = csvData[currentRowIndex];
    // 更新展示文本
    currentDisplayText = currentRow[0].toString().trim();
    // 更新背景图状态
    final imgName = currentRow[1].toString().trim();
    currentBgImage = imgName.isEmpty ? null : imgName;
  });

  // 行更新后判断是否需要弹问题框
  final question = csvData[currentRowIndex][2].toString().trim();
  if (question.isNotEmpty) {
    // 等页面渲染完成再弹框,避免同帧渲染冲突
    WidgetsBinding.instance.addPostFrameCallback((_) => showQuestionDialog(csvData[currentRowIndex]));
  }
}

2. 动态背景实现

页面根布局用Stack叠放,最底层放背景组件,根据currentBgImage的值切换资源,加错误兜底避免资源加载失败白屏:

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Stack(
      children: [
        // 背景层
        Positioned.fill(
          child: currentBgImage == null
            ? Image.asset(
                'assets/images/default_bg.jpg',
                fit: BoxFit.cover,
              )
            : Image.asset(
                'assets/images/$currentBgImage',
                fit: BoxFit.cover,
                // 资源不存在时回退默认背景
                errorBuilder: (_, __, ___) => Image.asset(
                  'assets/images/default_bg.jpg',
                  fit: BoxFit.cover,
                ),
              ),
        ),
        // 上层内容区:放文本展示组件、交互按钮
        Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Text(currentDisplayText, style: TextStyle(fontSize: 18)),
              SizedBox(height: 24),
              ElevatedButton(
                onPressed: onNextRowClick,
                child: Text('下一行'),
              )
            ],
          ),
        )
      ],
    ),
  );
}

注意:所有用到的图片资源需要提前在pubspec.yaml的assets节点下配置路径,否则会加载失败。


3. 问题弹窗实现

封装通用弹窗方法,传入当前行数据读取问题和选项:

void showQuestionDialog(List<dynamic> rowData) {
  final question = rowData[2].toString().trim();
  final option1 = rowData[3].toString().trim();
  final option2 = rowData[4].toString().trim();

  showDialog(
    context: context,
    barrierDismissible: false, // 可选:点击遮罩不关闭,必须选完才能进下一页
    builder: (context) => AlertDialog(
      title: Text(question),
      actions: [
        TextButton(
          onPressed: () {
            // 这里写选中第一个选项的业务逻辑
            Navigator.pop(context);
          },
          child: Text(option1),
        ),
        TextButton(
          onPressed: () {
            // 这里写选中第二个选项的业务逻辑
            Navigator.pop(context);
          },
          child: Text(option2),
        ),
      ],
    ),
  );
}

踩坑提示

  • 如果CSV文件带表头行,记得把currentRowIndex初始值改为1,跳过表头数据
  • 所有CSV读取出来的字段转字符串后必须加trim(),避免因为单元格存在不可见空格、换行导致空判断失效
  • 文件名大小写敏感,CSV里写的图片名必须和assets目录下的实际文件名完全一致,否则会加载失败
  • 路由传参时如果用Navigator传递csvData,大列表不要做冗余序列化,直接传引用即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 06:03:24