#更新履歴
2022/03/20
・書き方を修正し、githubのリポジトリーを追加
Flutter : 2.2.1 → 2.10.0に変更
#背景
以前書いた記事
flappy_search_barの使い方
で使用したライブラリーがDISCONTINUEDになったことと、やりたかったことができない部分があったので、自作してみた。
#環境
PC : macOS Monterey(インテルCPU)
エディター : Visual Studio Code
Flutter : 2.10.0
#状態管理
provider + changenotifier
#コード
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_search_bar_sample/view/app_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'search bar sample app',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const AppPage(),
);
}
}
app_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_search_bar_sample/view/search_bar_controller.dart';
import 'package:provider/provider.dart';
class AppPage extends StatelessWidget {
const AppPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) {
final controller = SearchBarController();
controller.init();
return controller;
},
child: Consumer<SearchBarController>(
builder: (BuildContext context, SearchBarController searchBarController,
Widget? child) {
if (searchBarController.isLoading) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
return Scaffold(
key: searchBarController.globalKey,
appBar: AppBar(
centerTitle: true,
title: _appBarTitle(searchBarController),
actions: [
IconButton(
icon: _appbarIcon(searchBarController),
onPressed: searchBarController.isSearching
? searchBarController.searchEnd
: searchBarController.searchStart,
),
],
),
body: _listWidget(searchBarController),
);
},
),
);
}
Widget _appbarIcon(SearchBarController searchBarController) {
return searchBarController.isSearching
? const Icon(
Icons.close,
color: Colors.white,
)
: const Icon(
Icons.search,
color: Colors.white,
);
}
Widget _appBarTitle(SearchBarController searchBarController) {
return searchBarController.isSearching
? TextField(
autofocus: true,
cursorColor: Colors.white,
style: const TextStyle(
color: Colors.white,
),
decoration: const InputDecoration(
prefixIcon: Icon(
Icons.search,
color: Colors.white,
),
hintText: 'Search...',
hintStyle: TextStyle(
color: Colors.white,
),
),
onChanged: (word) {
searchBarController.searchOperation(word);
},
)
: const Text(
'検索リスト',
style: TextStyle(
color: Colors.white,
),
);
}
Widget _listWidget(SearchBarController searchBarController) {
final itemList = (searchBarController.isSearching &&
searchBarController.searchedText.isNotEmpty)
? searchBarController.searchedItemList
: searchBarController.allItemList;
if (itemList.isEmpty) {
return Center(
child: Text('"${searchBarController.searchedText}"を含む文字列は見つかりませんでした。'),
);
} else {
return Container(
color: Colors.white,
child: ListView.builder(
key: const PageStorageKey(0),
controller: searchBarController.scrollController,
itemCount: itemList.length,
itemBuilder: (context, index) {
return SizedBox(
height: 60,
child: Card(
child: Center(
child: Text(itemList[index]),
),
),
);
},
),
);
}
}
}
search_bar_controller.dart
import 'package:flutter/material.dart';
import 'package:flutter_search_bar_sample/sample_data.dart';
class SearchBarController extends ChangeNotifier {
final globalKey = GlobalKey<ScaffoldState>();
/// state
bool isLoading = true;
bool isSearching = false;
String _searchedText = '';
var allItemList = <String>[];
var searchedItemList = <String>[];
String get searchedText => _searchedText;
final scrollController = ScrollController();
void init() {
fetch();
}
Future<void> fetch() async {
await Future.delayed(const Duration(seconds: 1)).then((_) {
// fetch data
// allItemList = await hogehogeRepo.fetch();
allItemList = exampleList;
isLoading = false;
notifyListeners();
});
}
void searchStart() {
isSearching = true;
notifyListeners();
}
void searchEnd() {
isSearching = false;
_searchedText = '';
searchedItemList.clear();
notifyListeners();
}
void searchOperation(String searchText) {
_searchedText = searchText;
searchedItemList.clear();
searchedItemList.addAll(
allItemList.where(
(element) => element.toLowerCase().contains(
searchText.toLowerCase(),
),
),
);
notifyListeners();
}
}
サンプルデータ
example_data.dart
List<String> exampleList = [
'Axvfgfdg',
'Axvfgfdg3',
'Bsdadasd',
'Axvfgfdg2',
'Bsdadasd3',
'Cat',
'Bsdadasd2',
'Cat2',
'Cat3',
'Dog',
'Dog2',
'Dog3',
'Elephant',
'Elephant2',
'Elephant3',
'Fans',
'Girls',
'Hiiii',
'Ilu',
'Jeans',
'Kite',
'Lion',
'Men',
'Nephow',
'Owl',
'Please',
'Quat',
'Rose',
'Salt',
'Trolly',
'Up',
'View',
'Window',
'Xbox',
'Yellow',
'Yummy',
'Zubin',
'Zara',
'Fans2',
'Girls2',
'Hiiii2',
'Ilu2',
'Jeans2',
'Kite2',
'Lion2',
'Men2',
'Nephow2',
'Owl2',
'Please2',
'Quat2',
'Rose2',
'Salt2',
'Trolly2',
'Up2',
'View2',
'Window2',
'Xbox2',
'Yellow2',
'Yummy2',
'Zubin2',
'Zara2',
'あいうえお',
'かきくけこ',
'さしすせそ',
'12345',
'678',
'999'
];

#その他
・検索バーでやりたかったこと(後で記事にする予定)
一覧リストと検索結果リストのPrimaryScrollControllerやScrollControllerをそれぞれ用意する。
リスト横にA-Zインデックス?みたいなwidgetを用意する。
ScrollControllerを渡してリストをスクロールできるようにする。
・リポジトリー