Compare commits
14 Commits
8f5ed07be9
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ce77237c9c | |||
| 3bc325763a | |||
| 0567bc61f6 | |||
| 6b8321d850 | |||
| 647418748b | |||
| c4007e2982 | |||
| 7b7e3e9fe0 | |||
| 2655779fac | |||
| 85b4d73f4e | |||
| 512a5cc414 | |||
| 74d17c70ff | |||
| 8fc92eab23 | |||
| 9175471431 | |||
| 698778a7e0 |
BIN
fonts/ubuntu_sans/UbuntuSans-Bold.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-Bold.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-BoldItalic.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-ExtraBold.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-ExtraBold.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-ExtraBoldItalic.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-ExtraBoldItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-ExtraLight.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-ExtraLight.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-ExtraLightItalic.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-ExtraLightItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-Italic.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-Italic.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-Light.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-Light.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-LightItalic.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-LightItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-Medium.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-Medium.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-MediumItalic.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-Regular.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-Regular.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-SemiBold.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-SemiBold.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-SemiBoldItalic.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-SemiBoldItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-Thin.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-Thin.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans/UbuntuSans-ThinItalic.ttf
Normal file
BIN
fonts/ubuntu_sans/UbuntuSans-ThinItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-Bold.ttf
Normal file
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-Bold.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-BoldItalic.ttf
Normal file
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-Italic.ttf
Normal file
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-Italic.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-Medium.ttf
Normal file
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-Medium.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-MediumItalic.ttf
Normal file
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-Regular.ttf
Normal file
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-Regular.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-SemiBold.ttf
Normal file
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-SemiBold.ttf
Normal file
Binary file not shown.
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-SemiBoldItalic.ttf
Normal file
BIN
fonts/ubuntu_sans_mono/UbuntuSansMono-SemiBoldItalic.ttf
Normal file
Binary file not shown.
@@ -1,11 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'pages/about_page.dart';
|
||||
import 'pages/flood_station_page.dart';
|
||||
import 'pages/main_navigation_scaffold.dart';
|
||||
import 'services/flood_station_provider.dart';
|
||||
import 'package:timezone/data/latest.dart' as tz;
|
||||
|
||||
import 'theme.dart';
|
||||
|
||||
void main() {
|
||||
runApp(
|
||||
ChangeNotifierProvider(
|
||||
@@ -23,14 +26,12 @@ class MyApp extends StatelessWidget {
|
||||
tz.initializeTimeZones();
|
||||
return MaterialApp(
|
||||
title: 'Floodwatch',
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
useMaterial3: true,
|
||||
),
|
||||
theme: defaultTheme,
|
||||
initialRoute: MainNavigationScaffold.routeName,
|
||||
routes: {
|
||||
MainNavigationScaffold.routeName: (context) => MainNavigationScaffold(),
|
||||
FloodStationPage.routeName: (context) => FloodStationPage(),
|
||||
AboutPage.routeName: (context) => AboutPage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
30
lib/pages/about_page.dart
Normal file
30
lib/pages/about_page.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AboutPage extends StatelessWidget {
|
||||
const AboutPage({super.key});
|
||||
|
||||
static const String aboutText = '''
|
||||
This App was build for demonstration purposes.
|
||||
It requests data from the Environment Agency Real Time flood-monitoring API
|
||||
and displays a list and map of all flood measurement stations,
|
||||
as well as a graph showing the last 24 hours of measurements.
|
||||
The source code can be found at https://git.skup.in/.
|
||||
''';
|
||||
|
||||
static const String routeName = '/about';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('About'),
|
||||
),
|
||||
body: Center(
|
||||
child: Text(
|
||||
aboutText,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../widgets/flood_station_table.dart';
|
||||
import '../widgets/reading_graph.dart';
|
||||
import '../model/flood_station.dart';
|
||||
import '../model/reading.dart';
|
||||
@@ -16,6 +17,8 @@ class FloodStationPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _FloodStationPageState extends State<FloodStationPage> {
|
||||
bool _tableVisible = false;
|
||||
|
||||
@override
|
||||
void deactivate() {
|
||||
context.read<FloodStationProvider>().selectedStation = null;
|
||||
@@ -29,6 +32,14 @@ class _FloodStationPageState extends State<FloodStationPage> {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(station?.label ?? ''),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => setState(() {
|
||||
_tableVisible = !_tableVisible;
|
||||
}),
|
||||
child: _tableVisible ? Text('Show Graph') : Text('Show Table'),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: FutureBuilder<List<Reading>>(
|
||||
future: Api.fetchReadingsFromStation(station?.id ?? ''),
|
||||
@@ -36,6 +47,15 @@ class _FloodStationPageState extends State<FloodStationPage> {
|
||||
if (snapshot.hasData) {
|
||||
if (snapshot.data!.isEmpty) {
|
||||
return Center(child: Text('No readings on record.'));
|
||||
} else if (_tableVisible) {
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 30),
|
||||
child: FloodStationTable(
|
||||
readings: snapshot.data!,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Center(
|
||||
child: Padding(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'about_page.dart';
|
||||
import 'landing_page.dart';
|
||||
import 'map_page.dart';
|
||||
|
||||
@@ -23,6 +24,16 @@ class _MainNavigationScaffoldState extends State<MainNavigationScaffold> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Floodwatch'),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pushNamed(AboutPage.routeName),
|
||||
icon: Icon(Icons.info_outline),
|
||||
)
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: NavigationBar(
|
||||
selectedIndex: _selectedPageIndex,
|
||||
onDestinationSelected: (value) => setState(() {
|
||||
|
||||
@@ -22,14 +22,28 @@ class MapPage extends StatefulWidget {
|
||||
class _MapPageState extends State<MapPage> {
|
||||
final _mapController = MapController();
|
||||
late FloodStationProvider _floodStationProvider;
|
||||
bool _loading = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_floodStationProvider = context.watch<FloodStationProvider>();
|
||||
if (_floodStationProvider.allStations.isEmpty) {
|
||||
if (_loading == true) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
} else if (_floodStationProvider.allStations.isEmpty) {
|
||||
return Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: _floodStationProvider.loadAllStations,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_loading = true;
|
||||
});
|
||||
_floodStationProvider
|
||||
.loadAllStations()
|
||||
.whenComplete(() => setState(() {
|
||||
_loading = false;
|
||||
}));
|
||||
},
|
||||
child: Text('Load Map'),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
import '../model/flood_station.dart';
|
||||
import '../model/reading.dart';
|
||||
import 'date_utility.dart';
|
||||
|
||||
class Api {
|
||||
Api._();
|
||||
|
||||
static const String _rootUrl =
|
||||
'https://environment.data.gov.uk/flood-monitoring';
|
||||
|
||||
@@ -56,7 +58,8 @@ class Api {
|
||||
static Future<List<Reading>> fetchReadingsFromStation(
|
||||
String stationId) async {
|
||||
List<Reading> readings = [];
|
||||
final dateTime = _getCurrentUKTime().subtract(Duration(days: 1)).toUtc();
|
||||
final dateTime =
|
||||
DateUtility.currentUKTimeUtc.subtract(Duration(days: 1)).toUtc();
|
||||
final url =
|
||||
'$_rootUrl/id/stations/$stationId/readings?since=${dateTime.toIso8601String()}&_sorted';
|
||||
final response = await http.get(Uri.parse(url));
|
||||
@@ -68,9 +71,4 @@ class Api {
|
||||
}
|
||||
return readings.reversed.toList();
|
||||
}
|
||||
|
||||
static DateTime _getCurrentUKTime() {
|
||||
final london = tz.getLocation('Europe/London');
|
||||
return tz.TZDateTime.now(london);
|
||||
}
|
||||
}
|
||||
|
||||
37
lib/services/date_utility.dart
Normal file
37
lib/services/date_utility.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
|
||||
class DateUtility {
|
||||
static final intl.DateFormat _hmFormat = intl.DateFormat('Hm');
|
||||
static final intl.DateFormat _ymdhmFormat =
|
||||
intl.DateFormat('yyyy-MM-dd HH:mm');
|
||||
static final intl.DateFormat _ymdFormat = intl.DateFormat('yyyy-MM-dd');
|
||||
|
||||
// private default contructor so class can't be instanciated
|
||||
DateUtility._();
|
||||
|
||||
static DateTime get currentUKTimeUtc {
|
||||
final london = tz.getLocation('Europe/London');
|
||||
return tz.TZDateTime.now(london).toUtc();
|
||||
}
|
||||
|
||||
/// Formats a date in minutesSinceEpoch to a formatted String of HH:mm
|
||||
static String formatMinutesToHm(double value) {
|
||||
return _hmFormat.format(
|
||||
DateTime.fromMillisecondsSinceEpoch((value * 1000 * 60).toInt()));
|
||||
}
|
||||
|
||||
static String formatDateToHm(DateTime date) {
|
||||
return _hmFormat.format(date);
|
||||
}
|
||||
|
||||
/// Formats a date to yyyy-MM-dd HH:mm
|
||||
static String formatDateToYmdhm(DateTime date) {
|
||||
return _ymdhmFormat.format(date);
|
||||
}
|
||||
|
||||
/// Formats a date to yyyy-MM-dd
|
||||
static String formatDateToYmd(DateTime date) {
|
||||
return _ymdFormat.format(date);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class FloodStationProvider extends ChangeNotifier {
|
||||
|
||||
/// loads all stations in batches of 500 and notifies listeners with every loop except if [silent] = true
|
||||
/// this has lower performance than loading them all at once an shouldn't be used
|
||||
Future loadAllStationsInBatches({silent = false}) {
|
||||
Future loadAllStationsInBatches({bool silent = false}) {
|
||||
int offset = 0;
|
||||
return Future.doWhile(() async {
|
||||
final stations = await Api.fetchStationsByRange(500, offset);
|
||||
@@ -36,7 +36,7 @@ class FloodStationProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
/// loads all flood stations and notifies listeners when done
|
||||
Future loadAllStations({silent = false}) {
|
||||
Future loadAllStations({bool silent = false}) {
|
||||
return Api.fetchAllStations().then(
|
||||
(value) {
|
||||
_allStations = value;
|
||||
@@ -48,7 +48,7 @@ class FloodStationProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
/// loads all stations whose label contains [filter]
|
||||
Future loadFilteredStations(String filter, {silent = false}) {
|
||||
Future loadFilteredStations(String filter, {bool silent = false}) {
|
||||
if (_filteredStationsFuture != null) {
|
||||
_filteredStationsFuture!.cancel();
|
||||
}
|
||||
|
||||
35
lib/theme.dart
Normal file
35
lib/theme.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
ThemeData get defaultTheme {
|
||||
return ThemeData(
|
||||
fontFamily: 'UbuntuSansMono',
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: Colors.amber,
|
||||
brightness: Brightness.light,
|
||||
),
|
||||
textTheme: _defaultTextTheme,
|
||||
);
|
||||
}
|
||||
|
||||
ThemeData get defaultDarkTheme {
|
||||
return ThemeData(
|
||||
fontFamily: 'UbuntuSansMono',
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: Colors.lightGreen,
|
||||
brightness: Brightness.dark,
|
||||
),
|
||||
textTheme: _defaultTextTheme,
|
||||
);
|
||||
}
|
||||
|
||||
TextTheme get _defaultTextTheme => TextTheme(
|
||||
titleLarge: TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
titleMedium: TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
titleSmall: TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
);
|
||||
69
lib/widgets/flood_station_table.dart
Normal file
69
lib/widgets/flood_station_table.dart
Normal file
@@ -0,0 +1,69 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../model/reading.dart';
|
||||
import '../services/date_utility.dart';
|
||||
|
||||
class FloodStationTable extends StatelessWidget {
|
||||
const FloodStationTable({super.key, required List<Reading> readings})
|
||||
: _readings = readings;
|
||||
|
||||
final List<Reading> _readings;
|
||||
|
||||
List<TableRow> get _children {
|
||||
return _readings.map<TableRow>((e) => _getTableRow(e)).toList();
|
||||
}
|
||||
|
||||
TableRow _getTableRow(Reading reading) {
|
||||
String date = DateUtility.formatDateToYmd(reading.dateTime);
|
||||
String time = DateUtility.formatDateToHm(reading.dateTime);
|
||||
return TableRow(
|
||||
decoration: BoxDecoration(),
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
date,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Padding(padding: EdgeInsets.only(right: 12)),
|
||||
Text(
|
||||
time,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
reading.value.toString(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
backgroundColor: _getColorFromSeverity(reading.value),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Table(
|
||||
children: _children,
|
||||
columnWidths: {
|
||||
0: FixedColumnWidth(160),
|
||||
1: FlexColumnWidth(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Color _getColorFromSeverity(double value) {
|
||||
if (value < 1.0) return Color.fromARGB(0, 0, 0, 0);
|
||||
if (value < 2.0) return Colors.yellow;
|
||||
if (value < 3.0) return Colors.orange;
|
||||
return Colors.red;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
|
||||
import '../model/flood_station.dart';
|
||||
import '../services/date_utility.dart';
|
||||
|
||||
class MapPopup extends StatelessWidget {
|
||||
const MapPopup(
|
||||
@@ -43,7 +43,7 @@ class MapPopup extends StatelessWidget {
|
||||
Padding(padding: EdgeInsets.only(left: 8)),
|
||||
Text(
|
||||
_station.dateOpened != null
|
||||
? intl.DateFormat.yMd().format(_station.dateOpened!)
|
||||
? DateUtility.formatDateToYmd(_station.dateOpened!)
|
||||
: '-',
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'dart:math';
|
||||
|
||||
import '../model/reading.dart';
|
||||
import '../services/date_utility.dart';
|
||||
|
||||
class ReadingGraph extends StatelessWidget {
|
||||
const ReadingGraph({super.key, required List<Reading> readings})
|
||||
@@ -41,19 +41,21 @@ class ReadingGraph extends StatelessWidget {
|
||||
)
|
||||
],
|
||||
titlesData: FlTitlesData(
|
||||
bottomTitles: getBottomTitles(spots),
|
||||
leftTitles: getLeftTitles(spots),
|
||||
bottomTitles: _getBottomTitles(spots),
|
||||
leftTitles: _getLeftTitles(spots),
|
||||
topTitles: const AxisTitles(sideTitles: SideTitles()),
|
||||
rightTitles: const AxisTitles(sideTitles: SideTitles()),
|
||||
),
|
||||
lineTouchData: LineTouchData(
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
getTooltipColor: (touchedSpot) =>
|
||||
Theme.of(context).colorScheme.primaryContainer,
|
||||
getTooltipItems: (touchedSpots) {
|
||||
return touchedSpots.map((touchedSpot) {
|
||||
return LineTooltipItem(
|
||||
'${touchedSpot.y.toString()}\n${getLongDate(touchedSpot.x)}',
|
||||
'${touchedSpot.y.toString()}\n${_getLongDate(touchedSpot.x)}',
|
||||
TextStyle(
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
fontWeight: FontWeight.bold));
|
||||
}).toList();
|
||||
},
|
||||
@@ -63,7 +65,7 @@ class ReadingGraph extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
AxisTitles getBottomTitles(List<FlSpot> spots) {
|
||||
AxisTitles _getBottomTitles(List<FlSpot> spots) {
|
||||
return AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
interval: 90,
|
||||
@@ -73,13 +75,13 @@ class ReadingGraph extends StatelessWidget {
|
||||
minIncluded: false,
|
||||
getTitlesWidget: (value, meta) => SideTitleWidget(
|
||||
meta: meta,
|
||||
child: Text(getDate(value)),
|
||||
child: Text(DateUtility.formatMinutesToHm(value)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
AxisTitles getLeftTitles(List<FlSpot> spots) {
|
||||
AxisTitles _getLeftTitles(List<FlSpot> spots) {
|
||||
return AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
reservedSize: 50,
|
||||
@@ -94,23 +96,17 @@ class ReadingGraph extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
String getDate(double value) {
|
||||
intl.DateFormat hmFormat = intl.DateFormat('Hm');
|
||||
return hmFormat.format(
|
||||
DateTime.fromMillisecondsSinceEpoch((value * 1000 * 60).toInt()));
|
||||
}
|
||||
|
||||
String getLongDate(double value) {
|
||||
String _getLongDate(double value) {
|
||||
DateTime date =
|
||||
DateTime.fromMillisecondsSinceEpoch((value * 1000 * 60).toInt());
|
||||
int daysDifference = (DateTime.now().weekday - date.weekday + 7) % 7;
|
||||
|
||||
if (daysDifference == 0) {
|
||||
return 'Today ${intl.DateFormat('Hm').format(date)}';
|
||||
return 'Today ${DateUtility.formatDateToHm(date)}';
|
||||
} else if (daysDifference == 1) {
|
||||
return 'Yesterday ${intl.DateFormat('Hm').format(date)}';
|
||||
return 'Yesterday ${DateUtility.formatDateToHm(date)}';
|
||||
}
|
||||
|
||||
return intl.DateFormat('yyyy-MM-dd H:m').format(date);
|
||||
return DateUtility.formatDateToYmdhm(date);
|
||||
}
|
||||
}
|
||||
|
||||
67
pubspec.yaml
67
pubspec.yaml
@@ -30,5 +30,70 @@ dev_dependencies:
|
||||
|
||||
|
||||
flutter:
|
||||
|
||||
uses-material-design: true
|
||||
fonts:
|
||||
- family: UbuntuSans
|
||||
fonts:
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-Thin.ttf
|
||||
weight: 100
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-ThinItalic.ttf
|
||||
weight: 100
|
||||
style: italic
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-ExtraLight.ttf
|
||||
weight: 200
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-ExtraLightItalic.ttf
|
||||
weight: 200
|
||||
style: italic
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-Light.ttf
|
||||
weight: 300
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-LightItalic.ttf
|
||||
weight: 300
|
||||
style: italic
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-Regular.ttf
|
||||
weight: 400
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-Italic.ttf
|
||||
weight: 400
|
||||
style: italic
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-Medium.ttf
|
||||
weight: 500
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-MediumItalic.ttf
|
||||
weight: 500
|
||||
style: italic
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-SemiBold.ttf
|
||||
weight: 600
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-SemiBoldItalic.ttf
|
||||
weight: 600
|
||||
style: italic
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-Bold.ttf
|
||||
weight: 700
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-BoldItalic.ttf
|
||||
weight: 700
|
||||
style: italic
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-ExtraBold.ttf
|
||||
weight: 800
|
||||
- asset: fonts/ubuntu_sans/UbuntuSans-ExtraBoldItalic.ttf
|
||||
weight: 800
|
||||
style: italic
|
||||
|
||||
- family: UbuntuSansMono
|
||||
fonts:
|
||||
- asset: fonts/ubuntu_sans_mono/UbuntuSansMono-Regular.ttf
|
||||
weight: 400
|
||||
- asset: fonts/ubuntu_sans_mono/UbuntuSansMono-Italic.ttf
|
||||
weight: 400
|
||||
style: italic
|
||||
- asset: fonts/ubuntu_sans_mono/UbuntuSansMono-Medium.ttf
|
||||
weight: 500
|
||||
- asset: fonts/ubuntu_sans_mono/UbuntuSansMono-MediumItalic.ttf
|
||||
weight: 500
|
||||
style: italic
|
||||
- asset: fonts/ubuntu_sans_mono/UbuntuSansMono-SemiBold.ttf
|
||||
weight: 600
|
||||
- asset: fonts/ubuntu_sans_mono/UbuntuSansMono-SemiBoldItalic.ttf
|
||||
weight: 600
|
||||
style: italic
|
||||
- asset: fonts/ubuntu_sans_mono/UbuntuSansMono-Bold.ttf
|
||||
weight: 700
|
||||
- asset: fonts/ubuntu_sans_mono/UbuntuSansMono-BoldItalic.ttf
|
||||
weight: 700
|
||||
style: italic
|
||||
47
readme.md
Normal file
47
readme.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Floodwatch
|
||||
This App was build for demonstration purposes.
|
||||
It requests data from the Environment Agency Real Time flood-monitoring API and displays a list and map of all flood measurement stations,
|
||||
as well as a graph showing the last 24 hours of measurements.
|
||||
|
||||
## Features
|
||||
|
||||
### Interactive Map View
|
||||
- Uses OpenStreetMap to display all flood monitoring stations
|
||||
|
||||
### Station List
|
||||
- Complete list of all flood monitoring stations
|
||||
- Filtering by station label
|
||||
|
||||
### Detailed Station View
|
||||
- Line graphs showing water levels over the past 24 hours
|
||||
- Table showing all values with colour-coding to show severity
|
||||
|
||||
## Third-Party Packages used
|
||||
|
||||
- **provider**: State management solution for handling station data
|
||||
- **flutter_map**: Integration with OpenStreetMap for the map view
|
||||
- **fl_chart**: Creating line graphs for water levels
|
||||
|
||||
## Installation
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone https://git.skup.in/marco/floodwatch
|
||||
```
|
||||
2. Install dependencies:
|
||||
```bash
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
3. Run the app:
|
||||
```bash
|
||||
flutter run
|
||||
```
|
||||
|
||||
The App has been tested on Linux and Web and can be tested on android, too.
|
||||
|
||||
## Building release version
|
||||
|
||||
```bash
|
||||
flutter build
|
||||
```
|
||||
Reference in New Issue
Block a user