0
1

More than 1 year has passed since last update.

Flutter 検索バー作成

Last updated at Posted at 2021-09-21

#更新履歴

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'
];

#画面
all.png

search.png

#その他

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

・リポジトリー

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1