我正在尝试实现自动完成字段,但是泛型有一点问题。这是我的实现:
import 'package:flutter/material.dart';
import 'package:kiwi_mobile/common/presentation/loading.dart';
import 'package:rxdart/rxdart.dart';
typedef AutocompleteDelegate<T> = Future<List<T>> Function(String query);
class AutocompleteField<T> extends StatefulWidget {
final InputDecoration decoration;
final FocusNode focusNode;
final bool autofocus;
final int maxLines;
final int itemExtent;
final TextInputType keyboardType;
final TextEditingController controller;
final Widget Function(BuildContext context, T entry) itemBuilder;//Here dynamic instead of T as T doesn't work for some reason....
final AutocompleteDelegate<T> delegate;
final Function(T entry) onItemSelected;//Here dynamic instead of T as T doesn't work for some reason....
const AutocompleteField(
{Key key, this.itemExtent, this.keyboardType, this.maxLines = 1, this.autofocus, this.controller, @required this.onItemSelected, @required this.itemBuilder, @required this.delegate, this.focusNode, this.decoration})
: super(key: key);
@override
_AutocompleteFieldState<T> createState() => _AutocompleteFieldState<T>();
}
class _AutocompleteFieldState<T> extends State<AutocompleteField> {
FocusNode _focusNode;
TextEditingController _controller;
AutocompleteBloc _bloc;
OverlayEntry _overlayEntry;
final LayerLink _layerLink = LayerLink();
@override
void initState() {
_controller = widget.controller ?? TextEditingController(text: '');
_bloc = AutocompleteBloc<T>(widget.delegate);
_bloc.query.add(_controller.text);
_focusNode = widget.focusNode ?? FocusNode();
_focusNode.addListener(() {
if (_focusNode.hasFocus) {
if (_controller.text.length >= 3) {
_showOverlay();
}
} else {
_hideOverlay();
}
});
super.initState();
}
void _showOverlay() {
if (_overlayEntry != null) {
_hideOverlay();
}
_overlayEntry = _createOverlayEntry();
Overlay.of(context).insert(_overlayEntry);
}
void _hideOverlay() {
if (_overlayEntry != null) {
_overlayEntry.remove();
_overlayEntry = null;
}
}
OverlayEntry _createOverlayEntry() {
// ignore: avoid_as
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
return OverlayEntry(
builder: (context) => Positioned(
width: size.width,
child: CompositedTransformFollower(
link: _layerLink,
showWhenUnlinked: false,
offset: Offset(0.0, size.height + 5.0),
child: Material(
elevation: 4.0,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: (widget.itemExtent ?? size.height) * 3),
child: StreamBuilder<List<T>>(
stream: _bloc.results,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: Loading());
}
return Scrollbar(
child: ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemBuilder: (context, index) {
final data = snapshot.data + snapshot.data;
final entry = data[index];
return InkWell(
child: widget.itemBuilder(context, entry),
onTap: () {
widget.onItemSelected(entry);
_hideOverlay();
},
);
},
itemCount: snapshot.data.length * 2,
),
);
},
),
),
),
),
),
);
}
@override
Widget build(BuildContext context) {
return CompositedTransformTarget(
link: _layerLink,
child: TextField(
focusNode: _focusNode,
maxLines: widget.maxLines,
autofocus: widget.autofocus,
keyboardType: widget.keyboardType,
controller: _controller,
onChanged: (value) {
if (value.length >= 3) {
_bloc.query.add(value);
if (_overlayEntry == null) {
_showOverlay();
}
} else {
_hideOverlay();
}
},
decoration: widget.decoration,
),
);
}
@override
void dispose() {
_bloc.dispose();
super.dispose();
}
}
class AutocompleteBloc<T> {
final AutocompleteDelegate<T> delegate;
final _query = BehaviorSubject<String>();
final _results = BehaviorSubject<List<T>>();
AutocompleteBloc(this.delegate) {
_query.distinct().debounceTime(Duration(milliseconds: 500)).listen(_search);
}
void _search(String query) async {
final results = await delegate(query);
_results.add(results);
}
Sink<String> get query => _query.sink;
Stream<List<T>> get results => _results.stream;
Future<void> dispose() {
return _query.close();
}
}
我正在这样使用它:
Padding(
padding: const EdgeInsets.all(8.0),
child: AutocompleteField<String>(
delegate: (query) async {
return ['Test', 'Test2', 'Test3'];
},
itemBuilder: (context, entry) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(entry),
);
},
onItemSelected: (entry) {
print(entry);
},
),
),
问题是可以编译,但是在运行时使用
type '(BuildContext, String) => Padding' is not a subtype of type '(BuildContext, dynamic) => Widget
崩溃通过使用动态而不是通用,它可以工作,但应该与通用一起工作,我找不到我做错的事情。
要复制使用此要点https://gist.github.com/jaumard/13681f287314e16e46942e57b520d15e
最佳答案
更改此行:
class _AutocompleteFieldState<T> extends State<AutocompleteField> {
至
class _AutocompleteFieldState<T> extends State<AutocompleteField<T>> {
也许您的AutocompleteBloc处理必须调用_results.dispose
关于generics - 通用 Dart : '(BuildContext, String) => Padding'类型不是 '(BuildContext, dynamic) => Widget'类型的子类型,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59153433/