Cara Mudah Membuat Search Delegate Pada Flutter
Memastikan possible untuk penelusuran pencarian data melalui showSearch
dari data asset pada json.
1. Membuat Search Delegate
Contoh
susunan directory
❏ lib
❏ page
❏ train_page.dart
❏ components
❏ station_widget.dart
❏ station_search_delegate.dart
❏ model
❏ station.dart
❏ station_lookup.dart
❏ main.dart
❏ assets
❏ data
❏ stations.json
assets/data/stations.json
[
{
"station_code": "BD",
"station_name": "BANDUNG"
},
{
"station_code": "BKS",
"station_name": "BEKASI"
},
{
"station_code": "BOO",
"station_name": "BOGOR"
},
{
"station_code": "WT",
"station_name": "WATES"
},
{
"station_code": "YK",
"station_name": "YOGYAKARTA"
}
]
tambahkan pada pubspec.yaml
assets:
- assets/data/stations.json
kemudian jalankan
flutter pub get
lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:my_app/page/train_page.dart';
import 'package:my_app/model/station.dart';
import 'package:my_app/model/station_lookup.dart';
import 'dart:convert';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
List<Station> stations = await StationDataReader.load('assets/data/stations.json');
runApp(MyApp(stations: stations));
}
class StationDataReader {
static Future<List<Station>> load(String path) async {
final data = await rootBundle.loadString(path);
return json.decode(data).map<Station>((json) => Station.fromJson(json)).toList();
}
}
class MyApp extends StatelessWidget {
MyApp({ this.stations });
List<Station> stations;
final appTitle = 'Search Delegate Demo';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: appTitle,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: TrainPage(appTitle: appTitle, stationLookup: StationLookup(stations: stations)),
);
}
}
lib/model/station.dart
class Station extends Object {
String station_code;
String station_name;
Station({this.station_code, this.station_name});
factory Station.fromJson(Map<String, dynamic> json) {
return Station(
station_code: json['station_code'],
station_name: json['station_name'],
);
}
}
lib/model/station_lookup.dart
import 'package:my_app/model/station.dart';
class StationLookup {
StationLookup({ this.stations });
final List<Station> stations;
List<Station> searchString(String string) {
string = string.toLowerCase();
final matching = stations.where((station) { return station.station_code.toLowerCase() == string || station.station_name.toLowerCase() == string; }).toList();
if (matching.length > 0) return matching;
return stations.where((station) { return station.station_code.toLowerCase().contains(string) || station.station_name.toLowerCase().contains(string); }).toList();
}
}
lib/page/train_page.dart
import 'package:flutter/material.dart';
import 'package:my_app/page/components/station_widget.dart';
import 'package:my_app/page/components/station_search_delegate.dart';
import 'package:my_app/model/station.dart';
import 'package:my_app/model/station_lookup.dart';
import 'dart:async';
import 'dart:convert';
class TrainPage extends StatefulWidget {
TrainPage({ this.appTitle, this.stationLookup });
final StationLookup stationLookup;
String appTitle;
@override
_TrainPageState createState() => new _TrainPageState(appTitle: appTitle, stationLookup: stationLookup);
}
class _TrainPageState extends State<TrainPage> {
_TrainPageState({ this.appTitle, this.stationLookup });
String appTitle;
final StationLookup stationLookup;
Station departure;
Station arrival;
Future<Station> _showSearch(BuildContext context) async {
return await showSearch<Station>(
context: context,
delegate: StationSearchDelegate( stationLookup: stationLookup )
);
}
void _selectDepart(BuildContext context) async {
var station = await _showSearch(context);
setState(() => departure = station);
}
void _selectArrival(BuildContext context) async {
var station = await _showSearch(context);
setState(() => arrival = station);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(appTitle),
),
body: Container(
padding: const EdgeInsets.all(8.0),
child: SafeArea( // form
child: Column(
children: <Widget>[
StationWidget(title: 'KEBERANGKATAN', station: departure, onPressed: () => _selectDepart(context)),
StationWidget(title: 'TIBA', station: arrival, onPressed: () => _selectArrival(context)),
],
),
),
),
);
}
}
lib/page/components/station_widget.dart
import 'package:flutter/material.dart';
import 'package:my_app/model/station.dart';
class StationWidget extends StatelessWidget {
StationWidget({ this.title, this.station, this.onPressed });
final String title;
final Station station;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onPressed,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text( title ),
Text( station != null ? '${station.station_name} (${station.station_code})' : '---' ),
],
),
),
);
}
}
lib/page/components/station_search_delegate.dart
import 'package:flutter/material.dart';
import 'package:my_app/model/station.dart';
import 'package:my_app/model/station_lookup.dart';
class StationSearchDelegate extends SearchDelegate<Station> {
StationSearchDelegate({ @required this.stationLookup });
final StationLookup stationLookup;
@override
Widget buildLeading(BuildContext context) {
return IconButton(
tooltip: 'Back',
icon: AnimatedIcon( icon: AnimatedIcons.menu_arrow, progress: transitionAnimation, ),
onPressed: () { close(context, null); },
);
}
@override
Widget buildSuggestions(BuildContext context) {
return buildMatchingSuggestions(context);
}
@override
Widget buildResults(BuildContext context) {
return buildMatchingSuggestions(context);
}
Widget buildMatchingSuggestions(BuildContext context) {
if (query.length < 3) return Container();
final searched = stationLookup.searchString(query);
if (searched.length == 0) return StationSearchPlaceholder(title: 'No results');
return ListView.builder(
itemCount: searched.length,
itemBuilder: (context, index) {
return StationSearchResultTile( station: searched[index], searchDelegate: this, );
},
);
}
@override
List<Widget> buildActions(BuildContext context) {
return query.isEmpty ? [] : <Widget>[
IconButton(
tooltip: 'Clear',
icon: const Icon(Icons.clear),
onPressed: () { query = ''; showSuggestions(context); },
)
];
}
}
class StationSearchPlaceholder extends StatelessWidget {
StationSearchPlaceholder({@required this.title});
final String title;
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return Center(
child: Text( title, style: theme.textTheme.headline, textAlign: TextAlign.center, ),
);
}
}
class StationSearchResultTile extends StatelessWidget {
const StationSearchResultTile({ @required this.station, @required this.searchDelegate });
final Station station;
final SearchDelegate<Station> searchDelegate;
@override
Widget build(BuildContext context) {
final title = '${station.station_code}';
final subtitle = '${station.station_name}';
final ThemeData theme = Theme.of(context);
return ListTile(
dense: true,
title: Text( title, style: theme.textTheme.body2, textAlign: TextAlign.start, ),
subtitle: Text( subtitle, style: theme.textTheme.body1, textAlign: TextAlign.start, ),
onTap: () => searchDelegate.close(context, station),
);
}
}
Sekian untuk kali ini semoga bermanfaat :D untuk lebih lanjut bisa kunjungi link tersebut.