flutter - 使用 Consumer 的 TextField 问题,在键盘上点击“完成”会导致 textField 被清除
问题描述
编辑:试图删除帖子,但因为答案而无法删除。因此,我正在编辑整篇文章以更好地描述我遇到的新问题,因为我现在已经缩小了问题范围。
我看过其他在不同情况下解决此问题的帖子,但我无法用他们的解决方案解决我的情况。
我有一个下拉菜单,用于快速访问经常输入的销售人员,该菜单接受任何新的销售人员姓名并添加到列表中。这与我的屏幕有关,我无法弄清楚如何不让键盘清除点击“完成”的文本字段。
任何帮助表示赞赏!
屏幕:
class SmallAddSaleScreen extends StatefulWidget {
@override
_SmallAddSaleScreenState createState() => _SmallAddSaleScreenState();
}
class _SmallAddSaleScreenState extends State<SmallAddSaleScreen> {
final randomGen = Faker.withGenerator(random);
final uuid = Uuid();
final salesPerson = TextEditingController();
final saleAmount = TextEditingController();
final item = TextEditingController();
final dateEdited = DateTime.now().millisecondsSinceEpoch;
int saleDate = DateTime.now().millisecondsSinceEpoch;
String capitolizeFirst(String string) => string[0].toUpperCase() + string.substring(1);
@override
void initState() {
item.text = capitolizeFirst(randomGen.lorem.word());
saleAmount.text = (random.integer(13000) + random.decimal()).toStringAsFixed(2);
super.initState();
}
@override
Widget build(BuildContext context) {
String formattedDate = DateFormat.yMMMMd().format(DateTime.fromMillisecondsSinceEpoch(saleDate));
Future<void> selectSaleDate(BuildContext context) async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: DateTime.fromMillisecondsSinceEpoch(saleDate),
firstDate: DateTime(2015, 8),
lastDate: DateTime(2101),
);
if (picked != null)
setState(() {
saleDate = picked.millisecondsSinceEpoch;
});
}
return Scaffold(
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
children: [
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Sales Person:'),
SizedBox(width: 20),
Container(
width: 350,
child: Consumer(builder: (context, watch, child) {
salesPerson.text = watch(appState).selectedSaleSource!;
return TextField(
controller: salesPerson,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.words,
autocorrect: false,
decoration: InputDecoration(
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.black)),
labelText: 'Name',
),
);
}),
),
]),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Sale Amount:'),
SizedBox(width: 20),
Container(
width: 350,
child: TextField(
controller: saleAmount,
keyboardType: TextInputType.numberWithOptions(decimal: true),
textCapitalization: TextCapitalization.words,
autocorrect: false,
decoration: InputDecoration(
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.black)),
labelText: '\$',
),
),
),
]),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Item:'),
SizedBox(width: 75),
Container(
width: 350,
child: TextField(
controller: item,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.words,
autocorrect: false,
decoration: InputDecoration(
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.black)),
labelText: 'Item',
),
),
),
]),
SizedBox(height: 20),
Container(
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Sale date: '),
SizedBox(width: 40),
Container(
width: 350,
child: Text(formattedDate),
),
]),
),
],
),
SizedBox(width: 50),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(height: 40),
Container(
width: 250,
child: SalesSourcePulldownMenuWidget(),
),
SizedBox(height: 100),
Container(
width: 250,
child: TextButton(
style: ButtonStyle(
textStyle: MaterialStateProperty.all(TextStyle(fontWeight: FontWeight.bold)),
foregroundColor: MaterialStateProperty.all(Colors.white),
backgroundColor: MaterialStateProperty.all(Colors.amber[800])),
onPressed: () async {
await selectSaleDate(context);
},
child: const Text('SELECT SALE DATE')),
),
SizedBox(height: 20),
Container(
width: 250,
child: TextButton(
style: ButtonStyle(
textStyle: MaterialStateProperty.all(TextStyle(fontWeight: FontWeight.bold)),
foregroundColor: MaterialStateProperty.all(Colors.white),
backgroundColor: MaterialStateProperty.all(Colors.amber[800])),
child: const Text('SAVE'),
onPressed: () async {
if (salesPerson.text.isNotEmpty && saleAmount.text.isNotEmpty && item.text.isNotEmpty) {
final userID = context.read(appState).authorizedLocalUser!.localID;
final sale = Sale(
salesPerson: salesPerson.text.trim(),
saleAmount: double.parse(saleAmount.text.trim()),
item: item.text.trim(),
saleDate: saleDate,
dateModified: dateEdited,
createdBy: userID,
saleUUID: uuid.v4(),
);
await context.read(cudServices).createSaleData(context, sale);
await context.read(appState).addSaleSource(salesPerson.text.trim());
context.read(appState).setSaleSource('');
salesPerson.text;
context.read(appState).saveSuccess(context);
} else {
context.read(appState).emptyField(context);
}
item.text = capitolizeFirst(randomGen.lorem.word()); // Randomize input
saleAmount.text =
(random.integer(13000) + random.decimal()).toStringAsFixed(2); // Randomize input
setState(() {
saleDate = DateTime.now().millisecondsSinceEpoch; // Reset input
});
},
),
)
],
)
],
),
);
}
}
下拉小部件:
class _SalesSourcePulldownMenuWidgetState extends State<SalesSourcePulldownMenuWidget> {
late String? selectedSaleSource;
bool selected = false;
@override
void initState() {
context.read(appState).getSaleSources();
super.initState();
}
@override
Widget build(BuildContext context) {
late List<String> listItems;
late List<DropdownMenuItem<String>> menuItems;
return Consumer(builder: (context, watch, child) {
listItems = watch(appState).saleSources;
selectedSaleSource = watch(appState).selectedSaleSource;
if (selectedSaleSource == '') {
selected = false;
}
menuItems = listItems
.map((String value) => DropdownMenuItem<String>(
value: value,
child: Text(value),
))
.toList();
return DropdownButton<String>(
value: selected ? selectedSaleSource : null,
isExpanded: true,
hint: const Text('Existing persons'),
items: menuItems,
onTap: () {
selected = true;
},
onChanged: (String? newValue) {
setState(() {
if (newValue != null) {
context.read(appState).setSaleSource(newValue);
selectedSaleSource = newValue;
}
});
},
);
});
}
}
解决方案
这是正常行为,在 initState() 方法中为这些变量赋值:
class _SmallAddSaleScreenState extends State<SmallAddSaleScreen> {
final randomGen = Faker.withGenerator(random);
final uuid = Uuid();
final salesPerson = TextEditingController();
final saleAmount = TextEditingController();
final item = TextEditingController();
final dateEdited = DateTime.now().millisecondsSinceEpoch;
int saleDate = DateTime.now().millisecondsSinceEpoch;
String capitolizeFirst(String _s) => _s[0].toUpperCase() + _s.substring(1);
String formattedDate(DateTime _d) = DateFormat.yMMMMd().format(DateTime.fromMillisecondsSinceEpoch(_d));
@override
void initState() {
item.text = capitolizeFirst(randomGen.lorem.word());
saleAmount.text = (random.integer(13000) + random.decimal()).toStringAsFixed(2);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
...
推荐阅读
- angular - 如何使用 Spring Boot 项目在 Angular 中管理登录会话?
- python - Jupiter notebook 中的罚款目录
- javascript - 我想在html中创建一些以随机间隔更改其字体的文本
- c# - 使用 EPPlus 创建动态数量的工作簿
- eslint - 如何禁用有关某些未使用参数的警告,但保留“@typescript-eslint/no-unused-vars”规则
- javascript - NodeJS子进程命令检查正在运行的进程
- scala - Windows 10 Local 无法识别 Zeppelin 上下文
- regex - 正则表达式匹配以结尾的字符串中的最后一个单词
- javascript - Math.random 可以产生的最大值是多少?
- reactjs - 创建 React 应用程序:ESLint 配置不适用于 Typescript