Flutter搜索功能插件searchbase的使用

Flutter搜索功能插件searchbase的使用

searchbase 是一个轻量级且与平台无关的库,提供了创建由Elasticsearch驱动的搜索体验所需的框架。本文将介绍如何在Flutter中使用 searchbase 插件来实现搜索功能,并提供完整的示例代码。

简单示例

以下示例创建了一个简单的搜索小部件,根据输入值渲染结果。

Dart代码

import 'dart:html';
import 'package:searchbase/searchbase.dart';

void main() {
  final index = 'gitxplore-app';
  final url = 'https://@appbase-demo-ansible-abxiydt-arc.searchbase.io';
  final credentials = 'a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61';

  // 实例化 SearchController
  final searchController = SearchController(
      index, // Elasticsearch索引名称
      url,   // Appbase URL
      credentials, // Appbase凭据
      'search-widget', // 搜索小部件唯一标识符
      dataField: ['name', 'description', 'name.search', 'fullname', 'owner', 'topics'],
      value: ''
  );

  // 获取输入元素
  final searchElement = querySelector('#search');

  // 绑定 searchController 值到输入值
  searchElement.value = searchController.value;

  // 更新搜索输入值以触发查询
  searchElement.addEventListener('input', (e) {
    searchController.setValue(e.target.value,
        options: Options(triggerDefaultQuery: true));
  });

  // 当搜索结果更新时构建DOM
  searchController.subscribeToStateChanges((change) {
    final results = change.Results!.next;
    final resultsElement = querySelector('#results');
    resultsElement.innerHTML = '';
    results.data.forEach((element) {
      var node = document.createElement('li'); // 创建 <li> 节点
      var resultNode = document.createTextNode(element.name); // 创建文本节点
      node.append(resultNode); // 将文本附加到 <li>
      resultsElement.append(node);
    });
  }, [KeysToSubscribe.Results]);

  // 在初始加载时获取默认结果
  searchController.triggerDefaultQuery();
}

HTML代码

<input placeholder="type to search" id="search" />
<div id="results"></div>

使用Facet的示例

以下示例创建了三个小部件:一个用于执行搜索的小部件、一个用于过滤GitHub仓库的语言过滤器小部件和一个用于渲染结果的小部件。

Dart代码

import 'dart:html';
import 'package:searchbase/searchbase.dart';

void main() {
  final index = 'gitxplore-app';
  final url = 'https://@appbase-demo-ansible-abxiydt-arc.searchbase.io';
  final credentials = 'a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61';

  final searchbase = SearchBase(index, url, credentials,
      appbaseConfig: AppbaseSettings(recordAnalytics: true));

  // 注册搜索小部件
  final searchController = searchbase.register('search-widget', {
    'enablePopularSuggestions': true,
    'dataField': [
      'name',
      'description',
      'name.raw',
      'fullname',
      'owner',
      'topics'
    ]
  });

  // 注册语言过滤器小部件
  final filterWidget = searchbase.register('language-filter', {
    'type': QueryType.term,
    'dataField': 'language.keyword',
    'react': {'and': 'search-widget'},
    'value': List<String>()
  });

  // 注册结果小部件
  final resultWidget = searchbase.register('result-widget', {
    'dataField': 'name',
    'react': {
      'and': ['search-widget', 'language-filter']
    },
  });

  // 渲染结果
  querySelector('#output').innerHtml = """
    <div id="root">
      <h2 class="text-center">Searchbase Demo with Facet</h2>
      <div id="autocomplete" class="autocomplete">
        <input class="autocomplete-input" id="input" />
        <ul class="autocomplete-result-list"></ul>
      </div>
      <div class="row">
        <div class="col">
          <div class="filter" id="language-filter"></div>
        </div>
        <div class="col">
          <div id="results">
            <div class="loading">Loading results... </div>
          </div>
        </div>
      </div>
    </div>
  """;

  final input = querySelector('#input');
  void handleInput(e) {
    searchController.setValue(e.target.value,
        options: Options(triggerDefaultQuery: true));
  }

  input.addEventListener('input', handleInput);

  void handleKeyPress(e) {
    if (e.key == 'Enter') {
      e.preventDefault();
      searchController.triggerCustomQuery();
    }
  }

  input.addEventListener('keydown', handleKeyPress);

  final resultElement = querySelector('#results');

  // 获取初始结果
  resultWidget.triggerDefaultQuery();

  // 订阅结果变化并更新结果列表
  resultWidget.subscribeToStateChanges((change) {
    final results = change.Results!.next;
    final items = results.data?.map((i) {
      return """
    <div id=${i['_id']} class="result-set">
      <div class="image">
        <img src=${i['avatar']} alt=${i['name']} />
      </div>
      <div class="details">
        <h4>${i['name']}</h4>
        <p>${i['description']}</p>
      </div>
    </div>""";
    });
    final resultStats = """<p class="results-stats">
                          Showing ${results.numberOfResults} in ${results.time}ms
                        <p>""";

    resultElement.setInnerHtml("${resultStats}${items.join('')}",
        validator: new NodeValidatorBuilder.common()
          ..allowHtml5()
          ..allowElement('img',
              attributes: ['src'], uriPolicy: new DefaultUriPolicy()));
  }, [KeysToSubscribe.Results]);

  // 获取初始过滤选项
  filterWidget.triggerDefaultQuery();

  // 订阅过滤选项的变化
  filterWidget.subscribeToStateChanges((change) {
    final aggregations = change.AggregationData!.next;
    final container = document.getElementById('language-filter');
    container.setInnerHtml('');
    aggregations.data.forEach((i) {
      if (i['_key'] != null) {
        final checkbox = document.createElement('input');
        checkbox.setAttribute('type', 'checkbox');
        checkbox.setAttribute('name', i['_key']);
        checkbox.setAttribute('value', i['_key']);
        checkbox.id = i['_key'];
        checkbox.addEventListener('click', (event) {
          final List<String> values =
              filterWidget.value != null ? filterWidget.value : [];
          if (values.contains(i['_key'])) {
            values.remove(i['_key']);
          } else {
            values.add(i['_key']);
          }
          filterWidget.setValue(values,
              options: Options(stateChanges: true, triggerCustomQuery: true));
        });
        final label = document.createElement('label');
        label.setAttribute('htmlFor', 'i._key');
        label.setInnerHtml("${i['_key']}(${i['_doc_count']})");
        final div = document.createElement('div');
        div.append(checkbox);
        div.append(label);
        container.append(div);
      }
    });
  }, [KeysToSubscribe.AggregationData]);

  searchController.subscribeToStateChanges((change) {
    print('Track State Updates');
    print(change);
  }, [KeysToSubscribe.Results]);
}

class DefaultUriPolicy implements UriPolicy {
  DefaultUriPolicy();
  bool allowsUri(String uri) {
    return true;
  }
}

通过以上示例,您可以轻松地在Flutter应用中集成 searchbase 插件来实现强大的搜索功能。


更多关于Flutter搜索功能插件searchbase的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter搜索功能插件searchbase的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用SearchBase插件来实现搜索功能的代码示例。SearchBase是一个专为Flutter设计的强大搜索解决方案,它能够与Firebase Firestore、Elasticsearch和Algolia等后端服务集成。

在这个示例中,我将展示如何配置SearchBase并使用它与Firebase Firestore进行基本的搜索操作。

1. 添加依赖

首先,你需要在pubspec.yaml文件中添加SearchBase的依赖:

dependencies:
  flutter:
    sdk: flutter
  searchbase: ^latest_version  # 请替换为实际的最新版本号
  firebase_firestore: ^latest_version  # 请替换为实际的最新版本号

2. 配置Firebase Firestore

确保你已经在Firebase项目中启用了Firestore数据库,并获取了配置信息。然后在你的Flutter项目中配置Firebase:

// 在你的main.dart或合适的位置
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_firestore/firebase_firestore.dart';
import 'package:flutter/material.dart';
import 'package:searchbase/searchbase.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

3. 使用SearchBase进行搜索

下面是一个完整的示例,展示了如何使用SearchBase与Firebase Firestore进行搜索:

import 'package:flutter/material.dart';
import 'package:firebase_firestore/firebase_firestore.dart';
import 'package:searchbase/searchbase.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SearchScreen(),
    );
  }
}

class SearchScreen extends StatefulWidget {
  @override
  _SearchScreenState createState() => _SearchScreenState();
}

class _SearchScreenState extends State<SearchScreen> {
  final SearchBaseController searchBaseController = SearchBaseController();
  late CollectionReference<Map<String, dynamic>> firestoreCollection;

  @override
  void initState() {
    super.initState();
    
    // 初始化Firestore集合
    firestoreCollection = FirebaseFirestore.instance.collection('your_collection_name');

    // 配置SearchBase
    searchBaseController.setDataSource({
      'firestore': {
        'collectionPath': 'your_collection_name',
        'firestore': FirebaseFirestore.instance,
      },
    });

    // 监听搜索变化
    searchBaseController.addSnapshotListener((snapshot) {
      // 处理搜索结果
      print(snapshot.docs);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SearchBase Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            TextField(
              decoration: InputDecoration(
                labelText: 'Search...',
                suffixIcon: IconButton(
                  icon: Icon(Icons.search),
                  onPressed: () {
                    // 触发搜索
                    searchBaseController.query(
                      search: TextField.focusedTextContext(context),
                    );
                  },
                ),
              ),
              onChanged: (value) {
                // 实时搜索
                searchBaseController.query(search: value);
              },
            ),
            SizedBox(height: 16),
            Expanded(
              child: searchBaseController.hasResults
                  ? ListView.builder(
                      itemCount: searchBaseController.snapshot?.docs?.length ?? 0,
                      itemBuilder: (context, index) {
                        Map<String, dynamic> doc =
                            searchBaseController.snapshot?.docs?[index].data() as Map<String, dynamic>;
                        return ListTile(
                          title: Text(doc['name'] ?? ''), // 假设你的文档有一个'name'字段
                        );
                      })
                  : Center(child: Text('No Results')),
            ),
          ],
        ),
      ),
    );
  }
}

注意事项

  1. 配置Firestore规则:确保你的Firestore安全规则允许你进行搜索操作。
  2. 字段映射:根据你的数据模型调整字段映射。
  3. 错误处理:添加适当的错误处理逻辑,以处理网络问题或数据解析错误。

这个示例展示了如何使用SearchBase与Firebase Firestore进行基本的搜索操作。根据你的需求,你可以进一步自定义和扩展这个示例。

回到顶部