Fixed bug - Loading Indicator now functions correctly

This commit is contained in:
2025-01-27 21:08:41 +01:00
parent 13bd344807
commit 36dec2d0de
5 changed files with 106 additions and 29 deletions

View File

@@ -18,20 +18,8 @@ class LandingPage extends StatefulWidget {
class _LandingPageState extends State<LandingPage> { class _LandingPageState extends State<LandingPage> {
late FloodStationProvider floodStationProvider; late FloodStationProvider floodStationProvider;
bool _isLoading = false;
OverlayEntry? _overlayEntry; OverlayEntry? _overlayEntry;
int loadingTimes = 0;
@override
initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_isLoading = true;
showLoadingNotifier(context: context, message: 'Loading');
floodStationProvider
.loadAllStations()
.whenComplete(() => removeLoadingNotifier());
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -40,26 +28,67 @@ class _LandingPageState extends State<LandingPage> {
body: Column( body: Column(
children: [ children: [
StationFilter( StationFilter(
onEditingComplete: (filterText) {}, 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;
},
), ),
Expanded( _shouldShowList()
? Expanded(
child: FloodStationListView( child: FloodStationListView(
stations: floodStationProvider.allStations, stations: floodStationProvider.filtered
? floodStationProvider.filteredStations
: floodStationProvider.allStations,
onItemTapped: (station) { onItemTapped: (station) {
floodStationProvider.selectedStation = station; floodStationProvider.selectedStation = station;
Navigator.of(context).pushNamed(FloodStationPage.routeName); 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<void> showLoadingNotifier({ Future<void> showLoadingNotifier({
required BuildContext context, required BuildContext context,
required String message, required String message,
}) async { }) async {
if (_overlayEntry != null) return;
OverlayState? overlayState = Overlay.of(context); OverlayState? overlayState = Overlay.of(context);
_overlayEntry = OverlayEntry( _overlayEntry = OverlayEntry(
builder: (c) { builder: (c) {
@@ -70,7 +99,10 @@ class _LandingPageState extends State<LandingPage> {
child: Center( child: Center(
child: LoadingNotifier( child: LoadingNotifier(
message: 'Loading', message: 'Loading',
onDismissed: () => removeLoadingNotifier(), onDismissed: () {
floodStationProvider.cancelFilterLoading();
removeLoadingNotifier();
},
), ),
), ),
); );

View File

@@ -21,6 +21,20 @@ class Api {
return stations; return stations;
} }
/// Fetches all stations whose label contain [label]
static Future<List<FloodStation>> fetchFilteredStations(String label) async {
List<FloodStation> stations = [];
final response =
await http.get(Uri.parse('$_rootUrl/id/stations?search=$label'));
if (response.statusCode == 200) {
final Map<String, dynamic> 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 /// [limit] limits the number of entries that are requested from the API and [offset] returns the
/// list starting from the specified number /// list starting from the specified number

View File

@@ -1,14 +1,22 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:async/async.dart';
import '../model/flood_station.dart'; import '../model/flood_station.dart';
import 'api.dart'; import 'api.dart';
class FloodStationProvider extends ChangeNotifier { class FloodStationProvider extends ChangeNotifier {
FloodStation? selectedStation; FloodStation? selectedStation;
bool filtered = false;
List<FloodStation> _allStations = []; List<FloodStation> _allStations = [];
List<FloodStation> _filteredStations = [];
List<FloodStation> get allStations => _allStations; List<FloodStation> get allStations => _allStations;
List<FloodStation> get filteredStations => _filteredStations;
CancelableOperation? _filteredStationsFuture;
CancelableOperation? get filteredStationsFuture => _filteredStationsFuture;
Future loadAllStationsInBatches({silent = false}) { Future loadAllStationsInBatches({silent = false}) {
int offset = 0; 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();
}
}
} }

View File

@@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class StationFilter extends StatefulWidget { class StationFilter extends StatefulWidget {
const StationFilter({super.key, required this.onEditingComplete}); const StationFilter({super.key, required this.onChanged});
final void Function(String filterText) onEditingComplete; final void Function(String filterText) onChanged;
@override @override
State<StationFilter> createState() => StationFilterState(); State<StationFilter> createState() => StationFilterState();
@@ -22,15 +22,15 @@ class StationFilterState extends State<StationFilter> {
child: IconButton( child: IconButton(
onPressed: () { onPressed: () {
filterController.clear(); filterController.clear();
setState(() {});
widget.onChanged(filterController.text);
}, },
icon: Icon(Icons.delete), icon: Icon(Icons.delete),
), ),
), ),
label: Text('Filter'), label: Text('Filter'),
), ),
onChanged: (_) => setState(() {}), onChanged: (_) => widget.onChanged(filterController.text),
onEditingComplete: () => widget.onEditingComplete(filterController.text),
); );
} }
} }

View File

@@ -16,6 +16,7 @@ dependencies:
intl: ^0.20.2 intl: ^0.20.2
provider: ^6.1.2 provider: ^6.1.2
timezone: ^0.10.0 timezone: ^0.10.0
async: ^2.11.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: