我正在尝试实现自动完成字段,但是泛型有一点问题。这是我的实现:

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/

10-11 04:17