我最近問了一個關于如何動態創建一組表單的問題。我有一個答案。但問題是當洗掉組的索引時,它會洗掉最后添加的表單。但值是正確的。組表單由兩個文本表單欄位和一個下拉串列組成。(代碼如下)
例如,如果我添加 3 個動態組表單欄位并洗掉了第二個索引index[1],則 ui 更新將洗掉最后一個索引,但洗掉的值只是選定的索引。為什么 ui 沒有按預期作業?
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
class Purchased extends StatefulWidget {
const Purchased({Key? key}) : super(key: key);
@override
State<Purchased> createState() => _PurchasedState();
}
class _PurchasedState extends State<Purchased> {
List<UserInfo> list = [];
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
/// every time you add new Userinfo, it will generate new FORM in the UI
list.add(UserInfo());
setState(() {}); // dont forget to call setState to update UI
},
child: const Icon(Icons.add),
),
body: Column(
children: [
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder: ((context, index) {
return Column(
children: [
Text('phone'),
Text(list[index].phone),
Text('email'),
Text(list[index].email),
Text('category'),
Text(list[index].category)
],
);
})),
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder: ((context, index) {
return MyForm(
// dont forget use the key, to make sure every MyForm is has identity. to avoid missed build
key: ValueKey(index),
//pass init value so the widget always update with current value
initInfo: list[index],
// every changes here will update your current list value
onChangePhone: (phoneVal) {
if (phoneVal != null) {
list[index].setPhone(phoneVal);
setState(() {});
}
},
// every changes here will update your current list value
onchangeEmail: (emailVal) {
if (emailVal != null) {
list[index].setEmail(emailVal);
setState(() {});
}
},
onchangeCategory: (categoryVal) {
if (categoryVal != null) {
list[index].setCategory(categoryVal);
setState(() {});
}
},
onremove: () {
list.removeAt(index);
setState(() {});
});
})),
)
],
),
);
}
}
class MyForm extends StatefulWidget {
final UserInfo initInfo;
final Function(String?) onChangePhone;
final Function(String?) onchangeEmail;
final Function(String?) onchangeCategory;
final VoidCallback? onremove;
const MyForm({
key,
required this.initInfo,
required this.onChangePhone,
required this.onchangeEmail,
required this.onchangeCategory,
required this.onremove,
});
@override
State<MyForm> createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
TextEditingController _phoneCtrl = TextEditingController();
TextEditingController _emailCtrl = TextEditingController();
String? selected;
final List<String> category = [
'Manager',
'Reception',
'Sales',
'Service',
];
@override
void initState() {
super.initState();
// set init value
_phoneCtrl = TextEditingController(text: widget.initInfo.phone);
_emailCtrl = TextEditingController(text: widget.initInfo.email);
selected = widget.initInfo.category;
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
child: Column(
children: [
IconButton(onPressed: widget.onremove, icon: Icon(Icons.remove)),
TextFormField(
controller: _phoneCtrl,
onChanged: widget.onChangePhone,
),
TextFormField(
controller: _emailCtrl,
onChanged: widget.onchangeEmail,
),
DropdownButtonFormField2(
//key: _key,
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.zero,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5),
),
),
isExpanded: true,
hint: const Text(
'Select Category',
style: TextStyle(fontSize: 14),
),
icon: const Icon(
Icons.arrow_drop_down,
color: Colors.black45,
),
iconSize: 30,
buttonHeight: 60,
buttonPadding: const EdgeInsets.only(left: 20, right: 10),
items: category
.map((item) => DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
))
.toList(),
validator: (value) {
if (value == null) {
return 'Please select Catagory.';
}
},
onChanged: widget.onchangeCategory,
onSaved: widget.onchangeCategory)
/// same like TextFormField, you can create new widget below
/// for dropdown, you have to 2 required value
/// the initValue and the onchage function
],
),
);
}
}
class UserInfo {
///define
String _phone = '';
String _email = '';
String _category = '';
/// getter
String get phone => _phone;
String get email => _email;
String get category => _category;
///setter
void setPhone(String phone) {
_phone = phone;
}
void setEmail(String email) {
_email = email;
}
void setCategory(String category) {
_category = category;
}
}
任何幫助表示贊賞。
新的方法。適用于文本欄位但不適用于下拉選單
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
class Purchased extends StatefulWidget {
const Purchased({Key? key}) : super(key: key);
@override
State<Purchased> createState() => _PurchasedState();
}
class _PurchasedState extends State<Purchased> {
List<UserInfo> list = [];
List<TextEditingController> textControllerList = [];
List<TextEditingController> textControllerList1 = [];
Map<String, String> listCtrl = {};
@override
void dispose() {
textControllerList.forEach((element) {
element.dispose();
});
textControllerList1.forEach((element) {
element.dispose();
});
listCtrl;
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
/// every time you add new Userinfo, it will generate new FORM in the UI
list.add(UserInfo());
setState(() {}); // dont forget to call setState to update UI
},
child: const Icon(Icons.add),
),
body: Column(
children: [
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder: ((context, index) {
return Column(
children: [
Text('phone'),
Text(list[index].phone),
Text('email'),
Text(list[index].email),
Text('category'),
Text(list[index].category)
],
);
})),
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder: ((context, index) {
TextEditingController controller = TextEditingController();
TextEditingController controller1 = TextEditingController();
textControllerList.add(controller);
textControllerList1.add(controller1);
return MyForm(
// dont forget use the key, to make sure every MyForm is has identity. to avoid missed build
textEditingController: textControllerList[index],
textEditingController1: textControllerList1[index],
key: ValueKey(index),
//pass init value so the widget always update with current value
initInfo: list[index],
dataCtrl: listCtrl,
// every changes here will update your current list value
onChangePhone: (phoneVal) {
if (phoneVal != null) {
list[index].setPhone(phoneVal);
setState(() {});
}
},
// every changes here will update your current list value
onchangeEmail: (emailVal) {
if (emailVal != null) {
list[index].setEmail(emailVal);
setState(() {});
}
},
onchangeCategory: (categoryVal) {
if (categoryVal != null) {
list[index].setCategory(categoryVal);
setState(() {});
}
},
onremove: () {
list.removeAt(index);
textControllerList.removeAt(index);
textControllerList1.removeAt(index);
if (listCtrl.containsKey(index)) {
listCtrl.remove(index);
}
setState(() {});
});
})),
)
],
),
);
}
}
class MyForm extends StatefulWidget {
final UserInfo initInfo;
final Function(String?) onChangePhone;
final Function(String?) onchangeEmail;
final Function(String?) onchangeCategory;
final TextEditingController textEditingController;
final TextEditingController textEditingController1;
Map<String, String> dataCtrl = {};
final VoidCallback? onremove;
MyForm({
key,
required this.initInfo,
required this.onChangePhone,
required this.onchangeEmail,
required this.onchangeCategory,
required dataCtrl,
required this.onremove,
required this.textEditingController,
required this.textEditingController1,
});
@override
State<MyForm> createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
TextEditingController _phoneCtrl = TextEditingController();
TextEditingController _emailCtrl = TextEditingController();
String? selected;
final List<String> category = [
'Manager',
'Reception',
'Sales',
'Service',
];
@override
void initState() {
super.initState();
// set init value
_phoneCtrl = TextEditingController(text: widget.initInfo.phone);
_emailCtrl = TextEditingController(text: widget.initInfo.email);
selected = widget.initInfo.category;
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
child: Column(
children: [
IconButton(onPressed: widget.onremove, icon: Icon(Icons.remove)),
TextFormField(
controller: widget.textEditingController,
onChanged: widget.onChangePhone,
),
TextFormField(
controller: widget.textEditingController1,
onChanged: widget.onchangeEmail,
),
DropdownButtonFormField2(
//key: _key,
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.zero,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5),
),
),
isExpanded: true,
hint: const Text(
'Select Category',
style: TextStyle(fontSize: 14),
),
icon: const Icon(
Icons.arrow_drop_down,
color: Colors.black45,
),
iconSize: 30,
buttonHeight: 60,
//value: category[1],
value: selected!.isEmpty ? null : selected,
buttonPadding: const EdgeInsets.only(left: 20, right: 10),
items: category
.map((item) => DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
))
.toList(),
validator: (value) {
if (value == null) {
return 'Please select Catagory.';
}
},
onChanged: widget.onchangeCategory,
onSaved: widget.onchangeCategory,
)
/// same like TextFormField, you can create new widget below
/// for dropdown, you have to 2 required value
/// the initValue and the onchage function
],
),
);
}
}
class UserInfo {
///define
String _phone = '';
String _email = '';
String _category = '';
/// getter
String get phone => _phone;
String get email => _email;
String get category => _category;
///setter
void setPhone(String phone) {
_phone = phone;
}
void setEmail(String email) {
_email = email;
}
void setCategory(String category) {
_category = category;
}
}
uj5u.com熱心網友回復:
您正在動態創建TextEditingControllers,但無法跟蹤它們。您需要一種方法來跟蹤所有控制器,方法是創建一個List<TextEditingController>
除了上述之外,您的代碼不起作用的原因是因為您正在為方法textEditingController中的每個設定文本initState()。這只會被呼叫一次,因此當樹重建時它使用存盤在控制器中的“舊”值。
我提出以下建議:
MyForm()應該將 atextEditingController作為引數- 在
Purchase()課堂上創建一個List<TextEditingControllers> ListView.builder每次添加新小部件時,使用索引動態地將 textController 添加到串列中。removeAt()呼叫該方法時洗掉 textController 。- 不要忘記處理你的 textEditingControllers
請參考下面的代碼。注意:為了簡單起見,我只留下了電話控制器
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Purchased(),
);
}
}
class Purchased extends StatefulWidget {
const Purchased({Key? key}) : super(key: key);
@override
State<Purchased> createState() => _PurchasedState();
}
class _PurchasedState extends State<Purchased> {
List<UserInfo> list = [];
List<TextEditingController> textControllerList = [];
@override
void dispose() {
textControllerList.forEach((element) {
element.dispose();
});
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
/// every time you add new Userinfo, it will generate new FORM in the UI
list.add(UserInfo());
setState(() {}); // dont forget to call setState to update UI
},
child: const Icon(Icons.add),
),
body: Column(
children: [
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder: ((context, index) {
return Column(
children: [
const Text('phone'),
Text(list[index].phone),
],
);
})),
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder: ((context, index) {
TextEditingController controller = TextEditingController();
textControllerList.add(controller);
return MyForm(
// dont forget use the key, to make sure every MyForm is has identity. to avoid missed build
textEditingController: textControllerList[index],
key: ValueKey(index),
//pass init value so the widget always update with current value
initInfo: list[index],
// every changes here will update your current list value
onChangePhone: (phoneVal) {
if (phoneVal != null) {
setState(() {
list[index].setPhone(phoneVal);
});
}
},
// every changes here will update your current list value
onremove: () {
list.removeAt(index);
textControllerList.removeAt(index);
setState(() {});
});
})),
)
],
),
);
}
}
class MyForm extends StatefulWidget {
final UserInfo initInfo;
final Function(String?) onChangePhone;
final TextEditingController textEditingController;
final VoidCallback? onremove;
const MyForm({
super.key,
required this.initInfo,
required this.onChangePhone,
required this.onremove,
required this.textEditingController,
});
@override
State<MyForm> createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
child: Column(
children: [
IconButton(
onPressed: widget.onremove,
icon: const Icon(
Icons.remove,
)),
TextFormField(
controller: widget.textEditingController,
onChanged: widget.onChangePhone,
),
],
),
);
}
}
class UserInfo {
///define
String _phone = '';
/// getter
String get phone => _phone;
///setter
void setPhone(String phone) {
_phone = phone;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/513584.html
標籤:扑形式镖列表显示
