[Flutter]Algoliaを使ってRitsBookにメルカリ風の全文検索機能をつけてみた

<h1>はじめに</h1>
始めましてwatnowに所属してますしゅんやです。<br/>
今日はRitsbookにAlgoliaを用いた全文検索機能をメルカリ風に実装していきたいと思います！！
<br/>楽しみですね！:raised_hands:

<h1>RitsBookとは</h1>

[RitsBook](https://ritsbook.netlify.app/)<br/>

立命館大学BKCキャンパス専用の教科書フリマアプリです。元々、所属している学生団体の先輩が作っていたものなのですが、現在はその方含め3人（エンジニア2名、デザイナー1名）でモバイル版の開発を行っています。
元々WEB版には完全一致検索はあったのですが、今回は全文検索機能を実装していきたいと思います。
<h1>Algoliaの連携</h1>
元々連携の方法について記事を書こうと思っていたのですが、すでにとてもわかりやすくまとめられていた方がいたので是非　

[こちらの記事](https://www.kamo-it.org/blog/algolia_search/)
をご覧になってください。

連携の流れとしては、
Algoliaのアカウント登録、設定
↓
Firebaseとの連携
↓
FirestoreのデータをAlgoliaに連携
↓
Flutterに導入

といった流れになっています。
<h1>検索フォームの実装</h1>
それでは検索フォームを作っていきます。<br/>
今回はメルカリの検索フォームのようにしていきたいと思っております。
メルカリは商品一覧ページのAppBarに検索窓がありそこから検索するUIかと思いきや、実はボタンでそこから検索ページに遷移する形で実装されていたのでその通り実装していこうと思います。
<br/>
<h2>商品一覧画面</h2>

![101006.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303922/f035e7e8-0f61-bd3e-0f27-85727a0a041b.jpeg)

<h2>検索画面</h2>

![101008.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303922/3388ce12-a68e-e497-01ba-0005edec4226.jpeg)


まずホームの画面（商品の一覧が載っている画面に検索ページに遷移するボタン）をつけていきたいと思います

```LandingPage.dart
import 'package:flutter/material.dart';
//クラス名などは省略
 @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Padding(
          padding: const EdgeInsets.all(8.0),
          child: SizedBox(
            child: ElevatedButton(
              onPressed: () {
                Navigator.push(
                    context, MaterialPageRoute(builder: (context) => Search()));
              },
              style: ElevatedButton.styleFrom(
                backgroundColor: const Color(0xfff5f5f5),
              ),
              child: const Align(
                alignment: Alignment.centerLeft,
                child: Text(
                  '検索',
                  style: TextStyle(color: Color(0xff8f8f8f), fontSize: 12),
                ),
              ),
            ),
          ),
        ),
        backgroundColor: Colors.white,
      ),
      //以下省略
```
これで検索フォームが完成しました。
![Simulator Screen Shot - iPhone 14 Pro Max - 2022-11-30 at 14.39.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303922/4984f35b-c0f2-687c-85c3-db5dbec55318.png)


<h1>検索画面の実装</h1>
とりあえずUIは作らず、タイトルのみを表示してみました。続きはまた投稿する予定です。

```SearchPage.dart
import 'dart:async';
import 'package:algolia/algolia.dart';
import 'package:flutter/material.dart';

class Application {
  static final Algolia algolia = Algolia.init(
    applicationId: 'Algoliaに載ってているapplicationId',
    apiKey: 'Algoliaに載っているapiKey',
  );
}

class Search extends StatefulWidget {
  @override
  _SearchState createState() => _SearchState();
}

class _SearchState extends State<Search> {
  StreamController searchController = StreamController();

  Future searchFireStore(word) async {
    Algolia algolia = Application.algolia;
    AlgoliaQuery query = algolia.instance.index("ItemName").query(word);
    AlgoliaQuerySnapshot snap = await query.getObjects();
    List<AlgoliaObjectSnapshot> hits = snap.hits;
    searchController.add(hits);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: SearchItems(),
        backgroundColor: Colors.white,
      ),
      body: Column(
        children: <Widget>[
            Expanded(
              child: StreamBuilder(
                stream: searchController.stream,
                builder: (context,AsyncSnapshot snapshot) {
                  if (snapshot.hasData) {
                    return ListView.builder(
                      itemCount: snapshot.data.length,
                      itemBuilder: (context, index) {
                        return Text((snapshot.data[index].data['ItemName']);
                      },
                    );
                  } else {
                    return Center(
                      child: Text("No Data"),
                    );
                  }
                
                },
              ),
            )
        ],
      ),
    );
  }
}

class SearchItems extends StatefulWidget {
  @override
  _SearchItemsState createState() => _SearchItemsState();
}

class _SearchItemsState extends State<SearchItems> {
  String searchWord = "";
  @override
  Widget build(BuildContext context) {
    return TextField(
      decoration: const InputDecoration(
        hintText: '検索',
      ),
      controller: TextEditingController(text: searchWord),
      onChanged: (String text) {
        searchWord = text;
      },
      onSubmitted: (searchWord) => _SearchState().searchFireStore(searchWord),
    );
  }
}

```
<h2>商品一覧画面</h2>

![Simulator Screen Shot - iPhone 14 Pro Max - 2022-11-30 at 14.39.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303922/b436fe1b-c12e-861a-60ec-59c0d81c0248.png)

<h2>検索画面</h2>


![Simulator Screen Shot - iPhone 14 Pro Max - 2022-12-01 at 02.09.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303922/f2f6fd69-6d43-5b46-5ff1-d68f9edc7d41.png)



<h1>感想</h1>
今回はAlogoliaを使ってRitsBookにリアルタイム検索機能を実装していきました。Algoliaを使えば思っていたより簡単に実装できたのでびっくりです。個人的にStreamでのデータの取得に一番苦戦しました。まだまだ理解が浅かったみたいです。RitsBookはWEBアプリ版とはデザインも一新されていて、検索画面のUIも整えてリリースするので完成されたRitsBookを是非見てみてください！
それでは良いFlutterライフを！:blush: