Fix review place loading state
Build and deploy Flutter Web / build (push) Successful in 4m28s

This commit is contained in:
Ruslan Bakiev
2026-06-24 14:07:47 +07:00
parent 98d6ec3f45
commit 739bc5391e
@@ -73,6 +73,7 @@ class _MapContentState extends State<_MapContent> {
final selected = state.selectedPlace;
final userCoordinate = state.userCoordinate;
final targetCenter = _focusCenter(state);
final reviewCoordinate = userCoordinate ?? selected?.coordinate;
final placeCubit = context.read<PlaceCubit>();
final traitCounts = _countPlaceTraits(state.places);
final availableTraits = [
@@ -98,7 +99,6 @@ class _MapContentState extends State<_MapContent> {
userCoordinate: userCoordinate,
onPlaceTap: (place) => _selectAndShowPlace(context, place),
),
if (targetCenter == null) const _MapTargetLoadingOverlay(),
SafeArea(
child: Align(
alignment: Alignment.topLeft,
@@ -118,10 +118,9 @@ class _MapContentState extends State<_MapContent> {
child: Align(
alignment: Alignment.topRight,
child: _AddReviewAction(
onPressed: () => _openAddFlow(
context,
userCoordinate ?? selected?.coordinate,
),
onPressed: reviewCoordinate == null
? null
: () => _openAddFlow(context, reviewCoordinate),
),
),
),
@@ -614,7 +613,7 @@ class _UserAvatar extends StatelessWidget {
class _AddReviewAction extends StatelessWidget {
const _AddReviewAction({required this.onPressed});
final VoidCallback onPressed;
final VoidCallback? onPressed;
@override
Widget build(BuildContext context) {
@@ -1515,49 +1514,6 @@ class _MapLoading extends StatelessWidget {
}
}
class _MapTargetLoadingOverlay extends StatelessWidget {
const _MapTargetLoadingOverlay();
@override
Widget build(BuildContext context) {
final tokens = context.mapflowTokens;
return IgnorePointer(
child: SafeArea(
child: Align(
alignment: Alignment.topCenter,
child: Padding(
padding: const EdgeInsets.only(top: 18),
child: DecoratedBox(
decoration: BoxDecoration(
color: tokens.mapPanel.withValues(alpha: 0.84),
borderRadius: BorderRadius.circular(999),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.24),
blurRadius: 22,
offset: const Offset(0, 10),
),
],
),
child: Padding(
padding: const EdgeInsets.all(10),
child: SizedBox.square(
dimension: 28,
child: CircularProgressIndicator(
strokeWidth: 2.5,
color: tokens.voiceAccent,
backgroundColor: tokens.onDark.withValues(alpha: 0.12),
),
),
),
),
),
),
),
);
}
}
class _MapError extends StatelessWidget {
const _MapError({required this.message});
@@ -2385,6 +2341,7 @@ class AddExperienceFlow extends StatefulWidget {
class _AddExperienceFlowState extends State<AddExperienceFlow> {
static const _minimumInformationUnits = 16.0;
static const _nearbyPlaceRadiusMeters = 50;
static const _nearbyPlacesTimeout = Duration(seconds: 12);
static const _voicePromptHints = [
'атмосфера',
'еда',
@@ -2445,10 +2402,13 @@ class _AddExperienceFlowState extends State<AddExperienceFlow> {
return const [];
}
return context.read<PlacesRepository>().fetchNearbyPlaces(
return context
.read<PlacesRepository>()
.fetchNearbyPlaces(
coordinate: coordinate,
radiusMeters: _nearbyPlaceRadiusMeters,
);
)
.timeout(_nearbyPlacesTimeout);
}
Future<void> _startRecording() async {
@@ -2559,8 +2519,14 @@ class _AddExperienceFlowState extends State<AddExperienceFlow> {
),
1 => _PlaceStep(
placesFuture: _nearbyPlacesFuture,
hasCoordinate: widget.coordinate != null,
radiusMeters: _nearbyPlaceRadiusMeters,
isSubmitting: _submitting,
onRetry: () {
setState(() {
_nearbyPlacesFuture = _loadNearbyPlaces();
});
},
onSelect: (place) async {
controller.setReviewPlace(place.name);
setState(() {
@@ -2659,14 +2625,18 @@ class _AddExperienceFlowState extends State<AddExperienceFlow> {
class _PlaceStep extends StatelessWidget {
const _PlaceStep({
required this.placesFuture,
required this.hasCoordinate,
required this.radiusMeters,
required this.isSubmitting,
required this.onRetry,
required this.onSelect,
});
final Future<List<PlaceRecommendation>>? placesFuture;
final bool hasCoordinate;
final int radiusMeters;
final bool isSubmitting;
final VoidCallback onRetry;
final Future<void> Function(PlaceRecommendation) onSelect;
@override
@@ -2687,42 +2657,43 @@ class _PlaceStep extends StatelessWidget {
),
const SizedBox(height: 16),
Expanded(
child: FutureBuilder<List<PlaceRecommendation>>(
child: placesFuture == null
? _PlaceUnavailable(
icon: Icons.location_off_outlined,
message: 'Нет геопозиции',
tokens: tokens,
)
: FutureBuilder<List<PlaceRecommendation>>(
future: placesFuture,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Center(
child: CircularProgressIndicator(color: tokens.voiceAccent),
child: CircularProgressIndicator(
color: tokens.voiceAccent,
),
);
}
if (snapshot.hasError) {
return Center(
child: Icon(
Icons.error_outline,
color: tokens.onDark,
size: 42,
return _PlaceUnavailable(
icon: Icons.error_outline,
message: 'Места не загрузились',
tokens: tokens,
action: TextButton(
onPressed: onRetry,
child: const Text('Повторить'),
),
);
}
final places = snapshot.data ?? const <PlaceRecommendation>[];
final places =
snapshot.data ?? const <PlaceRecommendation>[];
if (places.isEmpty) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.location_off_outlined,
color: tokens.onDark,
size: 42,
),
const SizedBox(height: 10),
Text(
'Нет мест в $radiusMetersм',
style: TextStyle(color: tokens.onDark),
),
],
),
return _PlaceUnavailable(
icon: Icons.location_off_outlined,
message: hasCoordinate
? 'Нет мест в $radiusMetersм'
: 'Нет геопозиции',
tokens: tokens,
);
}
@@ -2747,6 +2718,35 @@ class _PlaceStep extends StatelessWidget {
}
}
class _PlaceUnavailable extends StatelessWidget {
const _PlaceUnavailable({
required this.icon,
required this.message,
required this.tokens,
this.action,
});
final IconData icon;
final String message;
final MapflowThemeTokens tokens;
final Widget? action;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: tokens.onDark, size: 42),
const SizedBox(height: 10),
Text(message, style: TextStyle(color: tokens.onDark)),
if (action != null) ...[const SizedBox(height: 8), action!],
],
),
);
}
}
class _FavoriteStep extends StatelessWidget {
const _FavoriteStep({
required this.place,