我有同样的问题并通过使用ValueListenableBuilder
.
小部件的宽度Material
取决于返回的小部件的宽度field ViewBuilder
(默认为 a TextFormField
),因此一种解决方案是TextFormField
在一帧完成后获取宽度并通知您的自定义optionsViewBuilder
示例代码:
import 'package:flutter/material.dart';
class AutocompleteText extends StatefulWidget {
@override
State<StatefulWidget> createState() => AutocompleteTextState();
}
class AutocompleteTextState extends State<AutocompleteText> {
final ValueNotifier<double?> optionsViewWidthNotifier = ValueNotifier(null);
static const List<User> _userOptions = <User>[
User(name: 'Alice', email: 'alice@example.com'),
User(name: 'Bob', email: 'bob@example.com'),
User(name: 'Charlie', email: 'charlie123@gmail.com'),
];
static String _displayStringForOption(User option) => option.name;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(20),
child: Autocomplete<User>(
displayStringForOption: _displayStringForOption,
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<User>.empty();
}
return _userOptions.where((User option) {
return option
.toString()
.contains(textEditingValue.text.toLowerCase());
});
},
onSelected: (User selection) {
print('You just selected ${_displayStringForOption(selection)}');
},
fieldViewBuilder: (BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onFieldSubmitted) {
return OrientationBuilder(builder: (context, orientation) {
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
optionsViewWidthNotifier.value =
(context.findRenderObject() as RenderBox).size.width;
});
return TextFormField(
controller: textEditingController,
focusNode: focusNode,
onFieldSubmitted: (String value) {
onFieldSubmitted();
},
);
});
},
optionsViewBuilder: (
BuildContext context,
AutocompleteOnSelected<User> onSelected,
Iterable<User> options,
) {
return ValueListenableBuilder<double?>(
valueListenable: optionsViewWidthNotifier,
builder: (context, width, _child) {
return Align(
alignment: Alignment.topLeft,
child: Material(
elevation: 4.0,
child: SizedBox(
width: width,
height: 200.0,
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: options.length,
itemBuilder: (BuildContext context, int index) {
final User option = options.elementAt(index);
return InkWell(
onTap: () {
onSelected(option);
},
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(_displayStringForOption(option)),
),
);
},
),
),
),
);
});
},
),
));
}
@override
void dispose() {
optionsViewWidthNotifier.dispose();
super.dispose();
}
}
@immutable
class User {
const User({
required this.email,
required this.name,
});
final String email;
final String name;
@override
String toString() {
return '$name, $email';
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) {
return false;
}
return other is User && other.name == name && other.email == email;
}
@override
int get hashCode => hashValues(email, name);
}