mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-03-25 08:24:43 +01:00
Add a place picker
This commit is contained in:
parent
0c78419c72
commit
92286372cb
14 changed files with 368 additions and 14 deletions
|
@ -1521,6 +1521,7 @@
|
||||||
"@customizeButtonsUnsupportedWarning": {
|
"@customizeButtonsUnsupportedWarning": {
|
||||||
"description": "Some button can't be removed. This message will be shown instead when user try to do so"
|
"description": "Some button can't be removed. This message will be shown instead when user try to do so"
|
||||||
},
|
},
|
||||||
|
"placePickerTitle": "Pick a place",
|
||||||
|
|
||||||
"errorUnauthenticated": "Unauthenticated access. Please sign-in again if the problem continues",
|
"errorUnauthenticated": "Unauthenticated access. Please sign-in again if the problem continues",
|
||||||
"@errorUnauthenticated": {
|
"@errorUnauthenticated": {
|
||||||
|
|
|
@ -268,6 +268,7 @@
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning",
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle",
|
||||||
"errorUnauthenticated",
|
"errorUnauthenticated",
|
||||||
"errorDisconnected",
|
"errorDisconnected",
|
||||||
"errorLocked",
|
"errorLocked",
|
||||||
|
@ -286,7 +287,8 @@
|
||||||
"livePhotoTooltip",
|
"livePhotoTooltip",
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning"
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
"de": [
|
"de": [
|
||||||
|
@ -297,7 +299,8 @@
|
||||||
"livePhotoTooltip",
|
"livePhotoTooltip",
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning"
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
"el": [
|
"el": [
|
||||||
|
@ -453,7 +456,8 @@
|
||||||
"livePhotoTooltip",
|
"livePhotoTooltip",
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning"
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
"es": [
|
"es": [
|
||||||
|
@ -464,7 +468,8 @@
|
||||||
"livePhotoTooltip",
|
"livePhotoTooltip",
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning"
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
"fi": [
|
"fi": [
|
||||||
|
@ -511,7 +516,8 @@
|
||||||
"livePhotoTooltip",
|
"livePhotoTooltip",
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning"
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
"fr": [
|
"fr": [
|
||||||
|
@ -558,7 +564,8 @@
|
||||||
"livePhotoTooltip",
|
"livePhotoTooltip",
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning"
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
"it": [
|
"it": [
|
||||||
|
@ -610,7 +617,8 @@
|
||||||
"livePhotoTooltip",
|
"livePhotoTooltip",
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning"
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
"nl": [
|
"nl": [
|
||||||
|
@ -999,6 +1007,7 @@
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning",
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle",
|
||||||
"errorUnauthenticated",
|
"errorUnauthenticated",
|
||||||
"errorDisconnected",
|
"errorDisconnected",
|
||||||
"errorLocked",
|
"errorLocked",
|
||||||
|
@ -1057,7 +1066,8 @@
|
||||||
"livePhotoTooltip",
|
"livePhotoTooltip",
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning"
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
"pt": [
|
"pt": [
|
||||||
|
@ -1124,7 +1134,8 @@
|
||||||
"livePhotoTooltip",
|
"livePhotoTooltip",
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning"
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ru": [
|
"ru": [
|
||||||
|
@ -1171,7 +1182,8 @@
|
||||||
"livePhotoTooltip",
|
"livePhotoTooltip",
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning"
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
"tr": [
|
"tr": [
|
||||||
|
@ -1182,7 +1194,8 @@
|
||||||
"livePhotoTooltip",
|
"livePhotoTooltip",
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning"
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
"zh": [
|
"zh": [
|
||||||
|
@ -1260,7 +1273,8 @@
|
||||||
"livePhotoTooltip",
|
"livePhotoTooltip",
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning"
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
"zh_Hant": [
|
"zh_Hant": [
|
||||||
|
@ -1432,6 +1446,7 @@
|
||||||
"livePhotoTooltip",
|
"livePhotoTooltip",
|
||||||
"dragAndDropRearrangeButtons",
|
"dragAndDropRearrangeButtons",
|
||||||
"customizeCollectionsNavBarDescription",
|
"customizeCollectionsNavBarDescription",
|
||||||
"customizeButtonsUnsupportedWarning"
|
"customizeButtonsUnsupportedWarning",
|
||||||
|
"placePickerTitle"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import 'package:nc_photos/widget/image_enhancer.dart';
|
||||||
import 'package:nc_photos/widget/local_file_viewer.dart';
|
import 'package:nc_photos/widget/local_file_viewer.dart';
|
||||||
import 'package:nc_photos/widget/map_browser.dart';
|
import 'package:nc_photos/widget/map_browser.dart';
|
||||||
import 'package:nc_photos/widget/people_browser.dart';
|
import 'package:nc_photos/widget/people_browser.dart';
|
||||||
|
import 'package:nc_photos/widget/place_picker/place_picker.dart';
|
||||||
import 'package:nc_photos/widget/places_browser.dart';
|
import 'package:nc_photos/widget/places_browser.dart';
|
||||||
import 'package:nc_photos/widget/result_viewer.dart';
|
import 'package:nc_photos/widget/result_viewer.dart';
|
||||||
import 'package:nc_photos/widget/root_picker.dart';
|
import 'package:nc_photos/widget/root_picker.dart';
|
||||||
|
@ -213,6 +214,7 @@ class _WrappedAppState extends State<_WrappedApp>
|
||||||
ArchiveBrowser.routeName: ArchiveBrowser.buildRoute,
|
ArchiveBrowser.routeName: ArchiveBrowser.buildRoute,
|
||||||
TrustedCertManager.routeName: TrustedCertManager.buildRoute,
|
TrustedCertManager.routeName: TrustedCertManager.buildRoute,
|
||||||
MapBrowser.routeName: MapBrowser.buildRoute,
|
MapBrowser.routeName: MapBrowser.buildRoute,
|
||||||
|
PlacePicker.routeName: PlacePicker.buildRoute,
|
||||||
};
|
};
|
||||||
|
|
||||||
Route<dynamic>? _onGenerateRoute(RouteSettings settings) {
|
Route<dynamic>? _onGenerateRoute(RouteSettings settings) {
|
||||||
|
|
21
app/lib/widget/place_picker/bloc.dart
Normal file
21
app/lib/widget/place_picker/bloc.dart
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
part of 'place_picker.dart';
|
||||||
|
|
||||||
|
@npLog
|
||||||
|
class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
||||||
|
_Bloc() : super(_State.init()) {
|
||||||
|
on<_SetPosition>(_onSetPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get tag => _log.fullName;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool Function(dynamic, dynamic)? get shouldLog => (currentState, nextState) {
|
||||||
|
return currentState.position == nextState.position;
|
||||||
|
};
|
||||||
|
|
||||||
|
void _onSetPosition(_SetPosition ev, _Emitter emit) {
|
||||||
|
// _log.info(ev);
|
||||||
|
emit(state.copyWith(position: ev.value));
|
||||||
|
}
|
||||||
|
}
|
93
app/lib/widget/place_picker/place_picker.dart
Normal file
93
app/lib/widget/place_picker/place_picker.dart
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import 'package:copy_with/copy_with.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:nc_photos/app_localizations.dart';
|
||||||
|
import 'package:nc_photos/bloc_util.dart';
|
||||||
|
import 'package:nc_photos/controller/pref_controller.dart';
|
||||||
|
import 'package:nc_photos/stream_util.dart';
|
||||||
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
|
import 'package:np_gps_map/np_gps_map.dart';
|
||||||
|
import 'package:to_string/to_string.dart';
|
||||||
|
|
||||||
|
part 'bloc.dart';
|
||||||
|
part 'place_picker.g.dart';
|
||||||
|
part 'state_event.dart';
|
||||||
|
|
||||||
|
class PlacePicker extends StatelessWidget {
|
||||||
|
static const routeName = "/place-picker";
|
||||||
|
|
||||||
|
static Route buildRoute(RouteSettings settings) =>
|
||||||
|
MaterialPageRoute<CameraPosition>(
|
||||||
|
builder: (_) => const PlacePicker(),
|
||||||
|
settings: settings,
|
||||||
|
);
|
||||||
|
|
||||||
|
const PlacePicker({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => _Bloc(),
|
||||||
|
child: const _WrappedPlacePicker(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@npLog
|
||||||
|
class _WrappedPlacePicker extends StatelessWidget {
|
||||||
|
const _WrappedPlacePicker();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(L10n.global().placePickerTitle),
|
||||||
|
leading: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
final position = context.state.position;
|
||||||
|
_log.info("[build] Position picked: $position");
|
||||||
|
Navigator.of(context).pop(position);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.check_outlined),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: const _BodyView(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BodyView extends StatelessWidget {
|
||||||
|
const _BodyView();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final prevPosition =
|
||||||
|
context.read<PrefController>().mapBrowserPrevPositionValue;
|
||||||
|
return ValueStreamBuilderEx<GpsMapProvider>(
|
||||||
|
stream: context.read<PrefController>().gpsMapProvider,
|
||||||
|
builder: StreamWidgetBuilder.value(
|
||||||
|
(context, gpsMapProvider) => PlacePickerView(
|
||||||
|
providerHint: gpsMapProvider,
|
||||||
|
initialPosition: prevPosition ?? const MapCoord(0, 0),
|
||||||
|
initialZoom: prevPosition == null ? 2.5 : 10,
|
||||||
|
onCameraMove: (position) {
|
||||||
|
context.addEvent(_SetPosition(position));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
|
||||||
|
// typedef _BlocListener = BlocListener<_Bloc, _State>;
|
||||||
|
// typedef _BlocListenerT<T> = BlocListenerT<_Bloc, _State, T>;
|
||||||
|
// typedef _BlocSelector<T> = BlocSelector<_Bloc, _State, T>;
|
||||||
|
typedef _Emitter = Emitter<_State>;
|
||||||
|
|
||||||
|
extension on BuildContext {
|
||||||
|
_Bloc get bloc => read<_Bloc>();
|
||||||
|
_State get state => bloc.state;
|
||||||
|
void addEvent(_Event event) => bloc.add(event);
|
||||||
|
}
|
73
app/lib/widget/place_picker/place_picker.g.dart
Normal file
73
app/lib/widget/place_picker/place_picker.g.dart
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'place_picker.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// CopyWithLintRuleGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// ignore_for_file: library_private_types_in_public_api, duplicate_ignore
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// CopyWithGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
abstract class $_StateCopyWithWorker {
|
||||||
|
_State call({CameraPosition? position});
|
||||||
|
}
|
||||||
|
|
||||||
|
class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
||||||
|
_$_StateCopyWithWorkerImpl(this.that);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_State call({dynamic position = copyWithNull}) {
|
||||||
|
return _State(
|
||||||
|
position: position == copyWithNull
|
||||||
|
? that.position
|
||||||
|
: position as CameraPosition?);
|
||||||
|
}
|
||||||
|
|
||||||
|
final _State that;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension $_StateCopyWith on _State {
|
||||||
|
$_StateCopyWithWorker get copyWith => _$copyWith;
|
||||||
|
$_StateCopyWithWorker get _$copyWith => _$_StateCopyWithWorkerImpl(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// NpLogGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
extension _$_WrappedPlacePickerNpLog on _WrappedPlacePicker {
|
||||||
|
// ignore: unused_element
|
||||||
|
Logger get _log => log;
|
||||||
|
|
||||||
|
static final log =
|
||||||
|
Logger("widget.place_picker.place_picker._WrappedPlacePicker");
|
||||||
|
}
|
||||||
|
|
||||||
|
extension _$_BlocNpLog on _Bloc {
|
||||||
|
// ignore: unused_element
|
||||||
|
Logger get _log => log;
|
||||||
|
|
||||||
|
static final log = Logger("widget.place_picker.place_picker._Bloc");
|
||||||
|
}
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// ToStringGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
extension _$_StateToString on _State {
|
||||||
|
String _$toString() {
|
||||||
|
// ignore: unnecessary_string_interpolations
|
||||||
|
return "_State {position: $position}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension _$_SetPositionToString on _SetPosition {
|
||||||
|
String _$toString() {
|
||||||
|
// ignore: unnecessary_string_interpolations
|
||||||
|
return "_SetPosition {value: $value}";
|
||||||
|
}
|
||||||
|
}
|
28
app/lib/widget/place_picker/state_event.dart
Normal file
28
app/lib/widget/place_picker/state_event.dart
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
part of 'place_picker.dart';
|
||||||
|
|
||||||
|
@genCopyWith
|
||||||
|
@toString
|
||||||
|
class _State {
|
||||||
|
const _State({
|
||||||
|
this.position,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory _State.init() => const _State();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => _$toString();
|
||||||
|
|
||||||
|
final CameraPosition? position;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _Event {}
|
||||||
|
|
||||||
|
@toString
|
||||||
|
class _SetPosition implements _Event {
|
||||||
|
const _SetPosition(this.value);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => _$toString();
|
||||||
|
|
||||||
|
final CameraPosition value;
|
||||||
|
}
|
|
@ -3,4 +3,6 @@ library np_gps_map;
|
||||||
export 'src/gps_map.dart';
|
export 'src/gps_map.dart';
|
||||||
export 'src/interactive_map.dart';
|
export 'src/interactive_map.dart';
|
||||||
export 'src/map_coord.dart';
|
export 'src/map_coord.dart';
|
||||||
|
export 'src/place_picker.dart';
|
||||||
|
export 'src/type.dart';
|
||||||
export 'src/util.dart' show initGpsMap;
|
export 'src/util.dart' show initGpsMap;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'package:np_gps_map/src/gps_map.dart';
|
||||||
import 'package:np_gps_map/src/interactive_map/google.dart';
|
import 'package:np_gps_map/src/interactive_map/google.dart';
|
||||||
import 'package:np_gps_map/src/interactive_map/osm.dart';
|
import 'package:np_gps_map/src/interactive_map/osm.dart';
|
||||||
import 'package:np_gps_map/src/map_coord.dart';
|
import 'package:np_gps_map/src/map_coord.dart';
|
||||||
|
import 'package:np_gps_map/src/type.dart';
|
||||||
import 'package:np_gps_map/src/util.dart';
|
import 'package:np_gps_map/src/util.dart';
|
||||||
import 'package:np_platform_util/np_platform_util.dart';
|
import 'package:np_platform_util/np_platform_util.dart';
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ class InteractiveMap extends StatelessWidget {
|
||||||
this.googleClusterBuilder,
|
this.googleClusterBuilder,
|
||||||
this.contentPadding,
|
this.contentPadding,
|
||||||
this.onMapCreated,
|
this.onMapCreated,
|
||||||
|
this.onCameraMove,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -45,6 +47,7 @@ class InteractiveMap extends StatelessWidget {
|
||||||
clusterBuilder: osmClusterBuilder,
|
clusterBuilder: osmClusterBuilder,
|
||||||
contentPadding: contentPadding,
|
contentPadding: contentPadding,
|
||||||
onMapCreated: onMapCreated,
|
onMapCreated: onMapCreated,
|
||||||
|
onCameraMove: onCameraMove,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return GoogleInteractiveMap(
|
return GoogleInteractiveMap(
|
||||||
|
@ -55,6 +58,7 @@ class InteractiveMap extends StatelessWidget {
|
||||||
clusterBuilder: googleClusterBuilder,
|
clusterBuilder: googleClusterBuilder,
|
||||||
contentPadding: contentPadding,
|
contentPadding: contentPadding,
|
||||||
onMapCreated: onMapCreated,
|
onMapCreated: onMapCreated,
|
||||||
|
onCameraMove: onCameraMove,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,4 +74,5 @@ class InteractiveMap extends StatelessWidget {
|
||||||
final OsmClusterBuilder? osmClusterBuilder;
|
final OsmClusterBuilder? osmClusterBuilder;
|
||||||
final EdgeInsets? contentPadding;
|
final EdgeInsets? contentPadding;
|
||||||
final void Function(InteractiveMapController controller)? onMapCreated;
|
final void Function(InteractiveMapController controller)? onMapCreated;
|
||||||
|
final void Function(CameraPosition position)? onCameraMove;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,11 @@ import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_maps_cluster_manager/google_maps_cluster_manager.dart';
|
import 'package:google_maps_cluster_manager/google_maps_cluster_manager.dart';
|
||||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
|
import 'package:latlong2/latlong.dart' as type;
|
||||||
import 'package:np_common/object_util.dart';
|
import 'package:np_common/object_util.dart';
|
||||||
import 'package:np_gps_map/src/interactive_map.dart';
|
import 'package:np_gps_map/src/interactive_map.dart';
|
||||||
import 'package:np_gps_map/src/map_coord.dart';
|
import 'package:np_gps_map/src/map_coord.dart';
|
||||||
|
import 'package:np_gps_map/src/type.dart' as type;
|
||||||
|
|
||||||
typedef GoogleClusterBuilder = FutureOr<BitmapDescriptor> Function(
|
typedef GoogleClusterBuilder = FutureOr<BitmapDescriptor> Function(
|
||||||
BuildContext context, List<DataPoint> dataPoints);
|
BuildContext context, List<DataPoint> dataPoints);
|
||||||
|
@ -20,6 +22,7 @@ class GoogleInteractiveMap extends StatefulWidget {
|
||||||
this.onClusterTap,
|
this.onClusterTap,
|
||||||
this.contentPadding,
|
this.contentPadding,
|
||||||
this.onMapCreated,
|
this.onMapCreated,
|
||||||
|
this.onCameraMove,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -32,6 +35,7 @@ class GoogleInteractiveMap extends StatefulWidget {
|
||||||
final void Function(List<DataPoint> dataPoints)? onClusterTap;
|
final void Function(List<DataPoint> dataPoints)? onClusterTap;
|
||||||
final EdgeInsets? contentPadding;
|
final EdgeInsets? contentPadding;
|
||||||
final void Function(InteractiveMapController controller)? onMapCreated;
|
final void Function(InteractiveMapController controller)? onMapCreated;
|
||||||
|
final void Function(type.CameraPosition position)? onCameraMove;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GoogleInteractiveMapState extends State<GoogleInteractiveMap> {
|
class _GoogleInteractiveMapState extends State<GoogleInteractiveMap> {
|
||||||
|
@ -57,7 +61,17 @@ class _GoogleInteractiveMapState extends State<GoogleInteractiveMap> {
|
||||||
const CameraPosition(target: LatLng(0, 0)),
|
const CameraPosition(target: LatLng(0, 0)),
|
||||||
markers: _markers,
|
markers: _markers,
|
||||||
onMapCreated: _onMapCreated,
|
onMapCreated: _onMapCreated,
|
||||||
onCameraMove: _clusterManager.onCameraMove,
|
onCameraMove: (position) {
|
||||||
|
_clusterManager.onCameraMove(position);
|
||||||
|
widget.onCameraMove?.call(type.CameraPosition(
|
||||||
|
center: type.LatLng(
|
||||||
|
position.target.latitude,
|
||||||
|
position.target.longitude,
|
||||||
|
),
|
||||||
|
zoom: position.zoom,
|
||||||
|
rotation: position.bearing,
|
||||||
|
));
|
||||||
|
},
|
||||||
onCameraIdle: _clusterManager.updateMap,
|
onCameraIdle: _clusterManager.updateMap,
|
||||||
padding: widget.contentPadding ?? EdgeInsets.zero,
|
padding: widget.contentPadding ?? EdgeInsets.zero,
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,6 +8,7 @@ import 'package:latlong2/latlong.dart';
|
||||||
import 'package:np_common/object_util.dart';
|
import 'package:np_common/object_util.dart';
|
||||||
import 'package:np_gps_map/src/interactive_map.dart';
|
import 'package:np_gps_map/src/interactive_map.dart';
|
||||||
import 'package:np_gps_map/src/map_coord.dart';
|
import 'package:np_gps_map/src/map_coord.dart';
|
||||||
|
import 'package:np_gps_map/src/type.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
|
|
||||||
typedef OsmClusterBuilder = Widget Function(
|
typedef OsmClusterBuilder = Widget Function(
|
||||||
|
@ -23,6 +24,7 @@ class OsmInteractiveMap extends StatefulWidget {
|
||||||
this.onClusterTap,
|
this.onClusterTap,
|
||||||
this.contentPadding,
|
this.contentPadding,
|
||||||
this.onMapCreated,
|
this.onMapCreated,
|
||||||
|
this.onCameraMove,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -35,6 +37,7 @@ class OsmInteractiveMap extends StatefulWidget {
|
||||||
final void Function(List<DataPoint> dataPoints)? onClusterTap;
|
final void Function(List<DataPoint> dataPoints)? onClusterTap;
|
||||||
final EdgeInsets? contentPadding;
|
final EdgeInsets? contentPadding;
|
||||||
final void Function(InteractiveMapController controller)? onMapCreated;
|
final void Function(InteractiveMapController controller)? onMapCreated;
|
||||||
|
final void Function(CameraPosition position)? onCameraMove;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _OsmInteractiveMapState extends State<OsmInteractiveMap> {
|
class _OsmInteractiveMapState extends State<OsmInteractiveMap> {
|
||||||
|
@ -47,6 +50,11 @@ class _OsmInteractiveMapState extends State<OsmInteractiveMap> {
|
||||||
widget.onMapCreated?.call(_parentController!);
|
widget.onMapCreated?.call(_parentController!);
|
||||||
_subscriptions.add(_controller.mapEventStream.listen((ev) {
|
_subscriptions.add(_controller.mapEventStream.listen((ev) {
|
||||||
_mapRotationRadSubject.add(ev.camera.rotationRad);
|
_mapRotationRadSubject.add(ev.camera.rotationRad);
|
||||||
|
widget.onCameraMove?.call(CameraPosition(
|
||||||
|
center: ev.camera.center,
|
||||||
|
zoom: ev.camera.zoom,
|
||||||
|
rotation: (360 - ev.camera.rotation) % 360,
|
||||||
|
));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
46
np_gps_map/lib/src/place_picker.dart
Normal file
46
np_gps_map/lib/src/place_picker.dart
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:np_gps_map/src/gps_map.dart';
|
||||||
|
import 'package:np_gps_map/src/interactive_map.dart';
|
||||||
|
import 'package:np_gps_map/src/map_coord.dart';
|
||||||
|
import 'package:np_gps_map/src/type.dart';
|
||||||
|
|
||||||
|
class PlacePickerView extends StatelessWidget {
|
||||||
|
const PlacePickerView({
|
||||||
|
super.key,
|
||||||
|
required this.providerHint,
|
||||||
|
this.initialPosition,
|
||||||
|
this.initialZoom,
|
||||||
|
this.contentPadding,
|
||||||
|
this.onCameraMove,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
InteractiveMap(
|
||||||
|
providerHint: providerHint,
|
||||||
|
initialPosition: initialPosition,
|
||||||
|
initialZoom: initialZoom,
|
||||||
|
contentPadding: contentPadding,
|
||||||
|
onCameraMove: onCameraMove,
|
||||||
|
),
|
||||||
|
Positioned.fill(
|
||||||
|
child: Transform.translate(
|
||||||
|
// 48(height) / 2
|
||||||
|
offset: const Offset(0, -24),
|
||||||
|
child: Center(
|
||||||
|
child: Image.asset("packages/np_gps_map/assets/gps_map_pin.png"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final GpsMapProvider providerHint;
|
||||||
|
final MapCoord? initialPosition;
|
||||||
|
final double? initialZoom;
|
||||||
|
final EdgeInsets? contentPadding;
|
||||||
|
final void Function(CameraPosition position)? onCameraMove;
|
||||||
|
}
|
45
np_gps_map/lib/src/type.dart
Normal file
45
np_gps_map/lib/src/type.dart
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:latlong2/latlong.dart';
|
||||||
|
import 'package:np_common/type.dart';
|
||||||
|
|
||||||
|
class CameraPosition with EquatableMixin {
|
||||||
|
const CameraPosition({
|
||||||
|
required this.center,
|
||||||
|
required this.zoom,
|
||||||
|
required this.rotation,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory CameraPosition.fromJson(JsonObj json) {
|
||||||
|
return CameraPosition(
|
||||||
|
center: LatLng.fromJson(json["center"]),
|
||||||
|
zoom: json["zoom"],
|
||||||
|
rotation: json["rotation"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => "CameraPosition {"
|
||||||
|
"center: $center, "
|
||||||
|
"zoom: $zoom, "
|
||||||
|
"rotation: $rotation, "
|
||||||
|
"}";
|
||||||
|
|
||||||
|
JsonObj toJson() {
|
||||||
|
return {
|
||||||
|
"center": center.toJson(),
|
||||||
|
"zoom": zoom,
|
||||||
|
"rotation": rotation,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [center, zoom, rotation];
|
||||||
|
|
||||||
|
final LatLng center;
|
||||||
|
final double zoom;
|
||||||
|
// The camera's bearing in degrees, measured clockwise from north.
|
||||||
|
//
|
||||||
|
// A bearing of 0.0, the default, means the camera points north.
|
||||||
|
// A bearing of 90.0 means the camera points east.
|
||||||
|
final double rotation;
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ environment:
|
||||||
flutter: ">=3.19.0"
|
flutter: ">=3.19.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
equatable: ^2.0.5
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_map: ^6.1.0
|
flutter_map: ^6.1.0
|
||||||
|
|
Loading…
Add table
Reference in a new issue