Flutter中如何实现点击TextField唤起ListView并填入选中内容
Flutter 实现点击 TextField 唤起列表并填充选中内容
1. 需要用到的核心 Widget 及用法
- TextField/TextFormField:作为展示选中内容的输入框,必须设置
readOnly: true,防止点击时弹出系统软键盘,同时通过onTap回调触发列表弹窗。 - showModalBottomSheet:移动端常用的底部弹窗容器,用来承载选项列表,相比AlertDialog能提供更贴合移动端的交互体验。
- ListView.builder:高效渲染选项列表,适合数据量不确定的场景;数据量较小时也可以用
Column替代。 - TextEditingController:绑定到每个TextField,用于更新和控制输入框的文本内容。
- StatefulWidget:因为需要维护选中状态和更新UI,页面必须是Stateful类型,通过
setState刷新输入框内容。
2. 如何将列表数据填入 TextField/TextFormField
- 为每个输入框创建独立的
TextEditingController,示例:final customerController = TextEditingController(); final brandController = TextEditingController(); final productController = TextEditingController(); - 在列表选项的
onTap回调中,将选中的内容赋值给对应控制器的text属性,通过setState触发UI更新后关闭弹窗:onTap: () { setState(() { controller.text = options[index]; }); Navigator.pop(ctx); } - 将控制器绑定到对应的TextField,输入框会自动同步控制器的文本内容。
3. 点击 TextField 时如何唤起列表
- 给TextField设置
readOnly: true,避免点击弹出软键盘; - 给TextField的
onTap属性绑定弹窗触发方法,示例:onTap: () => _showSelectionSheet(context, customers, customerController), - 实现通用弹窗方法,调用
showModalBottomSheet生成选项列表:void _showSelectionSheet(BuildContext context, List<String> options, TextEditingController controller) { showModalBottomSheet( context: context, builder: (ctx) { return ListView.builder( shrinkWrap: true, itemCount: options.length, itemBuilder: (_, index) { return ListTile( title: Text(options[index]), onTap: () { setState(() { controller.text = options[index]; }); Navigator.pop(ctx); }, ); }, ); }, ); }
完整示例代码
import 'package:flutter/material.dart'; class SelectionTextFieldDemo extends StatefulWidget { const SelectionTextFieldDemo({super.key}); @override State<SelectionTextFieldDemo> createState() => _SelectionTextFieldDemoState(); } class _SelectionTextFieldDemoState extends State<SelectionTextFieldDemo> { // 绑定每个输入框的控制器 final customerController = TextEditingController(); final brandController = TextEditingController(); final productController = TextEditingController(); // 模拟业务数据 final List<String> customerList = ['客户A', '客户B', '客户C', '客户D']; final List<String> brandList = ['品牌X', '品牌Y', '品牌Z']; final List<String> productList = ['产品1', '产品2', '产品3', '产品4', '产品5']; // 通用弹窗方法:复用选择逻辑 void _showOptionSheet(BuildContext context, List<String> options, TextEditingController controller) { showModalBottomSheet( context: context, isScrollControlled: true, builder: (ctx) { return Padding( padding: EdgeInsets.only(bottom: MediaQuery.of(ctx).viewInsets.bottom), child: ListView.builder( shrinkWrap: true, itemCount: options.length, itemBuilder: (_, index) { return ListTile( title: Text(options[index]), onTap: () { setState(() { controller.text = options[index]; }); Navigator.pop(ctx); }, ); }, ), ); }, ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('选择器示例')), body: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), child: Column( children: [ TextField( controller: customerController, readOnly: true, decoration: const InputDecoration( labelText: 'Müşteri', border: OutlineInputBorder(), ), onTap: () => _showOptionSheet(context, customerList, customerController), ), const SizedBox(height: 16), TextField( controller: brandController, readOnly: true, decoration: const InputDecoration( labelText: 'Marka', border: OutlineInputBorder(), ), onTap: () => _showOptionSheet(context, brandList, brandController), ), const SizedBox(height: 16), TextField( controller: productController, readOnly: true, decoration: const InputDecoration( labelText: 'Ürün', border: OutlineInputBorder(), ), onTap: () => _showOptionSheet(context, productList, productController), ), ], ), ), ); } // 销毁控制器,避免内存泄漏 @override void dispose() { customerController.dispose(); brandController.dispose(); productController.dispose(); super.dispose(); } }
内容的提问来源于stack exchange,提问作者ckot
相关产品推荐
相关产品推荐

