Flutter 搜尋建議欄

Borderland_發表於2020-05-08

前言

在平時各種各樣的軟體中都會有搜尋欄方便各位進行資訊的搜尋,下面就用flutter擼一個搜尋欄建議。

實現效果

Flutter 搜尋建議欄
有內容的時候顯示建議內容以及刪除icon,點選icon刪除內容並且移除建議內容。

主要原始碼

  • 構建TextField

這裡關鍵點主要還是key,主要用於獲取當前TextFiled的位置。

GlobalKey textFieldKey;
TextEditingController _searchController = TextEditingController();
FocusNode _searchFocus = FocusNode();

TextField(
          key: textFieldKey,
          controller: _searchController,
          focusNode: _searchFocus,
          cursorColor: Colors.black,
          decoration: InputDecoration(
              suffixIcon: !_searchFocus.hasFocus
                  ? null
                  : _searchController.text.isEmpty
                      ? null
                      : IconButton(
                          icon: Icon(
                            Icons.close,
                            color: Colors.black,
                          ),
                          onPressed: () {},
                        ),
              focusedBorder: UnderlineInputBorder(
                  borderSide: BorderSide(color: Colors.black)))
        )
複製程式碼
  • 構建Overlay

findOverLayPosition是根據key來獲取當前TextFiled的位置,Positioned中的位置可以根據自己的需求進行調整,高度在這裡我是根據獲取到的內容條數來進行設定的。

void findOverLayPosition() {
    RenderBox renderBox = textFieldKey.currentContext.findRenderObject();
    height = renderBox.size.height;
    width = renderBox.size.width;
    Offset offset = renderBox.localToGlobal(Offset.zero);
    xPosition = offset.dx;
    yPosition = offset.dy;
}
  
  // 生成搜尋建議
OverlayEntry _buildSearchSuggest() {
    return OverlayEntry(builder: (context) {
      return Positioned(
        left: xPosition - 40.0,
        width: width + 40.0,
        top: yPosition + height + 10,
        height: _keywords.length * height,
        child: Material(
          child: Container(
              height: _keywords.length * height,
              decoration: BoxDecoration(color: Colors.white, boxShadow: [
                BoxShadow(
                  color: Colors.black12,
                  blurRadius: 5.0,
                ),
              ]),
              child: ListView.builder(
                  padding: EdgeInsets.zero,
                  itemBuilder: (context, index) {
                    return InkWell(
                        onTap: () {
                          searchSuggest.remove();
                          searchSuggest = null;
                          search2detail(_keywords[index]);
                        },
                        child: SearchItem(
                          text: _keywords[index],
                          itemHeight: height,
                          isFirstItem: index == 0 ? true : false,
                          isLastItem:
                              index == _keywords.length - 1 ? true : false,
                        ));
                  },
                  itemCount: _keywords.length)),
        ),
      );
    });
  }
  
複製程式碼
  • 插入Overlay

在這裡我是在TextField的ontap方法中獲取TextField位置資訊的,然後當TextField內容發生變化的時候就插入Overlay,在onSubmitted的時候把Overlay給移除。

OverlayEntry searchSuggest;
    
onTap: () {
    findDropdownPosition();
    setState(() {});
    },
onChanged: (value) async {
    if (value.isNotEmpty) {
        if (searchSuggest != null) removeOverlay();

        searchSuggest = _buildSearchSuggest();
        OverlayState overlayState = Overlay.of(context);
        overlayState.insert(searchSuggest);
    else {
        if (searchSuggest != null) removeOverlay();
        }
        setState(() {});
    },
onSubmitted: (value) {
        searchSuggest.remove();
        searchSuggest = null;
         }
複製程式碼

最後

在實現點選刪除按鈕把TextFiled內容清除的時候出現了個問題

invalid text selection: TextSelection(baseOffset: 2, extentOffset: 2, affinity:TextAffinity.upstream, isDirectional: false)

解決方法: 只需要新增小延遲就好了,具體可以看這個issue

Future.delayed(Duration(microseconds: 20), () {
    _searchController.clear();
    _searchFocus.unfocus();
    if (searchSuggest != null) removeOverlay();
});
複製程式碼

詳細內容可以檢視這裡 程式碼地址

相關文章