英文:
Flutter Android/IOS - Dynamic list with TextInputAction.next not behaving as expected
问题
我有一个包含TextFormFields的动态列表。我已经添加了一个textInputAction,将其值设置为TextInputAction.next,所以点击下一个时它会移动到下一个字段。在页面底部,我添加了一个点击完成的按钮。应用的Scaffold的resizeToAvoidBottomInset设置为true,所以TextFields在到达底部时会滚动。
按钮需要放在可滚动区域之外,这样它就始终可见在底部。
问题是当我点击下一个时,键盘在达到按钮时关闭,这不是预期的行为。
当我添加了一个15的Padding时,在我的手机上问题得到了解决,但在其他设备上没有,所以这不是一个有效的解决方案。
我创建了一个示例项目,在我的手机(三星S22)上它不起作用:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.secondary,
title: Text(widget.title),
),
body: const ItemList(),
);
}
}
class ItemList extends StatefulWidget {
const ItemList({Key? key});
@override
State<ItemList> createState() => _ItemListState();
}
class _ItemListState extends State<ItemList> {
final _formKey = GlobalKey<FormState>();
final List<String> items = [];
@override
void initState() {
items.addAll(List<String>.generate(20, (int index) => 'String$index',
growable: true));
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
const Row(
children: [
Text('Title: '),
Text('Value'),
],
),
Form(
key: _formKey,
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
final bool isLast = items.length - 1 == index;
return Row(
children: [
Text(item),
const SizedBox(
width: 20,
),
Expanded(
child: TextFormField(
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
textInputAction: isLast
? TextInputAction.done
: TextInputAction.next,
decoration: const InputDecoration(
isDense: true,
),
),
),
],
);
},
),
),
],
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(15.0),
child: ElevatedButton(
onPressed: () {},
child: const SizedBox(
height: 40,
child: Center(
child: Text('Finish'),
),
),
),
),
),
],
);
}
}
英文:
I have a dynamic list containing TextFormFields. I've added a textInputAction with TextInputAction.next as the value, so it will move to the next field upon clicking next. At the bottom of the page, I've added a button to click finish. The app Scaffold has the resizeToAvoidBottomInset set to true, so the TextFields scroll upon reaching the bottom.
The button needs to be outside of the ScrollableView so it's always visible at the bottom.
The issue is that when I click next, the keyboard closes when reaching the button, which is not intended behavior.
Also when I add a Padding of 15 the issue is resolved on my phone but not others, so that is not a valid solution.
I've created a sample project where on my phone (Samsung S22) it doesn't work:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: const ItemList(),
);
}
}
class ItemList extends StatefulWidget {
const ItemList({
super.key,
});
@override
State<ItemList> createState() => _ItemListState();
}
class _ItemListState extends State<ItemList> {
final _formKey = GlobalKey<FormState>();
final List<String> items = [];
@override
void initState() {
items.addAll(List<String>.generate(20, (int index) => 'String$index',
growable: true));
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
const Row(
children: [
Text('Title: '),
Text('Value'),
],
),
Form(
key: _formKey,
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
final bool isLast = items.length - 1 == index;
return Row(
children: [
Text(item),
const SizedBox(
width: 20,
),
Expanded(
child: TextFormField(
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
textInputAction: isLast
? TextInputAction.done
: TextInputAction.next,
decoration: const InputDecoration(
isDense: true,
),
),
),
],
);
},
),
),
],
),
),
),
Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: const EdgeInsets.all(15.0),
child: ElevatedButton(
onPressed: () {},
child: const SizedBox(
height: 40,
child: Center(
child: Text('Finish'),
),
),
),
),
),
],
);
}
}
- I've tried adding a Stack and placing the button behind the List.
- I've placed
resizeToAvoidBottomInsetonfalsebut the page didn't scroll to my TextFormField anymore. - I've added a padding but that only works for my screen size.
Any help would be appreciated 🙂.
答案1
得分: 0
我在github上找到了解决方案。我只需要将以下内容添加到按钮中:
ElevatedButton(
focusNode: FocusNode(skipTraversal: true), //new line
onPressed: () {},
child: const SizedBox(
height: 40,
child: Center(
child: Text('完成'),
),
),
)
英文:
I found the solution on github. I only needed to add the following to the button:
ElevatedButton(
focusNode: FocusNode(skipTraversal: true), //new line
onPressed: () {},
child: const SizedBox(
height: 40,
child: Center(
child: Text('Finish'),
),
),
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论