#更新履歴
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を渡してリストをスクロールできるようにする。
・リポジトリー
