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

Flutter动态生成ListView Widget时TextField无法正常工作

问题分析与解决方案

核心问题

  1. 嵌套滚动组件冲突:页面外层使用SingleChildScrollView,内部又嵌套ListView.builder并包裹Flexible,这种嵌套会导致布局ParentData异常,键盘弹出时触发重绘,直接丢失TextField焦点。
  2. 布局语法错误:原代码中存在多余的闭合标签,破坏了Column的子组件结构,触发"Incorrect use of ParentDataWidget"报错。
  3. Widget预创建导致状态混乱:提前将所有Question Widget存入allQuestions列表再渲染,会导致TextField的状态无法正确绑定,焦点管理失效。

修复步骤

步骤1:移除嵌套滚动,统一用ListView承载所有内容

去掉外层的SingleChildScrollView,将页面所有元素(标题栏、问题列表、提交按钮)放入同一个ListView中,避免滚动冲突。

步骤2:修复布局语法错误

删除原代码中Flexible之后的多余闭合代码:

// 以下代码为冗余内容,直接删除
                        const SizedBox(
                          width: 20,
                        ),
                      ],
                    ),
                  ),

步骤3:动态构建Widget,避免预创建列表

不要提前将Widget存入allQuestions,而是在列表渲染时直接根据myCarClaims数据实时构建对应的Question Widget,确保状态正确绑定。

步骤4:优化State类的泛型声明

将SubmitClaimQuestionsState的泛型补全为State<SubmitClaimQuestionsPage>,避免类型警告。

修改后的完整页面代码

import 'dart:io';
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:my_car/AppUI/CustomWidgets/DateQuestionBox.dart';
import 'package:my_car/AppUI/CustomWidgets/MultiSelectQuestionBox.dart';
import 'package:my_car/AppUI/CustomWidgets/RadioQuestionBox.dart';
import 'package:my_car/AppUI/CustomWidgets/SelectQuestionBox.dart';
import 'package:my_car/AppUI/CustomWidgets/TextQuestionBox.dart';

import 'package:my_car/LocalData/AppColors.dart';
import 'package:flutter/services.dart';
import 'package:my_car/Models/MyClaimQuestionsResponse.dart';
import 'package:my_car/Models/VehiclesResponse.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../../Models/VehiclesResponse.dart';

class SubmitClaimQuestionsPage extends StatefulWidget {
  String vehicle;
  String coverage;
  SubmitClaimQuestionsPage(this.vehicle, this.coverage, {Key? key})
      : super(key: key);

  @override
  SubmitClaimQuestionsState createState() =>
      SubmitClaimQuestionsState(this.coverage, this.vehicle);
}

class SubmitClaimQuestionsState extends State<SubmitClaimQuestionsPage> {
  String vehicle;
  String coverage;
  SubmitClaimQuestionsState(this.coverage, this.vehicle);
  
  Future getMyVehicles() async {
    final prefs = await SharedPreferences.getInstance();
    final String? action = prefs.getString('vehiclesList');
    VehiclesResponse myVehiclesResponse =
        VehiclesResponse.fromJson(jsonDecode(action!));
    return myVehiclesResponse.vehicles;
  }

  List<MyCarClaimType> myCarClaims = <MyCarClaimType>[];

  // Fetch content from the json file
  Future generateQuestionsFromJson() async {
    final String response = await rootBundle
        .loadString('lib/Assets/JsonDataFiles/MyCarDataClaimQuestions.json');
    MyClaimQuestionsResponse myClaimQuestionsResponse =
        MyClaimQuestionsResponse.fromJson(jsonDecode(response));
    if (myClaimQuestionsResponse.myCarClaimTypes.isNotEmpty) {
      myCarClaims.clear();
      myCarClaims = myClaimQuestionsResponse.myCarClaimTypes
          .where((element) =>
              element.claimType.toLowerCase() == coverage.toLowerCase())
          .toList();
    }
    setState(() {}); // 仅更新状态,不预创建Widget
    return myCarClaims;
  }

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

  bool isVehicleSelected = false;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        fontFamily: 'Lato',
      ),
      home: Scaffold(
        backgroundColor: Color(AppColors.bgColor),
        body: SafeArea(
          child: ListView( // 用单个ListView承载所有内容
            physics: const ClampingScrollPhysics(),
            padding: const EdgeInsets.only(top: 30, bottom: 20),
            children: [
              // 标题栏
              Container(
                margin: const EdgeInsets.only(
                  bottom: 15,
                  left: 20,
                  right: 20,
                ),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    GestureDetector(
                      onTap: () {
                        Navigator.of(context).pop();
                      },
                      child: Align(
                          alignment: Alignment.centerLeft,
                          child: SvgPicture.asset(
                            'lib/Assets/Images/backarrow.svg',
                            height: 20,
                          )),
                    ),
                    Expanded(
                        child: Column(
                      children: [
                        Align(
                          alignment: Alignment.center,
                          child: Padding(
                            padding: const EdgeInsets.only(right: 20),
                            child: Text(
                              vehicle,
                              textAlign: TextAlign.start,
                              style: const TextStyle(
                                fontSize: 18,
                                letterSpacing: -0.5,
                                fontWeight: FontWeight.w700,
                              ),
                            ),
                          ),
                        ),
                        Align(
                          alignment: Alignment.center,
                          child: Padding(
                            padding: const EdgeInsets.only(right: 20),
                            child: Text(
                              coverage,
                              textAlign: TextAlign.start,
                              style: TextStyle(
                                  fontSize: 13,
                                  fontWeight: FontWeight.w500,
                                  color: Color(AppColors.primaryBlueColor)),
                            ),
                          ),
                        ),
                      ],
                    )),
                  ],
                ),
              ),
              // 动态生成问题列表
              if (myCarClaims.isNotEmpty)
                ...myCarClaims.map((claim) {
                  if (claim.questionType == "TEXT") {
                    return TextQuestionBox(claim);
                  } else if (claim.questionType == "SELECT") {
                    return SelectQuestionBox(claim);
                  } else if (claim.questionType == "RADIO") {
                    return RadioQuestionBox(claim);
                  } else if (claim.questionType == "MULTI_SELECT") {
                    return MultiSelectQuestionBox(claim);
                  } else if (claim.questionType == "DATE") {
                    return DateQuestionBox(claim);
                  }
                  return const SizedBox.shrink();
                }).toList(),
              // 提交按钮
              Container(
                width: double.infinity,
                margin: const EdgeInsets.only(
                  left: 20,
                  right: 20,
                  top: 15,
                ),
                child: TextButton(
                  style: ButtonStyle(
                    padding: MaterialStateProperty.all(
                        const EdgeInsets.symmetric(vertical: 16)),
                    backgroundColor: MaterialStateProperty.all(
                        Color(AppColors.primaryBlueColor)),
                    shape:
                        MaterialStateProperty.all<RoundedRectangleBorder>(
                            RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10.0),
                    )),
                  ),
                  child: Text(
                    'Submit Claim',
                    style: TextStyle(
                        fontWeight: FontWeight.w500,
                        fontSize: 15,
                        color:
                            Color(AppColors.primaryWhiteButtomTextColor)),
                  ),
                  onPressed: () {},
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

额外优化建议

在TextQuestionBox中,给TextField开启多行输入支持:

TextField(
  controller: txtControler,
  keyboardType: TextInputType.multiline,
  maxLines: null, // 允许自动换行
  style: const TextStyle(
      fontSize: 14, fontWeight: FontWeight.w500),
  decoration: const InputDecoration(
    border: InputBorder.none,
    filled: true,
    fillColor: Colors.transparent,
    hintText: 'Description',
  ),
),

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 19:01:13