MapSurvey - refraktorálás

This commit is contained in:
torok.istvan 2026-05-24 14:50:31 +02:00
parent 53888c9dcb
commit ce8b539be3
5 changed files with 209 additions and 355 deletions

View File

@ -64,8 +64,8 @@ class MapSurveyController extends GetxController {
RxDouble get gpsLongitudeError => _gnss.longitudeError;
RxDouble get gpsAltitudeError => _gnss.altitudeError;
Rx<DateTime> get gpsDateTime => _gnss.gpsDateTime;
RxBool get gpsIsConnected =>
(_gnss.connectionState.value == GnssConnectionState.connected).obs;
bool get gpsIsConnected =>
_gnss.connectionState.value == GnssConnectionState.connected;
// NTRIP állapot
RxBool get ntripIsConnected => _ntrip.isConnected;
@ -85,6 +85,9 @@ class MapSurveyController extends GetxController {
Rx<Eov> eov = Eov(0, 0).obs;
Rx<double?> eovHeight = (0.0 as double?).obs;
final eovY = 0.0.obs;
final eovX = 0.0.obs;
// DMS formátum
RxInt latDegree = 0.obs;
RxInt latMin = 0.obs;
@ -101,17 +104,18 @@ class MapSurveyController extends GetxController {
RxBool isMapMoveToCenter = true.obs;
RxBool mapIsInitialized = false.obs;
late final MapController mapController;
final MapController mapController = MapController();
final currentLocationMarker = <Marker>[];
final pointNotesMarker = <Marker>[];
final pointsToMeasureMarker = <Marker>[];
final pointsToMeasureLabel = <PolyWidget>[];
final pointsToMeasureDropDownMenuItem = <DropdownMenuItem<int>>[];
final currentLocationMarker = <Marker>[].obs;
final pointNotesMarker = <Marker>[].obs;
final pointsToMeasureMarker = <Marker>[].obs;
final pointsToMeasureLabel = <PolyWidget>[].obs;
final pointsToMeasureDropDownMenuItem = <DropdownMenuItem<int>>[].obs;
// Pont adatok
List<PointToMeasure> pointsToMeasure = [];
List<PointWithDescription> pointWithDescriptionList = [];
final RxList<PointToMeasure> pointsToMeasure = <PointToMeasure>[].obs;
final RxList<PointWithDescription> pointWithDescriptionList =
<PointWithDescription>[].obs;
RxInt pointsToMeasureSelectedValue = (-1).obs;
RxDouble distance = 0.0.obs;
@ -149,22 +153,17 @@ class MapSurveyController extends GetxController {
//
@override
void onInit() async {
void onInit() {
super.onInit();
_initAsync();
}
Future<void> _initAsync() async {
prefs = await SharedPreferences.getInstance();
geoidGrid = await GeoidGrid.load('assets/Grids/geoid_eht2014.gtx');
mapController = MapController();
// NTRIP RTCM adat GNSS vevő
NtripService.to.onRtcmData = (data) {
GnssService.to.sendToReceiver(data);
};
// GnssService pozíció változás EOV, marker, NTRIP GGA
_gnssUpdateSub = _gnss.onDataUpdated.listen((_) {
_onGnssUpdate();
});
NtripService.to.onRtcmData = (data) => GnssService.to.sendToReceiver(data);
_gnssUpdateSub = _gnss.onDataUpdated.listen((_) => _onGnssUpdate());
// Supabase realtime
_supaChannel = Supabase.instance.client
@ -193,18 +192,19 @@ class MapSurveyController extends GetxController {
}
@override
void onClose() async {
super.onClose();
void onClose() {
_phoneLocationSub?.cancel();
_gnssUpdateSub?.cancel();
await _supaChannel?.unsubscribe();
final f = _supaChannel?.unsubscribe();
if (f != null) unawaited(f);
pointIdController.dispose();
pointDescriptionController.dispose();
gpsHeightController.dispose();
pointPrefixController.dispose();
pointPostfixController.dispose();
super.onClose();
}
//
@ -220,7 +220,11 @@ class MapSurveyController extends GetxController {
final sep = _gnss.geoidSeparation.value;
// EOV konverzió
eov.value = ConvertCoordinate.ConvertWgsToEov(lat, lon);
final converted = ConvertCoordinate.ConvertWgsToEov(lat, lon);
eov.value = converted;
eovY.value = converted.Y.toDouble();
eovX.value = converted.X.toDouble();
eovHeight.value = geoidGrid.toEovHeight(lat, lon, alt, sep);
// DMS
@ -254,10 +258,6 @@ class MapSurveyController extends GetxController {
);
}
//
// Telefon GPS fallback
//
//
// Térkép vezérlők
//
@ -288,21 +288,23 @@ class MapSurveyController extends GetxController {
}
void _updateCurrentLocationMarker() {
currentLocationMarker.clear();
currentLocationMarker.add(Marker(
point: LatLng(currentLatitude.value, currentLongitude.value),
width: 15.0,
height: 15.0,
child: Container(
currentLocationMarker.assignAll([
Marker(
point: LatLng(currentLatitude.value, currentLongitude.value),
width: 15.0,
height: 15.0,
decoration: BoxDecoration(
color: getCurrentLocationMarkerColor(_gnss.gpsQuality.value),
shape: BoxShape.circle,
border: Border.all(width: 1.5, color: Colors.white),
child: Container(
width: 15.0,
height: 15.0,
decoration: BoxDecoration(
color: getCurrentLocationMarkerColor(_gnss.gpsQuality.value),
shape: BoxShape.circle,
border: Border.all(width: 1.5, color: Colors.white),
),
),
),
));
)
]);
if (isMapMoveToCenter.value) {
mapController.move(
LatLng(currentLatitude.value, currentLongitude.value),
@ -622,7 +624,7 @@ class MapSurveyController extends GetxController {
//
double calculateDistance(LatLng start, LatLng end) {
const r = 6371.0;
const r = 6371000.0;
final lat1 = start.latitude * (pi / 180);
final lon1 = start.longitude * (pi / 180);
final lat2 = end.latitude * (pi / 180);

View File

@ -1,13 +1,8 @@
import 'dart:io';
import 'dart:math';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map_polywidget/flutter_map_polywidget.dart';
import 'package:get/get.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:rive/rive.dart';
import 'package:terepi_seged/pages/map_survey/presentations/controllers/map_survey_controller.dart';
import 'package:terepi_seged/pages/map_survey/presentations/views/settings_dialog.dart';
import 'package:terepi_seged/utils/rive_utils.dart';
@ -24,12 +19,29 @@ class MapSurveyView extends GetView<MapSurveyController> {
@override
Widget build(BuildContext context) {
return Stack(children: [
const SharedMapWidget(),
SharedMapWidget(
mapController: controller.mapController,
currentZoom: controller.currentZoom,
onZoomIn: controller.mapZoomIn,
onZoomOut: controller.mapZoomOut,
onCenterOnGps: controller.isMapMoveToCenter,
),
Positioned(
top: 8,
right: 60,
left: 8,
child: CoordinatePanel.fromController(controller),
child: CoordinatePanel(
eovY: controller.eovY,
eovX: controller.eovX,
horError: controller.gpsLatitudeError,
vertError: controller.gpsAltitudeError,
altitudeMsl: controller.gpsAltitude,
geoidSeparation: controller.gpsGeoidSeparation,
ntripConnected: controller.ntripIsConnected,
ntripBytes: controller.ntripReceivedData,
ntripPackets: controller.ntripDataPacketNumbers,
ggaPackets: controller.ggaSenDataPacketNumber,
),
),
// Positioned(top: 8, left: 0, right: 0, child: _ModeSelector()),
// Positioned(

View File

@ -23,6 +23,8 @@ class ShellView extends GetView<ShellController> {
@override
Widget build(BuildContext context) {
return Scaffold(
extendBody: true,
extendBodyBehindAppBar: false,
appBar: AppBar(
// Cím reaktívan frissül tab váltáskor
title: Obx(() => Text(controller.currentTitle)),

View File

@ -37,8 +37,8 @@ class NtripService extends GetxService {
final host = '84.206.45.44'.obs; // gnssnet.hu IP
final port = 2101.obs;
final mountpoint = 'SGO_RTK3.2'.obs;
final username = ''.obs;
final password = ''.obs;
final username = 'elgi03'.obs;
final password = 'StEfan14'.obs;
// UI controllerek (beállítás dialóghoz)
final hostController = TextEditingController();
@ -228,8 +228,8 @@ class NtripService extends GetxService {
host.value = prefs.getString('ntrip_host') ?? '84.206.45.44';
port.value = prefs.getInt('ntrip_port') ?? 2101;
mountpoint.value = prefs.getString('ntrip_mountpoint') ?? 'SGO_RTK3.2';
username.value = prefs.getString('ntrip_username') ?? '';
password.value = prefs.getString('ntrip_password') ?? '';
username.value = prefs.getString('ntrip_username') ?? 'elgi01';
password.value = prefs.getString('ntrip_password') ?? 'StEfan14';
}
void _syncControllersFromValues() {

View File

@ -1,264 +1,102 @@
// lib/widgets/shared_map_widget.dart
import 'dart:math' as math;
import 'dart:math';
// lib/widgets/shared_map_widgets.dart
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:get/get.dart';
import 'package:latlong2/latlong.dart';
import '../services/gnss/gnss_service.dart';
import '../services/coord_converter_service.dart';
import '../pages/map_survey/presentations/controllers/map_survey_controller.dart';
// SharedMapWidget
class SharedMapWidget extends StatefulWidget {
/// Extra flutter_map rétegek (pl. PolylineLayer, PolygonLayer).
final List<Widget> extraLayers;
/// Külső MapController ha az oldal saját maga is mozgatja a térképet.
final MapController? mapController;
/// Hosszú nyomás callback (terepbejárás pont hozzáadáshoz).
class SharedMapWidget extends StatelessWidget {
final MapController mapController;
final List<Widget> layers;
final void Function(TapPosition, LatLng)? onLongPress;
final void Function(MapCamera, bool)? onPositionChanged;
/// Megjelenítendő vezérlők.
final MapControls controls;
/// Kezdeti zoom szint.
final LatLng initialCenter;
final double initialZoom;
final double minZoom;
final double maxZoom;
// Controller-owned state (csak megjelenítéshez)
final RxBool? isFollowing;
final RxBool? isNorthUp;
final RxDouble? currentZoom;
final RxDouble? currentRotationRad;
// Controller-owned commands
final VoidCallback? onZoomIn;
final VoidCallback? onZoomOut;
final VoidCallback? onCenterOnGps;
final VoidCallback? onResetNorth;
const SharedMapWidget({
super.key,
this.extraLayers = const [],
this.mapController,
required this.mapController,
this.layers = const [],
this.onLongPress,
this.onPositionChanged,
this.controls = const MapControls(),
this.initialCenter = const LatLng(47.5, 19.0),
this.initialZoom = 18.0,
this.minZoom = 3.0,
this.maxZoom = 25.0,
this.isFollowing,
this.isNorthUp,
this.currentZoom,
this.currentRotationRad,
this.onZoomIn,
this.onZoomOut,
this.onCenterOnGps,
this.onResetNorth,
});
@override
State<SharedMapWidget> createState() => _SharedMapWidgetState();
}
class _SharedMapWidgetState extends State<SharedMapWidget> {
late final MapController _mapController;
// Reaktív belső állapot
final _isFollowing = true.obs; // GPS követés be/ki
final _currentZoom = 18.0.obs;
final _isNorthUp = true.obs; // forgó térkép vs. É-up
@override
void initState() {
super.initState();
_mapController = widget.mapController ?? MapController();
_currentZoom.value = widget.initialZoom;
}
@override
void dispose() {
// Csak akkor disposoljuk, ha belső controller
if (widget.mapController == null) {
_mapController.dispose();
}
super.dispose();
}
// GPS pozíció követése
void _onPositionChanged(MapCamera camera, bool hasGesture) {
_currentZoom.value = camera.zoom;
// Ha a felhasználó manuálisan mozgatja kikapcsol a követés
if (hasGesture && _isFollowing.value) {
_isFollowing.value = false;
}
}
void _centerOnPosition() {
final gnss = GnssService.to;
if (gnss.latitude.value == 0) return;
_mapController.move(
LatLng(gnss.latitude.value, gnss.longitude.value),
_currentZoom.value,
);
_isFollowing.value = true;
}
void _zoomIn() =>
_mapController.move(_mapController.camera.center, _currentZoom.value + 1);
void _zoomOut() =>
_mapController.move(_mapController.camera.center, _currentZoom.value - 1);
void _resetNorth() {
_mapController.rotate(0);
_isNorthUp.value = true;
}
// GPS stream térkép mozgatás
@override
Widget build(BuildContext context) {
return Obx(() {
final gnss = GnssService.to;
final lat = gnss.latitude.value;
final lon = gnss.longitude.value;
if (_isFollowing.value && lat != 0 && lon != 0) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
_mapController.move(LatLng(lat, lon), _currentZoom.value);
});
}
return Stack(
children: [
// Térkép
FlutterMap(
mapController: _mapController,
options: MapOptions(
initialCenter:
lat != 0 ? LatLng(lat, lon) : const LatLng(47.5, 19.0),
initialZoom: widget.initialZoom,
maxZoom: 25,
minZoom: 3,
onLongPress: widget.onLongPress,
onPositionChanged: _onPositionChanged,
interactionOptions: const InteractionOptions(
flags: InteractiveFlag.all,
),
return Stack(
children: [
FlutterMap(
mapController: mapController,
options: MapOptions(
initialCenter: initialCenter,
initialZoom: initialZoom,
minZoom: minZoom,
maxZoom: maxZoom,
onLongPress: onLongPress,
onPositionChanged: onPositionChanged,
interactionOptions: const InteractionOptions(
flags: InteractiveFlag.all,
),
children: [
// 1. Alaptérkép
TileLayer(
urlTemplate:
'http://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}',
subdomains: const ['mt0', 'mt1', 'mt2', 'mt3'],
maxNativeZoom: 18,
),
// 2. Extra rétegek (terepbejárás elemei)
...widget.extraLayers,
// 3. Bemért pontok
// if (Get.isRegistered<MapSurveyController>())
// Obx(() => MarkerLayer(
// markers: _buildMeasuredPointMarkers(),
// )),
// 4. Kitűzési célpont + vonal
if (Get.isRegistered<MapSurveyController>())
Obx(() {
final lat = GnssService.to.latitude.value;
final lon = GnssService.to.longitude.value;
return _buildStakeoutLayer(lat, lon);
}),
// 5. GPS pozíció
Obx(() {
final lat = GnssService.to.latitude.value;
final lon = GnssService.to.longitude.value;
return MarkerLayer(
markers: lat == 0 && lon == 0
? []
: [_buildCurrentPositionMarker(lat, lon)],
);
}),
],
),
// Vezérlők
_MapControlsOverlay(
controls: widget.controls,
isFollowing: _isFollowing,
isNorthUp: _isNorthUp,
currentZoom: _currentZoom,
onZoomIn: _zoomIn,
onZoomOut: _zoomOut,
onCenterOnGps: _centerOnPosition,
onResetNorth: _resetNorth,
),
// Zoom szint jelzés (opcionális)
if (widget.controls.showZoomLevel)
Positioned(
bottom: 8,
left: 8,
child: Obx(() => _ZoomLabel(_currentZoom.value)),
children: [
TileLayer(
urlTemplate:
'http://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}',
subdomains: const ['mt0', 'mt1', 'mt2', 'mt3'],
maxNativeZoom: 18,
),
],
);
});
}
// Marker builder metódusok
List<Marker> _buildMeasuredPointMarkers() {
if (!Get.isRegistered<MapSurveyController>()) return [];
return MapSurveyController.to.pointNotesMarker;
}
Widget _buildStakeoutLayer(double lat, double lon) {
if (!Get.isRegistered<MapSurveyController>())
return const SizedBox.shrink();
final ctrl = MapSurveyController.to;
if (ctrl.mode.value != MapSurveyMode.stakeout ||
ctrl.targetEovY.value == 0) {
return const SizedBox.shrink();
}
final wgs = CoordConverterService.to
.eovToWgsPoint(ctrl.targetEovY.value, ctrl.targetEovX.value);
final targetLatLng = LatLng(wgs.y, wgs.x);
final dx = ctrl.eov.value.X - ctrl.targetEovX.value;
final dy = ctrl.eov.value.Y - ctrl.targetEovY.value;
final dist = sqrt(dx * dx + dy * dy);
final onTarget = dist < 0.05;
return Stack(children: [
// Szaggatott vonal
PolylineLayer(polylines: [
Polyline(
points: [LatLng(lat, lon), targetLatLng],
color: Colors.orange.withOpacity(0.85),
strokeWidth: 2.5,
...layers,
],
),
]),
// Célpont marker
MarkerLayer(markers: [
Marker(
point: targetLatLng,
width: 130,
height: 72,
alignment: Alignment.bottomCenter,
child: _LabeledMarker(
label: ctrl.targetName.value,
icon: Icons.flag,
color: Colors.orange,
activeColor: onTarget ? Colors.green : null,
sublabel: onTarget ? '✓ Célponton' : '${dist.toStringAsFixed(3)} m',
_MapControlsOverlay(
controls: controls,
isFollowing: isFollowing,
isNorthUp: isNorthUp,
currentRotationRad: currentRotationRad,
onZoomIn: onZoomIn,
onZoomOut: onZoomOut,
onCenterOnGps: onCenterOnGps,
onResetNorth: onResetNorth,
),
if (controls.showZoomLevel && currentZoom != null)
Positioned(
bottom: 8,
left: 8,
child: Obx(() => _ZoomLabel(currentZoom!.value)),
),
),
]),
]);
}
Marker _buildCurrentPositionMarker(double lat, double lon) {
final color = switch (GnssService.to.gpsQuality.value) {
4 => Colors.green,
5 => Colors.lightGreen,
2 => Colors.blue,
1 => Colors.orange,
_ => Colors.grey,
};
return Marker(
point: LatLng(lat, lon),
width: 24,
height: 24,
child: _PulsingDot(color: color),
],
);
}
}
// Vezérlők konfigurációja
class MapControls {
final bool showZoomButtons;
final bool showFollowButton;
@ -274,7 +112,6 @@ class MapControls {
this.showCompass = false,
});
/// Terepbejárás módhoz nincs follow (rajzolás közben szabad mozgás)
const MapControls.fieldTrip()
: showZoomButtons = true,
showFollowButton = false,
@ -282,7 +119,6 @@ class MapControls {
showZoomLevel = true,
showCompass = false;
/// Navigáció módhoz minden vezérlő
const MapControls.navigation()
: showZoomButtons = true,
showFollowButton = true,
@ -290,7 +126,6 @@ class MapControls {
showZoomLevel = false,
showCompass = true;
/// Minimális csak zoom
const MapControls.minimal()
: showZoomButtons = true,
showFollowButton = false,
@ -299,23 +134,22 @@ class MapControls {
showCompass = false;
}
// Vezérlők overlay
class _MapControlsOverlay extends StatelessWidget {
final MapControls controls;
final RxBool isFollowing;
final RxBool isNorthUp;
final RxDouble currentZoom;
final VoidCallback onZoomIn;
final VoidCallback onZoomOut;
final VoidCallback onCenterOnGps;
final VoidCallback onResetNorth;
final RxBool? isFollowing;
final RxBool? isNorthUp;
final RxDouble? currentRotationRad;
final VoidCallback? onZoomIn;
final VoidCallback? onZoomOut;
final VoidCallback? onCenterOnGps;
final VoidCallback? onResetNorth;
const _MapControlsOverlay({
required this.controls,
required this.isFollowing,
required this.isNorthUp,
required this.currentZoom,
required this.currentRotationRad,
required this.onZoomIn,
required this.onZoomOut,
required this.onCenterOnGps,
@ -326,41 +160,37 @@ class _MapControlsOverlay extends StatelessWidget {
Widget build(BuildContext context) {
return Positioned(
right: 10,
bottom: 80, // BottomNav felett
bottom: 80,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Iránytű / É-ra forgat
if (controls.showNorthButton)
Obx(() => _ControlButton(
icon: Icons.navigation,
tooltip: 'Észak felfelé',
active: isNorthUp.value,
// A gomb elfordul ahogy a térkép forog vizuális jelzés
child: Transform.rotate(
angle: 0,
child: const Icon(Icons.navigation, size: 20),
),
onTap: onResetNorth,
)),
if (controls.showNorthButton && isNorthUp != null)
Obx(() {
final active = isNorthUp!.value;
final angle = currentRotationRad?.value ?? 0.0;
return _ControlButton(
tooltip: 'Észak felfelé',
active: active,
onTap: onResetNorth,
child: Transform.rotate(
angle: -angle,
child: const Icon(Icons.navigation, size: 20),
),
);
}),
if (controls.showNorthButton) const SizedBox(height: 6),
// GPS követés
if (controls.showFollowButton)
if (controls.showFollowButton && isFollowing != null)
Obx(() => _ControlButton(
tooltip: isFollowing.value
tooltip: isFollowing!.value
? 'GPS követés aktív'
: 'GPS követés kikapcsolva',
active: isFollowing.value,
icon:
isFollowing.value ? Icons.gps_fixed : Icons.gps_not_fixed,
active: isFollowing!.value,
icon: isFollowing!.value
? Icons.gps_fixed
: Icons.gps_not_fixed,
onTap: onCenterOnGps,
)),
if (controls.showFollowButton) const SizedBox(height: 6),
// Zoom gombok
if (controls.showZoomButtons) ...[
_ControlButton(
icon: Icons.add,
@ -385,7 +215,7 @@ class _ControlButton extends StatelessWidget {
final Widget? child;
final String tooltip;
final bool active;
final VoidCallback onTap;
final VoidCallback? onTap;
const _ControlButton({
this.icon,
@ -435,8 +265,6 @@ class _ControlButton extends StatelessWidget {
}
}
// Zoom szint label
class _ZoomLabel extends StatelessWidget {
final double zoom;
const _ZoomLabel(this.zoom);
@ -457,16 +285,15 @@ class _ZoomLabel extends StatelessWidget {
}
}
// Feliratozott marker
class _LabeledMarker extends StatelessWidget {
class LabeledMarker extends StatelessWidget {
final String label;
final IconData icon;
final Color color;
final Color? activeColor;
final String? sublabel;
const _LabeledMarker({
const LabeledMarker({
super.key,
required this.label,
required this.icon,
required this.color,
@ -490,7 +317,7 @@ class _LabeledMarker extends StatelessWidget {
color: Colors.black.withOpacity(0.3),
blurRadius: 4,
offset: const Offset(0, 2),
)
),
],
),
child: Column(
@ -499,39 +326,47 @@ class _LabeledMarker extends StatelessWidget {
Text(
label,
style: const TextStyle(
color: Colors.white,
fontSize: 11,
fontWeight: FontWeight.w600),
color: Colors.white,
fontSize: 11,
fontWeight: FontWeight.w600,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
if (sublabel != null)
Text(sublabel!,
style: TextStyle(
color: Colors.white.withOpacity(0.9), fontSize: 9)),
Text(
sublabel!,
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 9,
),
),
],
),
),
Container(width: 2, height: 6, color: c),
Icon(icon, color: c, size: 22, shadows: const [
Shadow(color: Colors.black45, blurRadius: 4, offset: Offset(0, 2))
]),
Icon(
icon,
color: c,
size: 22,
shadows: const [
Shadow(color: Colors.black45, blurRadius: 4, offset: Offset(0, 2)),
],
),
],
);
}
}
// Pulzáló GPS pont
class _PulsingDot extends StatefulWidget {
class PulsingDot extends StatefulWidget {
final Color color;
const _PulsingDot({required this.color});
const PulsingDot({super.key, required this.color});
@override
State<_PulsingDot> createState() => _PulsingDotState();
State<PulsingDot> createState() => _PulsingDotState();
}
class _PulsingDotState extends State<_PulsingDot>
class _PulsingDotState extends State<PulsingDot>
with SingleTickerProviderStateMixin {
late AnimationController _ctrl;
late Animation<double> _scale;
@ -540,10 +375,12 @@ class _PulsingDotState extends State<_PulsingDot>
void initState() {
super.initState();
_ctrl = AnimationController(
vsync: this, duration: const Duration(milliseconds: 1000))
..repeat(reverse: true);
_scale = Tween(begin: 0.8, end: 1.2)
.animate(CurvedAnimation(parent: _ctrl, curve: Curves.easeInOut));
vsync: this,
duration: const Duration(milliseconds: 1000),
)..repeat(reverse: true);
_scale = Tween(begin: 0.8, end: 1.2).animate(
CurvedAnimation(parent: _ctrl, curve: Curves.easeInOut),
);
}
@override
@ -564,9 +401,10 @@ class _PulsingDotState extends State<_PulsingDot>
border: Border.all(color: Colors.white, width: 2),
boxShadow: [
BoxShadow(
color: widget.color.withOpacity(0.5),
blurRadius: 8,
spreadRadius: 2)
color: widget.color.withOpacity(0.5),
blurRadius: 8,
spreadRadius: 2,
),
],
),
),