diff --git a/lib/pages/landing_page.dart b/lib/pages/landing_page.dart index 0a0f052..348a5e8 100644 --- a/lib/pages/landing_page.dart +++ b/lib/pages/landing_page.dart @@ -18,20 +18,8 @@ class LandingPage extends StatefulWidget { class _LandingPageState extends State { late FloodStationProvider floodStationProvider; - bool _isLoading = false; OverlayEntry? _overlayEntry; - - @override - initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - _isLoading = true; - showLoadingNotifier(context: context, message: 'Loading'); - floodStationProvider - .loadAllStations() - .whenComplete(() => removeLoadingNotifier()); - }); - } + int loadingTimes = 0; @override Widget build(BuildContext context) { @@ -40,26 +28,67 @@ class _LandingPageState extends State { body: Column( children: [ StationFilter( - onEditingComplete: (filterText) {}, - ), - Expanded( - child: FloodStationListView( - stations: floodStationProvider.allStations, - onItemTapped: (station) { - floodStationProvider.selectedStation = station; - Navigator.of(context).pushNamed(FloodStationPage.routeName); - }, - ), + onChanged: (filterText) { + if (filterText.isEmpty) { + floodStationProvider.filtered = false; + setState(() {}); + return; + } + showLoadingNotifier(context: context, message: 'Loading'); + floodStationProvider.loadFilteredStations(filterText); + floodStationProvider.filteredStationsFuture + ?.then((_) => removeLoadingNotifier()); + floodStationProvider.filtered = true; + }, ), + _shouldShowList() + ? Expanded( + child: FloodStationListView( + stations: floodStationProvider.filtered + ? floodStationProvider.filteredStations + : floodStationProvider.allStations, + onItemTapped: (station) { + floodStationProvider.selectedStation = station; + Navigator.of(context) + .pushNamed(FloodStationPage.routeName); + }, + ), + ) + : Expanded( + child: Center( + child: ElevatedButton( + onPressed: () { + showLoadingNotifier( + context: context, message: 'Loading'); + floodStationProvider + .loadAllStations() + .whenComplete(() => removeLoadingNotifier()); + }, + child: Text('Load all Stations'), + ), + ), + ), ], ), ); } + bool _shouldShowList() { + if (!floodStationProvider.filtered && + floodStationProvider.allStations.isNotEmpty) { + return true; + } + if (floodStationProvider.filtered) { + return true; + } + return false; + } + Future showLoadingNotifier({ required BuildContext context, required String message, }) async { + if (_overlayEntry != null) return; OverlayState? overlayState = Overlay.of(context); _overlayEntry = OverlayEntry( builder: (c) { @@ -70,7 +99,10 @@ class _LandingPageState extends State { child: Center( child: LoadingNotifier( message: 'Loading', - onDismissed: () => removeLoadingNotifier(), + onDismissed: () { + floodStationProvider.cancelFilterLoading(); + removeLoadingNotifier(); + }, ), ), ); diff --git a/lib/services/api.dart b/lib/services/api.dart index 099e040..ad6d69f 100644 --- a/lib/services/api.dart +++ b/lib/services/api.dart @@ -21,6 +21,20 @@ class Api { return stations; } + /// Fetches all stations whose label contain [label] + static Future> fetchFilteredStations(String label) async { + List stations = []; + final response = + await http.get(Uri.parse('$_rootUrl/id/stations?search=$label')); + if (response.statusCode == 200) { + final Map jsonStr = jsonDecode(response.body); + for (final str in jsonStr['items']) { + stations.add(FloodStation.fromMap(str)); + } + } + return stations; + } + /// [limit] limits the number of entries that are requested from the API and [offset] returns the /// list starting from the specified number diff --git a/lib/services/flood_station_provider.dart b/lib/services/flood_station_provider.dart index a1f8a77..972c71c 100644 --- a/lib/services/flood_station_provider.dart +++ b/lib/services/flood_station_provider.dart @@ -1,14 +1,22 @@ import 'package:flutter/material.dart'; +import 'package:async/async.dart'; import '../model/flood_station.dart'; import 'api.dart'; class FloodStationProvider extends ChangeNotifier { FloodStation? selectedStation; + bool filtered = false; List _allStations = []; + List _filteredStations = []; List get allStations => _allStations; + List get filteredStations => _filteredStations; + + CancelableOperation? _filteredStationsFuture; + + CancelableOperation? get filteredStationsFuture => _filteredStationsFuture; Future loadAllStationsInBatches({silent = false}) { int offset = 0; @@ -37,4 +45,26 @@ class FloodStationProvider extends ChangeNotifier { }, ); } + + Future loadFilteredStations(String filter, {silent = false}) { + if (_filteredStationsFuture != null) { + _filteredStationsFuture!.cancel(); + } + final future = Api.fetchFilteredStations(filter); + _filteredStationsFuture = CancelableOperation.fromFuture(future).then( + (value) { + _filteredStations = value; + if (!silent) { + notifyListeners(); + } + }, + ); + return future; + } + + void cancelFilterLoading() { + if (_filteredStationsFuture != null) { + _filteredStationsFuture!.cancel(); + } + } } diff --git a/lib/widgets/station_filter.dart b/lib/widgets/station_filter.dart index 5adf551..d6fdc1a 100644 --- a/lib/widgets/station_filter.dart +++ b/lib/widgets/station_filter.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; class StationFilter extends StatefulWidget { - const StationFilter({super.key, required this.onEditingComplete}); - final void Function(String filterText) onEditingComplete; + const StationFilter({super.key, required this.onChanged}); + final void Function(String filterText) onChanged; @override State createState() => StationFilterState(); @@ -22,15 +22,15 @@ class StationFilterState extends State { child: IconButton( onPressed: () { filterController.clear(); - setState(() {}); + + widget.onChanged(filterController.text); }, icon: Icon(Icons.delete), ), ), label: Text('Filter'), ), - onChanged: (_) => setState(() {}), - onEditingComplete: () => widget.onEditingComplete(filterController.text), + onChanged: (_) => widget.onChanged(filterController.text), ); } } diff --git a/pubspec.yaml b/pubspec.yaml index c0f74c1..a938642 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: intl: ^0.20.2 provider: ^6.1.2 timezone: ^0.10.0 + async: ^2.11.0 dev_dependencies: flutter_test: