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

Flutter分步多页面表单开发求助:复杂状态与Firebase数据提交

Flutter分步表单完整实现方案

1. 构建左侧分步导航界面

用Row拆分左右布局,左侧固定宽度作为步骤导航区,右侧承载表单内容。通过currentStep变量控制激活步骤的高亮样式:

class StepperForm extends StatefulWidget {
  @override
  _StepperFormState createState() => _StepperFormState();
}

class _StepperFormState extends State<StepperForm> {
  int currentStep = 0;
  String? initialSelection;
  final FormData formData = FormData();

  final List<StepItem> steps = [
    StepItem(title: "初始选择", isCompleted: false),
    StepItem(title: "表单详情1", isCompleted: false),
    StepItem(title: "表单详情2", isCompleted: false),
    StepItem(title: "确认提交", isCompleted: false),
  ];

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        // 左侧步骤导航栏
        Container(
          width: 200,
          color: Colors.grey[100],
          child: ListView.builder(
            itemCount: steps.length,
            itemBuilder: (context, index) {
              return GestureDetector(
                onTap: () => setState(() => currentStep = index),
                child: Container(
                  padding: EdgeInsets.symmetric(vertical: 16, horizontal: 12),
                  decoration: BoxDecoration(
                    color: currentStep == index ? Colors.blue[50] : null,
                    borderLeft: BorderSide(
                      color: currentStep == index ? Colors.blue : Colors.grey[300]!,
                      width: 3,
                    ),
                  ),
                  child: Text(
                    steps[index].title,
                    style: TextStyle(
                      fontWeight: currentStep == index ? FontWeight.bold : FontWeight.normal,
                      color: currentStep == index ? Colors.blue : Colors.black54,
                    ),
                  ),
                ),
              );
            },
          ),
        ),
        // 右侧表单内容区
        Expanded(child: _buildFormPage()),
      ],
    );
  }
}

class StepItem {
  final String title;
  bool isCompleted;

  StepItem({required this.title, required this.isCompleted});
}

2. 根据初始选择动态切换表单页面

在第一步放置DropdownButton,选择后更新initialSelection,后续步骤根据该值渲染对应表单:

Widget _buildFormPage() {
  switch (currentStep) {
    case 0:
      return _buildInitialSelectionPage();
    case 1:
      return initialSelection == "类型A" ? _buildFormPageTypeA() : _buildFormPageTypeB();
    case 2:
      return _buildComplexFormPage();
    case 3:
      return _buildConfirmationPage();
    default:
      return SizedBox();
  }
}

// 初始选择页面
Widget _buildInitialSelectionPage() {
  return Padding(
    padding: EdgeInsets.all(20),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text("选择表单类型"),
        SizedBox(height: 10),
        DropdownButton<String>(
          value: initialSelection,
          hint: Text("请选择"),
          items: ["类型A", "类型B"].map((e) => DropdownMenuItem(value: e, child: Text(e))).toList(),
          onChanged: (value) {
            setState(() {
              initialSelection = value;
              steps[0].isCompleted = true;
            });
          },
        ),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: initialSelection != null ? () => setState(() => currentStep = 1) : null,
          child: Text("下一步"),
        ),
      ],
    ),
  );
}

3. 处理map、list等复杂类型表单状态

定义全局表单数据类,集中管理所有字段(包括复杂类型):

class FormData {
  String? simpleText;
  Map<String, dynamic> userInfo = {};
  List<String> selectedOptions = [];

  // 转为Firebase兼容的Map格式
  Map<String, dynamic> toFirebaseMap() {
    return {
      "simpleText": simpleText,
      "userInfo": userInfo,
      "selectedOptions": selectedOptions,
    };
  }
}

// 包含复杂类型的表单页面示例
Widget _buildComplexFormPage() {
  return Padding(
    padding: EdgeInsets.all(20),
    child: Column(
      children: [
        // 填充map类型数据
        TextField(
          decoration: InputDecoration(labelText: "用户名"),
          onChanged: (value) => formData.userInfo["name"] = value,
        ),
        TextField(
          decoration: InputDecoration(labelText: "年龄"),
          keyboardType: TextInputType.number,
          onChanged: (value) => formData.userInfo["age"] = int.tryParse(value),
        ),
        SizedBox(height: 20),
        // 多选框更新list类型数据
        Wrap(
          children: ["选项1", "选项2", "选项3"].map((option) {
            return CheckboxListTile(
              title: Text(option),
              value: formData.selectedOptions.contains(option),
              onChanged: (value) {
                setState(() {
                  value == true ? formData.selectedOptions.add(option) : formData.selectedOptions.remove(option);
                });
              },
            );
          }).toList(),
        ),
        SizedBox(height: 20),
        Row(
          children: [
            TextButton(onPressed: () => setState(() => currentStep = 1), child: Text("上一步")),
            ElevatedButton(onPressed: () => setState(() => currentStep = 3), child: Text("下一步")),
          ],
        ),
      ],
    ),
  );
}

4. 提交数据到Firebase

先在pubspec.yaml添加依赖:

dependencies:
  flutter:
    sdk: flutter
  firebase_core: ^2.15.0
  cloud_firestore: ^4.8.4

初始化Firebase并实现提交逻辑:

// main函数中初始化Firebase
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

// 确认提交页面
Widget _buildConfirmationPage() {
  return Padding(
    padding: EdgeInsets.all(20),
    child: Column(
      children: [
        Text("确认表单数据"),
        SizedBox(height: 20),
        Text("简单文本: ${formData.simpleText}"),
        Text("用户信息: ${formData.userInfo.toString()}"),
        Text("选中选项: ${formData.selectedOptions.toString()}"),
        SizedBox(height: 30),
        ElevatedButton(
          onPressed: _submitToFirebase,
          child: Text("提交到Firebase"),
        ),
      ],
    ),
  );
}

Future<void> _submitToFirebase() async {
  try {
    await FirebaseFirestore.instance.collection("form_submissions").add(formData.toFirebaseMap());
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("提交成功")));
    // 重置表单状态
    setState(() {
      currentStep = 0;
      initialSelection = null;
      formData.simpleText = null;
      formData.userInfo.clear();
      formData.selectedOptions.clear();
      steps.forEach((step) => step.isCompleted = false);
    });
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("提交失败: ${e.toString()}")));
  }
}

内容的提问来源于stack exchange,提问作者ahmet kaan caliskan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 00:53:19