Stabilize location driven review flow
Build and deploy Flutter Web / build (push) Successful in 6m5s
Build and deploy Flutter Web / build (push) Successful in 6m5s
This commit is contained in:
@@ -329,18 +329,35 @@ class _MapboxMapLayer extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _MapboxMapLayerState extends State<_MapboxMapLayer> {
|
||||
static const _worldCenter = LatLng(0, 0);
|
||||
static const _fallbackCenter = LatLng(16.0544, 108.2022);
|
||||
|
||||
final _viewportController = mbx.ViewportController();
|
||||
late final mbx.CameraViewportState _initialViewport;
|
||||
mbx.MapboxMap? _mapboxMap;
|
||||
var _mapLoaded = false;
|
||||
var _didInitialFlyIn = false;
|
||||
var _cameraAnimationSequence = 0;
|
||||
var _projectionVersion = 0;
|
||||
Timer? _initialFlyInTimer;
|
||||
LatLng? _lastFocusCenter;
|
||||
List<_ProjectedMarker> _projectedPlaces = const [];
|
||||
Offset? _projectedUserCoordinate;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final initialFocus = widget.focusCenter;
|
||||
_initialViewport = _cameraViewport(
|
||||
initialFocus ?? _fallbackCenter,
|
||||
zoom: initialFocus == null ? 11.2 : 15.0,
|
||||
bearing: initialFocus == null ? 0 : -12,
|
||||
pitch: initialFocus == null ? 0 : 60,
|
||||
);
|
||||
if (initialFocus != null) {
|
||||
_didInitialFlyIn = true;
|
||||
_lastFocusCenter = initialFocus;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant _MapboxMapLayer oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
@@ -355,12 +372,8 @@ class _MapboxMapLayerState extends State<_MapboxMapLayer> {
|
||||
children: [
|
||||
mbx.MapWidget(
|
||||
styleUri: _mapboxStyleUri,
|
||||
viewport: mbx.CameraViewportState(
|
||||
center: _mapboxPoint(_worldCenter),
|
||||
zoom: 0.48,
|
||||
bearing: 0,
|
||||
pitch: 0,
|
||||
),
|
||||
viewport: _initialViewport,
|
||||
viewportController: _viewportController,
|
||||
onMapCreated: _onMapCreated,
|
||||
onStyleLoadedListener: (_) {
|
||||
if (!kIsWeb) {
|
||||
@@ -404,7 +417,7 @@ class _MapboxMapLayerState extends State<_MapboxMapLayer> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_initialFlyInTimer?.cancel();
|
||||
_viewportController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -429,46 +442,65 @@ class _MapboxMapLayerState extends State<_MapboxMapLayer> {
|
||||
}
|
||||
|
||||
_lastFocusCenter = focusCenter;
|
||||
final camera = mbx.CameraOptions(
|
||||
center: _mapboxPoint(focusCenter),
|
||||
final camera = _cameraViewport(
|
||||
focusCenter,
|
||||
zoom: 15.0,
|
||||
bearing: -12,
|
||||
pitch: 60,
|
||||
);
|
||||
if (_didInitialFlyIn) {
|
||||
_cameraAnimationSequence++;
|
||||
unawaited(
|
||||
mapboxMap.easeTo(camera, mbx.MapAnimationOptions(duration: 650)),
|
||||
_viewportController.moveTo(
|
||||
camera,
|
||||
transition: const mbx.EasingViewportTransition(
|
||||
duration: Duration(milliseconds: 650),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
_didInitialFlyIn = true;
|
||||
final animationSequence = ++_cameraAnimationSequence;
|
||||
unawaited(
|
||||
mapboxMap.easeTo(
|
||||
mbx.CameraOptions(
|
||||
center: _mapboxPoint(_targetOrbitCenter(focusCenter)),
|
||||
zoom: 0.72,
|
||||
bearing: _targetOrbitBearing(focusCenter),
|
||||
pitch: 0,
|
||||
),
|
||||
mbx.MapAnimationOptions(duration: 850),
|
||||
_viewportController.moveTo(
|
||||
_cameraViewport(
|
||||
_targetOrbitCenter(focusCenter),
|
||||
zoom: 0.72,
|
||||
bearing: _targetOrbitBearing(focusCenter),
|
||||
pitch: 0,
|
||||
),
|
||||
transition: const mbx.EasingViewportTransition(
|
||||
duration: Duration(milliseconds: 850),
|
||||
),
|
||||
);
|
||||
_initialFlyInTimer?.cancel();
|
||||
_initialFlyInTimer = Timer(const Duration(milliseconds: 520), () {
|
||||
Timer(const Duration(milliseconds: 520), () {
|
||||
if (!mounted ||
|
||||
animationSequence != _cameraAnimationSequence ||
|
||||
_mapboxMap != mapboxMap) {
|
||||
return;
|
||||
}
|
||||
unawaited(
|
||||
mapboxMap.flyTo(camera, mbx.MapAnimationOptions(duration: 2800)),
|
||||
_viewportController.moveTo(
|
||||
camera,
|
||||
transition: const mbx.FlyViewportTransition(
|
||||
duration: Duration(milliseconds: 2800),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
mbx.CameraViewportState _cameraViewport(
|
||||
LatLng center, {
|
||||
required double zoom,
|
||||
required double bearing,
|
||||
required double pitch,
|
||||
}) {
|
||||
return mbx.CameraViewportState(
|
||||
center: _mapboxPoint(center),
|
||||
zoom: zoom,
|
||||
bearing: bearing,
|
||||
pitch: pitch,
|
||||
);
|
||||
}
|
||||
|
||||
LatLng _targetOrbitCenter(LatLng target) {
|
||||
final latitude = (target.latitude * 0.18).clamp(-18.0, 18.0);
|
||||
return LatLng(latitude, target.longitude);
|
||||
@@ -2396,12 +2428,7 @@ class _AddExperienceFlowState extends State<AddExperienceFlow> {
|
||||
await _startRecording();
|
||||
}
|
||||
|
||||
Future<List<PlaceRecommendation>> _loadNearbyPlaces() async {
|
||||
final coordinate = widget.coordinate;
|
||||
if (coordinate == null) {
|
||||
return const [];
|
||||
}
|
||||
|
||||
Future<List<PlaceRecommendation>> _loadNearbyPlaces(LatLng coordinate) async {
|
||||
return context
|
||||
.read<PlacesRepository>()
|
||||
.fetchNearbyPlaces(
|
||||
@@ -2490,9 +2517,29 @@ class _AddExperienceFlowState extends State<AddExperienceFlow> {
|
||||
Widget build(BuildContext context) {
|
||||
final tokens = context.mapflowTokens;
|
||||
final controller = context.read<PlaceCubit>();
|
||||
final locationState = context.select((PlaceCubit cubit) {
|
||||
final state = cubit.state.placeState;
|
||||
return (coordinate: state?.userCoordinate, status: state?.locationStatus);
|
||||
});
|
||||
final effectiveCoordinate = widget.coordinate ?? locationState.coordinate;
|
||||
final coordinateResolving =
|
||||
effectiveCoordinate == null &&
|
||||
locationState.status == LocationResolutionStatus.resolving;
|
||||
final hasTelegramAuth = context.select(
|
||||
(PlaceCubit cubit) => cubit.state.placeState?.hasTelegramAuth ?? false,
|
||||
);
|
||||
if (_step == 1 &&
|
||||
_nearbyPlacesFuture == null &&
|
||||
effectiveCoordinate != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted || _step != 1 || _nearbyPlacesFuture != null) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_nearbyPlacesFuture = _loadNearbyPlaces(effectiveCoordinate);
|
||||
});
|
||||
});
|
||||
}
|
||||
final informationProgress = (_informationUnits / _minimumInformationUnits)
|
||||
.clamp(0.0, 1.0);
|
||||
final content = switch (_step) {
|
||||
@@ -2512,19 +2559,24 @@ class _AddExperienceFlowState extends State<AddExperienceFlow> {
|
||||
await _stopRecording();
|
||||
}
|
||||
setState(() {
|
||||
_nearbyPlacesFuture = _loadNearbyPlaces();
|
||||
_nearbyPlacesFuture = effectiveCoordinate == null
|
||||
? null
|
||||
: _loadNearbyPlaces(effectiveCoordinate);
|
||||
_step = 1;
|
||||
});
|
||||
},
|
||||
),
|
||||
1 => _PlaceStep(
|
||||
placesFuture: _nearbyPlacesFuture,
|
||||
hasCoordinate: widget.coordinate != null,
|
||||
hasCoordinate: effectiveCoordinate != null,
|
||||
coordinateResolving: coordinateResolving,
|
||||
radiusMeters: _nearbyPlaceRadiusMeters,
|
||||
isSubmitting: _submitting,
|
||||
onRetry: () {
|
||||
setState(() {
|
||||
_nearbyPlacesFuture = _loadNearbyPlaces();
|
||||
_nearbyPlacesFuture = effectiveCoordinate == null
|
||||
? null
|
||||
: _loadNearbyPlaces(effectiveCoordinate);
|
||||
});
|
||||
},
|
||||
onSelect: (place) async {
|
||||
@@ -2626,6 +2678,7 @@ class _PlaceStep extends StatelessWidget {
|
||||
const _PlaceStep({
|
||||
required this.placesFuture,
|
||||
required this.hasCoordinate,
|
||||
required this.coordinateResolving,
|
||||
required this.radiusMeters,
|
||||
required this.isSubmitting,
|
||||
required this.onRetry,
|
||||
@@ -2634,6 +2687,7 @@ class _PlaceStep extends StatelessWidget {
|
||||
|
||||
final Future<List<PlaceRecommendation>>? placesFuture;
|
||||
final bool hasCoordinate;
|
||||
final bool coordinateResolving;
|
||||
final int radiusMeters;
|
||||
final bool isSubmitting;
|
||||
final VoidCallback onRetry;
|
||||
@@ -2658,11 +2712,17 @@ class _PlaceStep extends StatelessWidget {
|
||||
const SizedBox(height: 16),
|
||||
Expanded(
|
||||
child: placesFuture == null
|
||||
? _PlaceUnavailable(
|
||||
icon: Icons.location_off_outlined,
|
||||
message: 'Нет геопозиции',
|
||||
tokens: tokens,
|
||||
)
|
||||
? coordinateResolving
|
||||
? Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: tokens.voiceAccent,
|
||||
),
|
||||
)
|
||||
: _PlaceUnavailable(
|
||||
icon: Icons.location_off_outlined,
|
||||
message: 'Нет геопозиции',
|
||||
tokens: tokens,
|
||||
)
|
||||
: FutureBuilder<List<PlaceRecommendation>>(
|
||||
future: placesFuture,
|
||||
builder: (context, snapshot) {
|
||||
|
||||
Reference in New Issue
Block a user