Flutter原始自动完成建议隐藏在软键盘下



我正在创建一个原始的自动完成小部件。问题是,如果小部件在屏幕的中心或底部附近,当我开始键入时,显示的自动建议会隐藏在软键盘下。如何构建选项ViewBuilder以克服选项隐藏在键盘下的问题?

样本源代码:

class AutoCompleteWidget extends StatefulWidget {
const AutoCompleteWidget(
Key key,
) : super(key: key);
@override
_AutoCompleteWidgetState createState() => _AutoCompleteWidgetState();
}
class _AutoCompleteWidgetState extends State<AutoCompleteWidget> {
late TextEditingController _textEditingController;
String? _errorText;
final FocusNode _focusNode = FocusNode();
final GlobalKey _autocompleteKey = GlobalKey();
List<String> _autoSuggestions = ['abc', 'def', 'hij', 'aub', 'bted' 'donfr', 'xyz'];
@override
void initState() {
super.initState();
_textEditingController = TextEditingController();
}
@override
void dispose() {
_textEditingController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return RawAutocomplete<String>(
key: _autocompleteKey,
focusNode: _focusNode,
textEditingController: _textEditingController,
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return _autoSuggestions;
}
return _autoSuggestions.where((dynamic option) {
return option
.toString()
.toLowerCase()
.startsWith(textEditingValue.text.toLowerCase());
});
},
optionsViewBuilder: (BuildContext context,
AutocompleteOnSelected<String> onSelected, Iterable<String> options) {
return Material(
elevation: 4.0,
child: ListView(
children: options
.map((String option) => GestureDetector(
onTap: () {
onSelected(option);
},
child: ListTile(
title: Text(option),
),
))
.toList(),
),
);
},
fieldViewBuilder: (
BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onSubmitted,
) {
return Card(
elevation: (null == _errorText ? 8 : 0),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
child: TextField(
controller: textEditingController,
focusNode: focusNode,
),
);
},
);
}
}

我提出的一个解决方案是使用TextFormField构建自己版本的简单自动完成小部件,并在其上设置scrollPadding

@override
Widget build(BuildContext context) {
return ListView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: [
// THE AUTOCOMPLETE INPUT FIELD
TextFormField(
focusNode: _focusNode,
scrollPadding: const EdgeInsets.only(bottom: 300),
maxLines: null,
key: const ValueKey('company_address'),
autocorrect: false,
enableSuggestions: false,
controller: widget.textEditingController,
validator: (value) {
if (value!.isEmpty) {
return _i10n.enterAName;
}
return null;
},
decoration: InputDecoration(
labelText: widget.labelText,
),
textInputAction: TextInputAction.next,
onChanged: (_) {
_handleChange();
widget.onChange();
},
onTap: () {
setState(() {
_showAutocompleteSuggestions = true;
});
},
),
const SizedBox(
height: 5.0,
),
// THE AUTOCOMPLETE RESULTS
if (_showAutocompleteSuggestions)
Container(
// height: _autocompleteHeight,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: const [
BoxShadow(blurRadius: 10.0, color: Colors.black12)
],
color: Colors.white,
),
constraints: const BoxConstraints(maxHeight: 200.0),
child: Scrollbar(
child: SingleChildScrollView(
child: Column(children: [
if (_autocompleteSuggestions.isEmpty)
const ListTile(
title: Text('No results'),
)
else
..._autocompleteSuggestions.map((_autocompleteSuggestion) =>
Material(
child: InkWell(
onTap: () {
_handleSelectSuggestion(_autocompleteSuggestion);
},
child: ListTile(
leading: const Icon(Icons.location_on_outlined),
title: Text(_autocompleteSuggestion.description),
),
),
))
]),
),
),
),
],
);
}

请原谅快速的代码转储。

您应该在屏幕上使用SingleChildScrollView,其中RawAutocomplete使用reverse:true属性放置。

就像下面一样:

child: Center(
child: SingleChildScrollView(
reverse: true,
child: Column()

您可以使用一些约束来实现您想要的行为。

首先,将根小部件作为LayoutBuilder的子部件放置,以获得布局约束(我还使用了Align top来更好地放置选项视图)。

之后,可以使用ConstrainedBox作为选项视图的父级。

您可以根据需要自定义这些约束。下面的示例设置为将半屏幕高度设置为选项视图的最大高度减去底部视图插图(动态设置为软键盘的状态)。

您在示例中给出的代码如下:

@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.topLeft,
child: LayoutBuilder(
builder: (context, constraints) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
),
child: RawAutocomplete<String>(
key: _autocompleteKey,
focusNode: _focusNode,
textEditingController: _textEditingController,
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return _autoSuggestions;
}
return _autoSuggestions.where((dynamic option) {
return option
.toString()
.toLowerCase()
.startsWith(textEditingValue.text.toLowerCase());
});
},
optionsViewBuilder: (BuildContext context,
AutocompleteOnSelected<String> onSelected,
Iterable<String> options) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16),
child: Material(
elevation: 4.0,
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: constraints.biggest.width,
maxHeight: (MediaQuery.of(context).size.height / 2) -
(MediaQuery.of(context).viewInsets.bottom / 4),
),
child: ListView(
children: options
.map((String option) => GestureDetector(
onTap: () {
onSelected(option);
},
child: ListTile(
title: Text(option),
),
))
.toList(),
),
),
),
);
},
fieldViewBuilder: (
BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onSubmitted,
) {
return Card(
elevation: (null == _errorText ? 8 : 0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0)),
child: TextField(
controller: textEditingController,
focusNode: focusNode,
),
);
},
),
),
),
);
}

最新更新