【Flutter/Dart開発】twitterのような#ハッシュタグ検索をしてみたい話(実践編)
こんにちは、エメラルドどハマり中おじさん
たちつてとです。
過去記事で
【Flutter/Dart開発】twitterのような#ハッシュタグ検索をしてみたい話(理屈編)
表題通りタグ検索の作るためのロジックを
考えてみたんですが。
実際に作ったで。という話を忘れてしまいそうで
悲しかったので記事にする次第です。
基本的に作戦は過去記事にある(はず...)。
大前提
以下にはソースを書いていくんですが
流石に全てを載せても仕方がないので
箇条書きで前提を書きます。
- 雑学等を保存するメモアプリが主役
- メモアプリにはオリジナルクラス「Memo」がある
- 「Memo」には「#タグ」に相当するプロパティがある
- 「#タグ」プロパティには、半角スペースを境に複数のタグを格納している
こんな感じです。
順に補足
メモアプリにはオリジナルクラス「Memo」がある
「Memo」クラスには、「#タグ」プロパティの他に
「タイトル」・「メモ内容」というものがあります。
「Memo」には「#タグ」に相当するプロパティがある
過去記事での話では、
「#タグ」プロパティをわざわざ用意する必要があるか
ということに、簡単に触れました。
→まとめると、めんどくさいので用意しました。
これ以降出番はありませんが、
入力画面は以下の画像です。
画像のようにタグを登録するテキスト欄を用意しています。
「#タグ」プロパティには、半角スペースを境に複数のタグを格納している
りんご バナナ もも...
こんな感じで、単語(半角スペース)単語(半角スペース)...
という保存形式です。
プロパティをタグ1、タグ2...と
それぞれに一個ずつ保存する方法も考えましたが、
参考もとのtwitterはタグ同士を空欄で区別してましたから
これから着想し実装しました。
実際の登録形式とかは知らないです。
実際のコード
以下、方針と実際のソースコードになります。
作戦概要
- データからタグをList型に格納
- 重複があるので、重複を除く
- 格納したタグをfor文で、1つ1つ配置
登録メモデータからタグを取得する
登録してあるデータからタグをList型に格納。
基本的にはコメントしてある通りです。
「Hive〜」という記述は、
ストレージからデータを取得するための文なので
適宜、参考/無視でお願いします。
- メモループを回す
- ループ内で空白付きのタグ(=tag1というプロパティ)を分解
- さらに1つ1つのタグをListに格納
List<Category> _cates = [];// オリジナルクラス Category 重要ではない
List<Memo> _memos = [];// オリジナルクラス Memo(の中のtagがやや)大事
List<String> _tags = [];// タグ格納用 やや大事
Hive.openBox<Memo>(_memo).then((value) {// 保存データの読み込み
Box<Memo> mesBox = value;
_memos.clear();
_tags.clear();
_cates.clear();
for (Memo memo in mesBox.values) {// 保存データを(検索条件で)表示分add
if (memo.categoryCd == _categoryCd) {
if (new RegExp(r'^.*' + _contTitle.text + '.*').hasMatch(memo.title) &&
new RegExp(r'^.*' + _contMemo.text + '.*').hasMatch(memo.memo)) {
_memos.add(memo);
}
}
for (String tag in memo.tag1.split(' ')) {// ※保存データを分解し格納
_tags.add(tag);// 保存データ全てからタグをadd※※
}
_tags = _tags.toSet().toList();// ※※タグダブりがあるのでSet型にして解消
}
});
やや細かい補足
最後のtoSet.toList()という記述。
一度、Set型という型に変換しています。
Set型というのは、値の重複が許されないList型と思えばよくて、
この変換で、重複をのぞいてくれます。
なお、重複が発生する理由を具体的に述べると、
全データを1回目のループで回すので
1回目:りんご
2回目:りんご
3回目:バナナ
とかってあると、
Listの中に「りんご」タグが2個格納
→表示も2個表示
となってしまいます。
下手にロジック組むのも面倒だったので、
全データでループかけるのはそのままで
後からtoSetで重複を取る方針にしました。
取得したタグを画面に表示する
上述した通り、for文で取得したタグを配置しています。
Widget build(BuildContext context) {
if (asyncWidget != null) {
return Scaffold(
backgroundColor: Color.fromRGBO(240, 248, 255, 1.0),
persistentFooterButtons: <Widget>[// 画面下部に固定表示
Column(children: <Widget>[
SingleChildScrollView(
scrollDirection: Axis.horizontal, // 横スクロール仕込み
child: Row(children: [
for (String tag in _tags) ...{// タグ1つ1つを画面に横並べ
if(tag.trim() != '')...{
Container(
height: 90,
child: TextButton(
onPressed: () {// タグをタップで検索
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return MemoList(tag: tag);// 検索
}));
},
child: Card(
child: Container(
//height: 50,
padding: EdgeInsets.all(10),
child: Text(
'#' + tag,// 見た目のために#をつける
style: TextStyle(
fontSize: 17,
color: Colors.blueAccent,
decoration: TextDecoration.underline,
)),)// 見た目のために下線とかをつける
),
),
),
}
},
]),
),
])
],
);
}
return Scaffold(//気にしないでOK
appBar: AppBar(
title: const Text('loading...'),
));
}
この時点で、タグデータの取得ができているので
あとは簡単...つっても横並びに表示させるので
ちょっと時間取られたんですが...
大事なのは2点で、
- タグ表示エリアをpersistentFootterButtonsで画面下部に固定している
- SingleScrollViewで横スクロールを実装している
- for文で横並びにタグを設置している
これくらいだと思います。
あとは、タップイベントに何を仕込むかとか
見た目をどうするかという問題だけです。
僕は、タップイベントには
検索機能(まあ、普通ですよね)を入れてます。
検索機能はまた別の話なので割愛します。
見た目をどうするかについては、
まあ、それっぽくしました。
twitterリスペクトってことで
「#」+登録タグ名って感じで表示してます。
以上となります。
この実装は結構楽しかった。
ソースは役立たないかもしれませんが
考え方などなんかの参考になれば幸いです。
Flutter モバイルアプリ開発バイブル |