Friday, November 23, 2018

Flutter - ListView items custom filter.

We always use listview to display too many items like contact numbers and products in any shopping application. Let's assume a situation, you want to see an item that's placed in the bottom of list. So, it will irritate you to always scroll it down to see a particular item.

list view cutom search filter
In mobility, we can make it easy with the help of a search feature. ListView with search functionality helps the user to easily get the required results. In the search box, we can filter the data according to data entered by the user and this option is really necessary for making it user-friendly. By using this feature user can filter items by name, occupation, address, etc. Search functionality reduces navigation complexity for the user and also saves the user’s valuable time.

In this post, we going to create a Flutter application with listview items and we applying a custom filter inside a ListView. When user will write some characters inside an input search field. The search function will filter the listview with a matching string from the user input. The final output of app looks like below:


>Creating a New Project
1. Create a new project from File ⇒ New Flutter Project with your development IDE and add http: "^0.12.0" for fetch data from the server.

2. Create a PODO model country.dart file to representing a country in the list view.
country.dart
class Country { String name; String flag; String capital; Country({this.name, this.flag, this.capital}); factory Country.fromJson(Map<String, dynamic> json) { return new Country( name: json['name'] as String, flag: json['flag'] as String, capital: json['capital'] as String, ); } }

3. Now create a networklayer.dart business layer of the application. It will fetch some data from the server and prase it in above PODO model.
networklayer.dart
import 'dart:async'; import 'package:http/http.dart' as http; Future<String> fetchCountry(http.Client client) async { final response = await client.get('http://restcountries.eu/rest/v2/all'); // Use the compute function to run parsePhotos in a separate isolate return response.body; }

4. Now start work on user interface part of this application. Open main.dart file and start editing it. As we have set our theme and change debug banner property of Application.
main.dart
import 'package:flutter/material.dart'; import 'package:flutter_list_filter/home.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( debugShowCheckedModeBanner: false, theme: new ThemeData( primaryColor: const Color(0xFF02BB9F), primaryColorDark: const Color(0xFF167F67), accentColor: const Color(0xFF167F67), ), home: new MainPage(), ); } }

5. Create home.dart widget for display search box in the app bar. When you tap on search icon, a big search box will open for type something and apply filter. Let's see complete widget snippet and try to understand it. We have written some doc in this file to understand it deeply with yellow color.
home.dart
import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_list_filter/country.dart'; import 'package:flutter_list_filter/list.dart'; import 'package:flutter_list_filter/netwoklayer.dart'; import 'package:http/http.dart' as http; class MainPage extends StatefulWidget { const MainPage(); @override _MainPageState createState() => new _MainPageState(); } class _MainPageState extends State<MainPage> with SingleTickerProviderStateMixin { static final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>(); //it will hold search box value TextEditingController _searchQuery; bool _isSearching = false;//maintain search box on/off state List<Country> filteredRecored; //getting data from the server @override void initState() { super.initState(); _searchQuery = new TextEditingController(); fetchCountry(new http.Client()).then((String) { parseData(String); }); } List<Country> allRecord; //parsing server response. parseData(String responseBody) { final parsed = json.decode(responseBody).cast<Map<String, dynamic>>(); setState(() { allRecord = parsed.map<Country>((json) => new Country.fromJson(json)).toList(); }); filteredRecored = new List<Country>(); filteredRecored.addAll(allRecord); } //It'll open search box void _startSearch() { ModalRoute.of(context) .addLocalHistoryEntry(new LocalHistoryEntry(onRemove: _stopSearching)); setState(() { _isSearching = true; }); } //It'll close search box. void _stopSearching() { _clearSearchQuery(); setState(() { _isSearching = false; filteredRecored.addAll(allRecord); }); } //clear search box data. void _clearSearchQuery() { setState(() { _searchQuery.clear(); updateSearchQuery("Search query"); }); } //Create a app bar title widget Widget _buildTitle(BuildContext context) { var horizontalTitleAlignment = Platform.isIOS ? CrossAxisAlignment.center : CrossAxisAlignment.start; return new InkWell( onTap: () => scaffoldKey.currentState.openDrawer(), child: new Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), child: new Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: horizontalTitleAlignment, children: <Widget>[ new Text('Seach box', style: new TextStyle(color: Colors.white),), ], ), ), ); } //Creating search box widget Widget _buildSearchField() { return new TextField( controller: _searchQuery, autofocus: true, decoration: const InputDecoration( hintText: 'Search...', border: InputBorder.none, hintStyle: const TextStyle(color: Colors.white30), ), style: const TextStyle(color: Colors.white, fontSize: 16.0), onChanged: updateSearchQuery, ); } //It'll update list items atfer searching complete. void updateSearchQuery(String newQuery) { filteredRecored.clear(); if (newQuery.length > 0) { Set<Country> set = Set.from(allRecord); set.forEach((element) => filterList(element, newQuery)); } if (newQuery.isEmpty) { filteredRecored.addAll(allRecord); } setState(() {}); } //Filtering the list item with found match string. filterList(Country country, String searchQuery) { setState(() { if (country.name.toLowerCase().contains(searchQuery) || country.name.contains(searchQuery)) { filteredRecored.add(country); } }); } List<Widget> _buildActions() { if (_isSearching) { return <Widget>[ new IconButton( icon: const Icon(Icons.clear,color: Colors.white,), onPressed: () { if (_searchQuery == null || _searchQuery.text.isEmpty) { Navigator.pop(context); return; } _clearSearchQuery(); }, ), ]; } return <Widget>[ new IconButton( icon: const Icon(Icons.search,color: Colors.white,), onPressed: _startSearch, ), ]; } @override Widget build(BuildContext context) { return new Scaffold( key: scaffoldKey, appBar: new AppBar( leading: _isSearching ? new BackButton( color: Colors.white,) : null, title: _isSearching ? _buildSearchField() : _buildTitle(context), actions: _buildActions(), ), body: filteredRecored != null && filteredRecored.length > 0 ? new CountyList(country: filteredRecored) : allRecord == null ? new Center(child: new CircularProgressIndicator()) : new Center( child: new Text("No recored match!"), ), ); } }




6. At the end, create final widget list.dart for this example. It'll show list of the countries. In this widget, we passing all or filtered list of items.
list.dart
import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_list_filter/country.dart'; class CountyList extends StatelessWidget { final List<Country> country; CountyList({Key key, this.country}) : super(key: key); @override Widget build(BuildContext context) { return new ListView.builder( itemCount: country == null ? 0 : country.length, itemBuilder: (BuildContext context, int index) { return new Card( child: new Container( child: new Center( child: new Column( // Stretch the cards in horizontal axis crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ new Text( // Read the name field value and set it in the Text widget country[index].name, // set some style to text style: new TextStyle( fontSize: 20.0, color: Colors.lightBlueAccent), ), new Text( // Read the name field value and set it in the Text widget "Capital:- " + country[index].capital, // set some style to text style: new TextStyle( fontSize: 20.0, color: Colors.amber), ), ], )), padding: const EdgeInsets.all(15.0), ), ); }); } }


Listview custom filter example source code               Android Custom filtter of listview APK


If you have followed the article carefully, you can see the app running very smoothly as shown in the above video. But if you are facing any problem or you have any quires, please feel free to ask it from below comment section and you can ask it on SLACK.

Share:

Get it on Google Play

React Native - Start Development with Typescript

React Native is a popular framework for building mobile apps for both Android and iOS. It allows developers to write JavaScript code that ca...