feat(map): migrate to flutter_map 8.2 with OSM-compliant tiles
This commit is contained in:
@ -34,12 +34,14 @@ Future<void> main() async {
|
|||||||
create: (BuildContext context) => GetIt.I.get<RefreshCubit>(),
|
create: (BuildContext context) => GetIt.I.get<RefreshCubit>(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: MyApp(),
|
child: const MyApp(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
|
const MyApp({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
|
|||||||
@ -1,14 +1,7 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:dio_cache_interceptor_hive_store/dio_cache_interceptor_hive_store.dart';
|
|
||||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:flutter_map_cache/flutter_map_cache.dart';
|
|
||||||
import 'package:flutter_map_compass/flutter_map_compass.dart';
|
import 'package:flutter_map_compass/flutter_map_compass.dart';
|
||||||
import 'package:flutter_map_math/flutter_geo_math.dart';
|
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
@ -18,15 +11,9 @@ import 'package:ot_viewer_app/settings_page.dart';
|
|||||||
import 'package:ot_viewer_app/user_path_bloc.dart';
|
import 'package:ot_viewer_app/user_path_bloc.dart';
|
||||||
import 'package:ot_viewer_app/util.dart';
|
import 'package:ot_viewer_app/util.dart';
|
||||||
import 'package:ot_viewer_app/web_socket_cubit.dart';
|
import 'package:ot_viewer_app/web_socket_cubit.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'owntracks_api.dart';
|
import 'owntracks_api.dart';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
Future<String> getPath() async {
|
|
||||||
final cacheDirectory = await getTemporaryDirectory();
|
|
||||||
return cacheDirectory.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MapPage extends StatefulWidget {
|
class MapPage extends StatefulWidget {
|
||||||
const MapPage({super.key});
|
const MapPage({super.key});
|
||||||
|
|
||||||
@ -55,14 +42,8 @@ class _MapPageState extends State<MapPage> {
|
|||||||
refreshCubit.stream.listen((_) => cubit.reconnect());
|
refreshCubit.stream.listen((_) => cubit.reconnect());
|
||||||
return cubit;
|
return cubit;
|
||||||
},
|
},
|
||||||
child: FutureBuilder<String>(
|
child: BlocBuilder<SettingsCubit, SettingsState>(
|
||||||
future: getPath(),
|
|
||||||
builder: (something, tempPath) =>
|
|
||||||
BlocBuilder<SettingsCubit, SettingsState>(
|
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (tempPath.data == null) {
|
|
||||||
return const Center(child: Text('Loading Map...'));
|
|
||||||
}
|
|
||||||
return Stack(children: [
|
return Stack(children: [
|
||||||
FlutterMap(
|
FlutterMap(
|
||||||
mapController: _mapController,
|
mapController: _mapController,
|
||||||
@ -72,12 +53,13 @@ class _MapPageState extends State<MapPage> {
|
|||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
TileLayer(
|
TileLayer(
|
||||||
urlTemplate:
|
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||||
'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
userAgentPackageName: 'ot_viewer_app',
|
||||||
tileProvider: CachedTileProvider(
|
tileProvider: NetworkTileProvider(
|
||||||
maxStale: const Duration(days: 30),
|
headers: {
|
||||||
store: HiveCacheStore(tempPath.data!,
|
'User-Agent': 'ot_viewer_app/1.0',
|
||||||
hiveBoxName: 'HiveCacheStore')),
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
...state.activeDevices.map((id) =>
|
...state.activeDevices.map((id) =>
|
||||||
UserPath(key: ValueKey(id), device: id, settings: state)),
|
UserPath(key: ValueKey(id), device: id, settings: state)),
|
||||||
@ -105,7 +87,6 @@ class _MapPageState extends State<MapPage> {
|
|||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,7 +121,6 @@ class _UserPathState extends State<UserPath> {
|
|||||||
if (state
|
if (state
|
||||||
case LocationUpdateReceived(
|
case LocationUpdateReceived(
|
||||||
:final position,
|
:final position,
|
||||||
:final deviceId
|
|
||||||
)) {
|
)) {
|
||||||
if (userPathBloc.deviceId == state.deviceId) {
|
if (userPathBloc.deviceId == state.deviceId) {
|
||||||
userPathBloc.add(UserPathLiveSubscriptionUpdate(position));
|
userPathBloc.add(UserPathLiveSubscriptionUpdate(position));
|
||||||
@ -161,15 +141,15 @@ class _UserPathState extends State<UserPath> {
|
|||||||
.add(UserPathLoginDataChanged(widget.settings));
|
.add(UserPathLoginDataChanged(widget.settings));
|
||||||
|
|
||||||
print("rebuild");
|
print("rebuild");
|
||||||
final _istate = state as MainUserPathState;
|
final mainState = state as MainUserPathState;
|
||||||
// make markers
|
// make markers
|
||||||
final List<Marker> markers = [];
|
final List<Marker> markers = [];
|
||||||
|
|
||||||
if (state.livePoints.isNotEmpty) {
|
if (mainState.livePoints.isNotEmpty) {
|
||||||
markers.add(Marker(
|
markers.add(Marker(
|
||||||
width: 500,
|
width: 500,
|
||||||
height: 100,
|
height: 100,
|
||||||
point: state.livePoints.last.asLatLng,
|
point: mainState.livePoints.last.asLatLng,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
@ -205,10 +185,11 @@ class _UserPathState extends State<UserPath> {
|
|||||||
// Create fancy fade-out and blended line (TODO: make distance based. use flutter_map_math)
|
// Create fancy fade-out and blended line (TODO: make distance based. use flutter_map_math)
|
||||||
List<List<LatLng>> segments = [];
|
List<List<LatLng>> segments = [];
|
||||||
List<Color> colors = [];
|
List<Color> colors = [];
|
||||||
if (state.initialPoints.isNotEmpty) {
|
if (mainState.initialPoints.isNotEmpty) {
|
||||||
final allPoints =
|
final allPoints = mainState.initialPoints
|
||||||
state.initialPoints.map((e) => LatLng(e.lat, e.lon)).toList();
|
.map((e) => LatLng(e.lat, e.lon))
|
||||||
final segmentCount = math.min(100, allPoints.length);
|
.toList();
|
||||||
|
final int segmentCount = math.min(100, allPoints.length);
|
||||||
final pointsPerSegment = (allPoints.length / segmentCount).ceil();
|
final pointsPerSegment = (allPoints.length / segmentCount).ceil();
|
||||||
|
|
||||||
// Split the points into segments and generate colors
|
// Split the points into segments and generate colors
|
||||||
@ -259,7 +240,7 @@ class _UserPathState extends State<UserPath> {
|
|||||||
key: ValueKey('${widget.device}_liveLines'),
|
key: ValueKey('${widget.device}_liveLines'),
|
||||||
polylines: [
|
polylines: [
|
||||||
Polyline(
|
Polyline(
|
||||||
points: state.livePoints
|
points: mainState.livePoints
|
||||||
.map((e) => LatLng(e.lat, e.lon))
|
.map((e) => LatLng(e.lat, e.lon))
|
||||||
.toList(),
|
.toList(),
|
||||||
strokeWidth: 4.0,
|
strokeWidth: 4.0,
|
||||||
|
|||||||
56
pubspec.lock
56
pubspec.lock
@ -113,38 +113,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
dio:
|
dart_polylabel2:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dio
|
name: dart_polylabel2
|
||||||
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
|
sha256: "7eeab15ce72894e4bdba6a8765712231fc81be0bd95247de4ad9966abc57adc6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.8.0+1"
|
version: "1.0.0"
|
||||||
dio_cache_interceptor:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: dio_cache_interceptor
|
|
||||||
sha256: "1346705a2057c265014d7696e3e2318b560bfb00b484dac7f9b01e2ceaebb07d"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.5.1"
|
|
||||||
dio_cache_interceptor_hive_store:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: dio_cache_interceptor_hive_store
|
|
||||||
sha256: "449b36541216cb20543228081125ad2995eb9712ec35bd030d85663ea1761895"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.2.2"
|
|
||||||
dio_web_adapter:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: dio_web_adapter
|
|
||||||
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.1"
|
|
||||||
duration_picker:
|
duration_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -234,18 +210,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_map
|
name: flutter_map
|
||||||
sha256: "2ecb34619a4be19df6f40c2f8dce1591675b4eff7a6857bd8f533706977385da"
|
sha256: "391e7dc95cc3f5190748210a69d4cfeb5d8f84dcdfa9c3235d0a9d7742ccb3f8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.2"
|
version: "8.2.2"
|
||||||
flutter_map_cache:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_map_cache
|
|
||||||
sha256: "5b30c9b0d36315a22f4ee070737104a6017e7ff990e8addc8128ba81786e03ef"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.5.2"
|
|
||||||
flutter_map_compass:
|
flutter_map_compass:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -292,10 +260,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
|
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.6.0"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -512,14 +480,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.8"
|
||||||
polylabel:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: polylabel
|
|
||||||
sha256: "41b9099afb2aa6c1730bdd8a0fab1400d287694ec7615dd8516935fa3144214b"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.1"
|
|
||||||
posix:
|
posix:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -35,7 +35,7 @@ dependencies:
|
|||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.6
|
cupertino_icons: ^1.0.6
|
||||||
flutter_map: ^7.0.2
|
flutter_map: ^8.2.0
|
||||||
http: ^1.2.1
|
http: ^1.2.1
|
||||||
flutter_dotenv: ^5.1.0
|
flutter_dotenv: ^5.1.0
|
||||||
shared_preferences: ^2.2.2
|
shared_preferences: ^2.2.2
|
||||||
@ -50,8 +50,6 @@ dependencies:
|
|||||||
fast_immutable_collections: ^10.1.2
|
fast_immutable_collections: ^10.1.2
|
||||||
flutter_map_compass: ^1.0.1
|
flutter_map_compass: ^1.0.1
|
||||||
# flutter_map_location_marker: ^8.0.8
|
# flutter_map_location_marker: ^8.0.8
|
||||||
flutter_map_cache: ^1.5.0
|
|
||||||
dio_cache_interceptor_hive_store: ^3.2.2
|
|
||||||
flutter_map_math: ^0.1.7
|
flutter_map_math: ^0.1.7
|
||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
get_it: ^7.6.7
|
get_it: ^7.6.7
|
||||||
|
|||||||
@ -1,30 +1,34 @@
|
|||||||
// This is a basic Flutter widget test.
|
import 'dart:io';
|
||||||
//
|
|
||||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
|
||||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
|
||||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
|
||||||
// tree, read text, and verify that the values of widget properties are correct.
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||||
import 'package:ot_viewer_app/main.dart';
|
import 'package:ot_viewer_app/main.dart';
|
||||||
|
import 'package:ot_viewer_app/refresh_cubit.dart';
|
||||||
|
import 'package:ot_viewer_app/settings_page.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
setUpAll(() async {
|
||||||
// Build our app and trigger a frame.
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
await tester.pumpWidget(const MyApp());
|
final tempDir = await Directory.systemTemp.createTemp('ot_viewer_test');
|
||||||
|
HydratedBloc.storage = await HydratedStorage.build(
|
||||||
|
storageDirectory: tempDir,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// Verify that our counter starts at 0.
|
testWidgets('App shell renders with map and settings tabs',
|
||||||
expect(find.text('0'), findsOneWidget);
|
(WidgetTester tester) async {
|
||||||
expect(find.text('1'), findsNothing);
|
await tester.pumpWidget(
|
||||||
|
MultiBlocProvider(
|
||||||
|
providers: [
|
||||||
|
BlocProvider(create: (context) => SettingsCubit()),
|
||||||
|
BlocProvider(create: (context) => RefreshCubit()),
|
||||||
|
],
|
||||||
|
child: const MyApp(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// Tap the '+' icon and trigger a frame.
|
expect(find.text('Map'), findsOneWidget);
|
||||||
await tester.tap(find.byIcon(Icons.add));
|
expect(find.text('Settings'), findsOneWidget);
|
||||||
await tester.pump();
|
|
||||||
|
|
||||||
// Verify that our counter has incremented.
|
|
||||||
expect(find.text('0'), findsNothing);
|
|
||||||
expect(find.text('1'), findsOneWidget);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user