commit 7b2220109b020f42e36e6f9e15eacaeadad30c1f
parent 6324b0444b261b965421da237540e6353b7d3a94
Author: Marcel <mtrnord1@gmail.com>
Date: Sat, 29 Feb 2020 21:40:06 +0100
Add Search to StationsTab
Took 47 minutes
Diffstat:
3 files changed, 189 insertions(+), 54 deletions(-)
diff --git a/lib/components/SearchBar.dart b/lib/components/SearchBar.dart
@@ -0,0 +1,86 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+
+class SearchBar extends StatefulWidget {
+ final TextEditingController filterTextController;
+ final onTextChanged;
+
+ SearchBar({
+ this.filterTextController,
+ this.onTextChanged,
+ Key key,
+ }) : super(key: key);
+
+ @override
+ SearchBarState createState() => SearchBarState();
+}
+
+class SearchBarState extends State<SearchBar> {
+ String text;
+
+ @override
+ build(BuildContext context) {
+ return TextField(
+ controller: widget.filterTextController,
+ keyboardType: TextInputType.text,
+ maxLines: 1,
+ style: TextStyle(
+ color: Colors.blueGrey,
+ fontSize: 15,
+ fontWeight: FontWeight.w400,
+ fontStyle: FontStyle.normal,
+ letterSpacing: 0,
+ ),
+ onChanged: (String newText) {
+ setState(() {
+ text = newText;
+ });
+ if (widget.onTextChanged != null) widget.onTextChanged(newText);
+ },
+ decoration: InputDecoration(
+ fillColor: Color(0xffedf0f2),
+ border: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(20),
+ borderSide: BorderSide(
+ width: 0,
+ style: BorderStyle.none,
+ ),
+ ),
+ filled: true,
+ contentPadding: EdgeInsets.all(11),
+ prefixIcon: Icon(
+ Icons.search,
+ color: Color(0xffedf0f2),
+ ),
+ suffixIcon: text != null && text != ""
+ ? IconButton(
+ icon: Icon(
+ Icons.cancel,
+ color: Colors.blueGrey,
+ ),
+ onPressed: () {
+ // workaround
+ // see https://github.com/flutter/flutter/issues/35848#issuecomment-527854562
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ widget.filterTextController.clear(); // clear text
+ FocusScope.of(context)
+ .requestFocus(FocusNode()); // hide keyboard
+ setState(() {
+ text = "";
+ });
+ });
+ },
+ )
+ : null,
+ hintText: "Search",
+ hintStyle: TextStyle(
+ color: Colors.blueGrey,
+ fontSize: 15,
+ fontWeight: FontWeight.w400,
+ fontStyle: FontStyle.normal,
+ letterSpacing: 0,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/views/HomePage.dart b/lib/views/HomePage.dart
@@ -86,13 +86,8 @@ class HomePage extends StatelessWidget {
railwayStationsRepository: railwayStationsRepository,
),
),
- Scaffold(
- appBar: AppBar(
- title: Text("Bahnhöfe"),
- ),
- body: StationsTab(
- railwayStationsRepository: railwayStationsRepository,
- ),
+ StationsTab(
+ railwayStationsRepository: railwayStationsRepository,
),
Scaffold(
appBar: AppBar(
diff --git a/lib/views/tabs/StationsTab.dart b/lib/views/tabs/StationsTab.dart
@@ -1,5 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
+import 'package:rs_flutter_app/components/SearchBar.dart';
import '../../models/models.dart';
import '../../repos/repositories.dart';
@@ -17,11 +18,52 @@ class StationsTab extends StatefulWidget {
class StationsTabState extends State<StationsTab>
with AutomaticKeepAliveClientMixin<StationsTab> {
+ final filterTextController = TextEditingController();
List<Station> stations;
+ String filterText;
- Future<List<Station>> getStations() async {
+ List<Station> filter() {
+ var stationsL = stations;
+ if (filterText != null && filterText != "") {
+ stationsL = stations
+ .where(
+ (station) => (filterText != null &&
+ filterText != "" &&
+ station.title
+ .toLowerCase()
+ .startsWith(filterText.toLowerCase())),
+ )
+ .toList(growable: false);
+ }
+ return stationsL;
+ }
+
+ void handleFilter() {
+ setState(() {
+ filterText = filterTextController.text;
+ });
+ }
+
+ @override
+ initState() {
+ super.initState();
+ filterTextController.addListener(handleFilter);
+ }
+
+ @override
+ void dispose() {
+ filterTextController.removeListener(handleFilter);
+ super.dispose();
+ }
+
+ Future<List<Station>> getStations(String filterText) async {
if (stations == null) {
- stations = await widget.railwayStationsRepository.getStations();
+ List<Station> stationsL =
+ await widget.railwayStationsRepository.getStations();
+ stationsL.sort(
+ (a, b) => (a.title).compareTo(b.title),
+ );
+ stations = stationsL;
}
return stations;
}
@@ -30,55 +72,67 @@ class StationsTabState extends State<StationsTab>
Widget build(BuildContext context) {
super.build(context);
- return FutureBuilder(
- future: getStations(),
- builder: (BuildContext context, AsyncSnapshot<List<Station>> snapshot) {
- switch (snapshot.connectionState) {
- case ConnectionState.done:
- if (snapshot.hasError) {
- // TODO handle error
- debugPrint(snapshot.error.toString());
- }
- if (snapshot.hasData) {
- List<Station> stations = snapshot.data;
- stations.sort(
- (a, b) => (a.title).compareTo(b.title),
- );
+ return Scaffold(
+ appBar: AppBar(
+ title: Text("Bahnhöfe"),
+ bottom: PreferredSize(
+ preferredSize: Size.fromHeight(56),
+ child: Padding(
+ padding: EdgeInsets.all(8),
+ child: SearchBar(
+ filterTextController: filterTextController,
+ ),
+ ),
+ ),
+ ),
+ body: FutureBuilder<List<Station>>(
+ future: getStations(filterText),
+ builder: (BuildContext context, AsyncSnapshot<List<Station>> snapshot) {
+ if (snapshot.hasError) {
+ // TODO handle error
+ debugPrint(snapshot.error.toString());
+ }
+ if (!snapshot.hasData) {
+ return Center(
+ child: CircularProgressIndicator(),
+ );
+ }
+ var filtered = filter();
- return ListView.separated(
- itemCount: stations.length,
- separatorBuilder: (BuildContext context, int index) {
- return Divider();
- },
- itemBuilder: (BuildContext context, int index) {
- return ListTile(
- trailing: stations[index].photoUrl != ""
- ? Icon(Icons.image)
- : null,
- title: Text(stations[index].title),
- onTap: () {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (BuildContext context) => DetailsPage(
- station: stations[index],
- railwayStationsRepository:
- widget.railwayStationsRepository,
- ),
- ),
- );
- },
+ if (filtered == null || filtered.isEmpty) {
+ return Container();
+ }
+
+ if (stations.isEmpty) {
+ return Container();
+ }
+
+ return ListView.separated(
+ itemCount: filtered.length,
+ separatorBuilder: (BuildContext context, int index) {
+ return Divider();
+ },
+ itemBuilder: (BuildContext context, int index) {
+ return ListTile(
+ trailing:
+ filtered[index].photoUrl != "" ? Icon(Icons.image) : null,
+ title: Text(filtered[index].title),
+ onTap: () {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (BuildContext context) => DetailsPage(
+ station: filtered[index],
+ railwayStationsRepository:
+ widget.railwayStationsRepository,
+ ),
+ ),
);
},
);
- }
- // TODO handle missing date
- return Container();
- default:
- return Center(
- child: CircularProgressIndicator(),
- );
- }
- },
+ },
+ );
+ },
+ ),
);
}