Flutter SearchViewとListViewのチュートリアル例です。
この作品では、最高で最も簡単なFlutter SearchViewの例を見てみたいと思います。
例1 - カードの検索フィルターリストビュー
flutterでサーチビューを使って、リストビューを検索/フィルタリングする方法を見てみましょう。いわゆるサーチビューとは、実際にはカスタムテキストフィールドのことです。TextEditingControllerを使って、テキストフィールドのテキストが変更されたことを通知します。これで簡単にデータの検索ができるようになります。
このデータは実際には単純なリストから束ねられています。実際には、2つのリストを管理します。
- 最初のリスト - 実際のデータソースです。これは決して変更されません。
- 2.第2リスト - フィルターの結果を保持します。例えば、検索して100件のリストから5件の結果が出た場合、その5件の結果をこの第2リストに保持します。
検索自体はとても簡単です。単にstring
クラスのcontains()
メソッドを使うだけです。ただし、大文字と小文字の一貫性を確保しなければなりません。たとえば、クエリが小文字の場合は、データも小文字にしなければなりません。
デモ
このプロジェクトのデモをご紹介します。
ビデオチュートリアル
ビデオチュートリアルをご希望の方は、こちらのYouTubeチャンネルでご覧いただけます。
(a). pubspec.yaml
ここでは、サードパーティのライブラリは使用しません。
代わりに、アプリの名前と説明を記述します。
name: mr_searchview
description: Flutter SearchView example project.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
dev_dependencies:
flutter_test:
sdk: flutter
(b). main.dart
ここでは、完全な main.dart
コードを紹介します。
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
//Our MyApp class. Represents our application
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "ListView SearchView",
home: new Home(),
theme: ThemeData(primaryColor: Colors.orange),
);
}
}
//Represents the Homepage widget
class Home extends StatefulWidget {
//createState()
will create the mutable state for this widget at
//a given location in the tree.
@override
_HomeState createState() => _HomeState();
}
//Our Home state, the logic and internal state for a StatefulWidget.
class _HomeState extends State<Home> {
//A controller for an editable text field.
//Whenever the user modifies a text field with an associated
//TextEditingController, the text field updates value and the
//controller notifies its listeners.
var _searchview = new TextEditingController();
bool _firstSearch = true;
String _query = "";
List<String> _nebulae;
List<String> _filterList;
@override
void initState() {
super.initState();
_nebulae = new List<String>();
_nebulae = [
"Orion",
"Boomerang",
"Cat's Eye",
"Pelican",
"Ghost Head",
"Witch Head",
"Snake",
"Ant",
"Bernad 68",
"Flame",
"Eagle",
"Horse Head",
"Elephant's Trunk",
"Butterfly"
];
_nebulae.sort();
}
_HomeState() {
//Register a closure to be called when the object changes.
_searchview.addListener(() {
if (_searchview.text.isEmpty) {
//Notify the framework that the internal state of this object has changed.
setState(() {
_firstSearch = true;
_query = "";
});
} else {
setState(() {
_firstSearch = false;
_query = _searchview.text;
});
}
});
}
//Build our Home widget
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: new Text("SearchView ListView"),
),
body: new Container(
margin: EdgeInsets.only(left: 10.0, right: 10.0, top: 10.0),
child: new Column(
children: <Widget>[
_createSearchView(),
_firstSearch ? _createListView() : _performSearch()
],
),
),
);
}
//Create a SearchView
Widget _createSearchView() {
return new Container(
decoration: BoxDecoration(border: Border.all(width: 1.0)),
child: new TextField(
controller: _searchview,
decoration: InputDecoration(
hintText: "Search",
hintStyle: new TextStyle(color: Colors.grey[300]),
),
textAlign: TextAlign.center,
),
);
}
//Create a ListView widget
Widget _createListView() {
return new Flexible(
child: new ListView.builder(
itemCount: _nebulae.length,
itemBuilder: (BuildContext context, int index) {
return new Card(
color: Colors.white,
elevation: 5.0,
child: new Container(
margin: EdgeInsets.all(15.0),
child: new Text("${_nebulae[index]}"),
),
);
}),
);
}
//Perform actual search
Widget _performSearch() {
_filterList = new List<String>();
for (int i = 0; i < _nebulae.length; i++) {
var item = _nebulae[i];
if (item.toLowerCase().contains(_query.toLowerCase())) {
_filterList.add(item);
}
}
return _createFilteredListView();
}
//Create the Filtered ListView
Widget _createFilteredListView() {
return new Flexible(
child: new ListView.builder(
itemCount: _filterList.length,
itemBuilder: (BuildContext context, int index) {
return new Card(
color: Colors.white,
elevation: 5.0,
child: new Container(
margin: EdgeInsets.all(15.0),
child: new Text("${_filterList[index]}"),
),
);
}),
);
}
}
ダウンロードして実行する方法です。
このコードを main.dart
ファイルにコピーするだけです。セットアップは必要ありません。
例2 - AppBar/Toolbarからの検索フィルターリストビュー
これは2つ目のflutter searchviewの例です。この例も理解しやすく、Dartプログラミングを使ってflutterに検索フィルターを実装するのに役立ちます。この例では、セットアップや依存関係も必要ありません。この例では、アプリケーションのツールバーやアプリバーにサーチビューをレンダリングします。
それでは始めましょう。
**デモ
ここにデモがあります。
Flutter AppBar SearchView
(a). search_list.dart
このファイルでは、検索可能なリストビューを表現するためのステートフルなウィジェットを定義します。
インポートの追加。
まず、material.dart
をインポートします。
import 'package:flutter/material.dart';
**ステートフル・ウィジェットの作成
次に、StatefulWidgetを拡張して、ウィジェットを作成します。
class SearchList extends StatefulWidget {
を拡張し、コンストラクタを定義してウィジェットを作成します。
SearchList({ Key key }) : super(key: key);
そして、最後にcreateState()
メソッドをオーバーライドします。
@override
_SearchListState createState() => new _SearchListState();
}
**Stateクラスの作成
まず、Stateクラスを拡張します。
class _SearchListState extends State<SearchList>
{
次にインスタンスフィールドを定義します。
Widget appBarTitle = new Text("Search Sample", style: new TextStyle(color: Colors.white),);
Icon actionIcon = new Icon(Icons.search, color: Colors.white,);
final key = new GlobalKey<ScaffoldState>();
final TextEditingController _searchQuery = new TextEditingController();
List<String> _list;
bool _IsSearching;
String _searchText = "";
インスタンスフィールドには、appBarのタイトル、アイコン、TextEditingControllerなどが含まれていることがわかります。
コンストラクタでは、TextEditingControllerにリスナーをアタッチします。これにより、ユーザーの入力による編集フィールドのテキスト変更イベントを通知することができます。
_SearchListState() {
_searchQuery.addListener(() {
if (_searchQuery.text.isEmpty) {
setState(() {
_IsSearching = false;
_searchText = "";
});
}
else {
setState(() {
_IsSearching = true;
_searchText = _searchQuery.text;
});
}
});
}
次に、initState()
メソッドをオーバーライドします。
@override
void initState() {
super.initState();
_IsSearching = false;
init();
}
データソースとして動作するアイテムのリストを追加します。
void init() {
_list = List();
_list.add("Google");
_list.add("IOS");
_list.add("Andorid");
_list.add("Dart");
_list.add("Flutter");
_list.add("Python");
_list.add("React");
_list.add("Xamarin");
_list.add("Kotlin");
_list.add("Java");
_list.add("RxAndroid");
}
このステートクラスのbuild()
メソッドをオーバーライドします。
@override
Widget build(BuildContext context) {
return new Scaffold(
key: key,
appBar: buildBar(context),
body: new ListView(
padding: new EdgeInsets.symmetric(vertical: 8.0),
children: _IsSearching ? _buildSearchList() : _buildList(),
),
);
}
これを見ると、appBarとbodyを指定してウィジェットを構築していることがわかります。この場合、bodyにはListViewが含まれます。
そして、データのリストを返す2つのヘルパー・メソッドがあります。
List<ChildItem> _buildList() {
return _list.map((contact) => new ChildItem(contact)).toList();
}
List<ChildItem> _buildSearchList() {
if (_searchText.isEmpty) {
return _list.map((contact) => new ChildItem(contact))
.toList();
}
else {
List<String> _searchList = List();
for (int i = 0; i < _list.length; i++) {
String name = _list.elementAt(i);
if (name.toLowerCase().contains(_searchText.toLowerCase())) {
_searchList.add(name);
}
}
return _searchList.map((contact) => new ChildItem(contact))
.toList();
}
}
次に、AppBarウィジェットの構築を助けるヘルパー・メソッドがあります。
Widget buildBar(BuildContext context) {
return new AppBar(
centerTitle: true,
title: appBarTitle,
actions: <Widget>[
new IconButton(icon: actionIcon, onPressed: () {
setState(() {
if (this.actionIcon.icon == Icons.search) {
this.actionIcon = new Icon(Icons.close, color: Colors.white,);
this.appBarTitle = new TextField(
controller: _searchQuery,
style: new TextStyle(
color: Colors.white,
),
decoration: new InputDecoration(
prefixIcon: new Icon(Icons.search, color: Colors.white),
hintText: "Search...",
hintStyle: new TextStyle(color: Colors.white)
),
);
_handleSearchStart();
}
else {
_handleSearchEnd();
}
});
},),
]
);
}
上記のメソッドでわかるように、このAppBarウィジェットにはsearchviewが含まれています。
そして最後に、このステートクラスのために、検索開始と検索終了を処理するメソッドを作成します。
void _handleSearchStart() {
setState(() {
_IsSearching = true;
});
}
void _handleSearchEnd() {
setState(() {
this.actionIcon = new Icon(Icons.search, color: Colors.white,);
this.appBarTitle =
new Text("Search Sample", style: new TextStyle(color: Colors.white),);
_IsSearching = false;
_searchQuery.clear();
});
}
}
これでStateクラスは終わりです。
その下には、childItem
というクラスを作ります。これもまた、1つのListViewアイテムを表すウィジェットです。
class ChildItem extends StatelessWidget {
final String name;
ChildItem(this.name);
@override
Widget build(BuildContext context) {
return new ListTile(title: new Text(this.name));
}
}
(b). main.dart
mainファイルに以下のコードを追加します。
import 'package:flutter/material.dart';
import 'package:flutter_search_app/search_list.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Search',
debugShowCheckedModeBanner: false,
theme: new ThemeData(
brightness: Brightness.light,
primarySwatch:Colors.yellow,
primaryColor: Color(0xFFFFBB54),
accentColor: Color(0xFFECEFF1),
),
home: new SearchList(),
);
}
}
以上で完成です。
Special Thanks to @MageshPandian20 for this wonderful example.
Download Projectをご覧ください。
それでは、良い一日をお過ごしください。