security: sanitize all error messages shown to users
Created centralized userFriendlyError() utility that converts raw
exceptions into clean, user-friendly messages. Strips hostnames,
ports, OS error codes, HTTP status codes, stack traces, and Django
field names. Maps network/timeout/auth/server errors to plain
English messages.
Fixed 16 locations across 10 files:
- home_screen, calendar_screen, learn_more_screen (SnackBar/Text)
- login_screen, desktop_login_screen (SnackBar)
- profile_screen, contribute_screen, search_screen (SnackBar)
- review_form, review_section (inline error text)
- gamification_provider (error field)
Also removed double-wrapped exceptions in ReviewService (rethrow
instead of throw Exception('Failed to...: $e')).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
73
lib/core/utils/error_utils.dart
Normal file
73
lib/core/utils/error_utils.dart
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// lib/core/utils/error_utils.dart
|
||||||
|
|
||||||
|
/// Converts raw exceptions into user-friendly messages.
|
||||||
|
/// Strips technical details (hostnames, ports, stack traces, exception chains)
|
||||||
|
/// and returns a clean message safe to display in the UI.
|
||||||
|
String userFriendlyError(Object e) {
|
||||||
|
final raw = e.toString();
|
||||||
|
|
||||||
|
// Network / connectivity issues
|
||||||
|
if (raw.contains('SocketException') ||
|
||||||
|
raw.contains('Connection refused') ||
|
||||||
|
raw.contains('Connection reset') ||
|
||||||
|
raw.contains('Network is unreachable') ||
|
||||||
|
raw.contains('No address associated') ||
|
||||||
|
raw.contains('Failed to fetch') ||
|
||||||
|
raw.contains('HandshakeException') ||
|
||||||
|
raw.contains('ClientException')) {
|
||||||
|
return 'Unable to connect. Please check your internet connection and try again.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout
|
||||||
|
if (raw.contains('TimeoutException') || raw.contains('timed out')) {
|
||||||
|
return 'The request took too long. Please try again.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rate limited
|
||||||
|
if (raw.contains('status 429') || raw.contains('throttled') || raw.contains('Too Many Requests')) {
|
||||||
|
return 'Too many requests. Please wait a moment and try again.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth expired / forbidden
|
||||||
|
if (raw.contains('status 401') || raw.contains('Unauthorized')) {
|
||||||
|
return 'Session expired. Please log in again.';
|
||||||
|
}
|
||||||
|
if (raw.contains('status 403') || raw.contains('Forbidden')) {
|
||||||
|
return 'You do not have permission to perform this action.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server error
|
||||||
|
if (RegExp(r'status 5\d\d').hasMatch(raw)) {
|
||||||
|
return 'Something went wrong on our end. Please try again later.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not found
|
||||||
|
if (raw.contains('status 404') || raw.contains('Not Found')) {
|
||||||
|
return 'The requested resource was not found.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip Exception wrappers and nested chains for validation messages
|
||||||
|
var cleaned = raw
|
||||||
|
.replaceAll(RegExp(r'Exception:\s*'), '')
|
||||||
|
.replaceAll(RegExp(r'Failed to \w+ \w+:\s*'), '')
|
||||||
|
.replaceAll(RegExp(r'Network error:\s*'), '')
|
||||||
|
.replaceAll(RegExp(r'Request failed \(status \d+\)\s*'), '')
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
// If the cleaned message is empty or still looks technical, use a generic fallback
|
||||||
|
if (cleaned.isEmpty ||
|
||||||
|
cleaned.contains('errno') ||
|
||||||
|
cleaned.contains('address =') ||
|
||||||
|
cleaned.contains('port =') ||
|
||||||
|
cleaned.startsWith('{') ||
|
||||||
|
cleaned.startsWith('[')) {
|
||||||
|
return 'Something went wrong. Please try again.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capitalize first letter
|
||||||
|
if (cleaned.isNotEmpty) {
|
||||||
|
cleaned = cleaned[0].toUpperCase() + cleaned.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// lib/features/gamification/providers/gamification_provider.dart
|
// lib/features/gamification/providers/gamification_provider.dart
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import '../../../core/utils/error_utils.dart';
|
||||||
import '../models/gamification_models.dart';
|
import '../models/gamification_models.dart';
|
||||||
import '../services/gamification_service.dart';
|
import '../services/gamification_service.dart';
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ class GamificationProvider extends ChangeNotifier {
|
|||||||
shopItems = results[2] as List<ShopItem>;
|
shopItems = results[2] as List<ShopItem>;
|
||||||
achievements = results[3] as List<AchievementBadge>;
|
achievements = results[3] as List<AchievementBadge>;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e.toString();
|
error = userFriendlyError(e);
|
||||||
} finally {
|
} finally {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@@ -58,7 +59,7 @@ class GamificationProvider extends ChangeNotifier {
|
|||||||
try {
|
try {
|
||||||
leaderboard = await _service.getLeaderboard(district: district, timePeriod: leaderboardTimePeriod);
|
leaderboard = await _service.getLeaderboard(district: district, timePeriod: leaderboardTimePeriod);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e.toString();
|
error = userFriendlyError(e);
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
@@ -73,7 +74,7 @@ class GamificationProvider extends ChangeNotifier {
|
|||||||
try {
|
try {
|
||||||
leaderboard = await _service.getLeaderboard(district: leaderboardDistrict, timePeriod: period);
|
leaderboard = await _service.getLeaderboard(district: leaderboardDistrict, timePeriod: period);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e.toString();
|
error = userFriendlyError(e);
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ class ReviewService {
|
|||||||
page: (res['page'] as num?)?.toInt() ?? page,
|
page: (res['page'] as num?)?.toInt() ?? page,
|
||||||
pageSize: (res['page_size'] as num?)?.toInt() ?? pageSize,
|
pageSize: (res['page_size'] as num?)?.toInt() ?? pageSize,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (_) {
|
||||||
throw Exception('Failed to load reviews: $e');
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,8 +70,8 @@ class ReviewService {
|
|||||||
},
|
},
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (_) {
|
||||||
throw Exception('Failed to submit review: $e');
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,8 +84,8 @@ class ReviewService {
|
|||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
);
|
);
|
||||||
return (res['helpful_count'] as num?)?.toInt() ?? 0;
|
return (res['helpful_count'] as num?)?.toInt() ?? 0;
|
||||||
} catch (e) {
|
} catch (_) {
|
||||||
throw Exception('Failed to mark review as helpful: $e');
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,8 +97,8 @@ class ReviewService {
|
|||||||
body: {'review_id': reviewId},
|
body: {'review_id': reviewId},
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (_) {
|
||||||
throw Exception('Failed to flag review: $e');
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// lib/features/reviews/widgets/review_form.dart
|
// lib/features/reviews/widgets/review_form.dart
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../../../core/storage/token_storage.dart';
|
import '../../../core/storage/token_storage.dart';
|
||||||
|
import '../../../core/utils/error_utils.dart';
|
||||||
import '../models/review_models.dart';
|
import '../models/review_models.dart';
|
||||||
import 'star_rating_input.dart';
|
import 'star_rating_input.dart';
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ class _ReviewFormState extends State<ReviewForm> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) setState(() { _state = _FormState.idle; _error = e.toString(); });
|
if (mounted) setState(() { _state = _FormState.idle; _error = userFriendlyError(e); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// lib/features/reviews/widgets/review_section.dart
|
// lib/features/reviews/widgets/review_section.dart
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../../../core/storage/token_storage.dart';
|
import '../../../core/storage/token_storage.dart';
|
||||||
|
import '../../../core/utils/error_utils.dart';
|
||||||
import '../models/review_models.dart';
|
import '../models/review_models.dart';
|
||||||
import '../services/review_service.dart';
|
import '../services/review_service.dart';
|
||||||
import 'review_summary.dart';
|
import 'review_summary.dart';
|
||||||
@@ -55,7 +56,7 @@ class _ReviewSectionState extends State<ReviewSection> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) setState(() { _loading = false; _error = e.toString(); });
|
if (mounted) setState(() { _loading = false; _error = userFriendlyError(e); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// lib/screens/calendar_screen.dart
|
// lib/screens/calendar_screen.dart
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../core/utils/error_utils.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import '../features/events/services/events_service.dart';
|
import '../features/events/services/events_service.dart';
|
||||||
@@ -94,7 +95,7 @@ class _CalendarScreenState extends State<CalendarScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.toString())));
|
if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(userFriendlyError(e))));
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setState(() => _loadingMonth = false);
|
if (mounted) setState(() => _loadingMonth = false);
|
||||||
}
|
}
|
||||||
@@ -117,7 +118,7 @@ class _CalendarScreenState extends State<CalendarScreen> {
|
|||||||
final events = await _service.getEventsForDate(yyyyMMdd);
|
final events = await _service.getEventsForDate(yyyyMMdd);
|
||||||
if (mounted) setState(() => _eventsOfDay = events);
|
if (mounted) setState(() => _eventsOfDay = events);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.toString())));
|
if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(userFriendlyError(e))));
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setState(() => _loadingDay = false);
|
if (mounted) setState(() => _loadingDay = false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../core/utils/error_utils.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@@ -1829,7 +1830,7 @@ class _ContributeScreenState extends State<ContributeScreen>
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Failed to pick images: $e')));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(userFriendlyError(e))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1868,7 +1869,7 @@ class _ContributeScreenState extends State<ContributeScreen>
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error: $e'), backgroundColor: Colors.red));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(userFriendlyError(e)), backgroundColor: Colors.red));
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setState(() => _submitting = false);
|
if (mounted) setState(() => _submitting = false);
|
||||||
@@ -2652,7 +2653,7 @@ class _ContributeScreenState extends State<ContributeScreen>
|
|||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Redemption failed: $e'), backgroundColor: Colors.red));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(userFriendlyError(e)), backgroundColor: Colors.red));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// lib/screens/desktop_login_screen.dart
|
// lib/screens/desktop_login_screen.dart
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../core/utils/error_utils.dart';
|
||||||
import '../features/auth/services/auth_service.dart';
|
import '../features/auth/services/auth_service.dart';
|
||||||
import '../core/auth/auth_guard.dart';
|
import '../core/auth/auth_guard.dart';
|
||||||
import 'home_desktop_screen.dart';
|
import 'home_desktop_screen.dart';
|
||||||
@@ -101,7 +102,7 @@ class _DesktopLoginScreenState extends State<DesktopLoginScreen> with SingleTick
|
|||||||
));
|
));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final message = e.toString().replaceAll('Exception: ', '');
|
final message = userFriendlyError(e);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
||||||
setState(() => _isAnimating = false);
|
setState(() => _isAnimating = false);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -335,7 +336,7 @@ class _DesktopRegisterScreenState extends State<DesktopRegisterScreen> {
|
|||||||
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => const HomeDesktopScreen(skipSidebarEntranceAnimation: true)));
|
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => const HomeDesktopScreen(skipSidebarEntranceAnimation: true)));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final message = e.toString().replaceAll('Exception: ', '');
|
final message = userFriendlyError(e);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setState(() => _loading = false);
|
if (mounted) setState(() => _loading = false);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// lib/screens/home_screen.dart
|
// lib/screens/home_screen.dart
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
import '../core/utils/error_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import '../core/auth/auth_guard.dart';
|
import '../core/auth/auth_guard.dart';
|
||||||
@@ -122,7 +123,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() => _loading = false);
|
setState(() => _loading = false);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.toString())));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(userFriendlyError(e))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:cached_network_image/cached_network_image.dart';
|
|||||||
import '../features/events/models/event_models.dart';
|
import '../features/events/models/event_models.dart';
|
||||||
import '../features/events/services/events_service.dart';
|
import '../features/events/services/events_service.dart';
|
||||||
import '../core/auth/auth_guard.dart';
|
import '../core/auth/auth_guard.dart';
|
||||||
|
import '../core/utils/error_utils.dart';
|
||||||
import '../core/constants.dart';
|
import '../core/constants.dart';
|
||||||
import '../features/reviews/widgets/review_section.dart';
|
import '../features/reviews/widgets/review_section.dart';
|
||||||
|
|
||||||
@@ -108,7 +109,7 @@ class _LearnMoreScreenState extends State<LearnMoreScreen> {
|
|||||||
_startAutoScroll();
|
_startAutoScroll();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() => _error = e.toString());
|
setState(() => _error = userFriendlyError(e));
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setState(() => _loading = false);
|
if (mounted) setState(() => _loading = false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// lib/screens/login_screen.dart
|
// lib/screens/login_screen.dart
|
||||||
|
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
import '../core/utils/error_utils.dart';
|
||||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
@@ -106,7 +107,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
));
|
));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final message = e.toString().replaceAll('Exception: ', '');
|
final message = userFriendlyError(e);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setState(() => _loading = false);
|
if (mounted) setState(() => _loading = false);
|
||||||
@@ -606,7 +607,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||||||
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => const HomeScreen()));
|
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => const HomeScreen()));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final message = e.toString().replaceAll('Exception: ', '');
|
final message = userFriendlyError(e);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setState(() => _loading = false);
|
if (mounted) setState(() => _loading = false);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import '../core/utils/error_utils.dart';
|
||||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
@@ -211,7 +212,7 @@ class _ProfileScreenState extends State<ProfileScreen>
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context)
|
ScaffoldMessenger.of(context)
|
||||||
.showSnackBar(SnackBar(content: Text('Failed to load events: $e')));
|
.showSnackBar(SnackBar(content: Text(userFriendlyError(e))));
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setState(() => _loadingEvents = false);
|
if (mounted) setState(() => _loadingEvents = false);
|
||||||
@@ -259,7 +260,7 @@ class _ProfileScreenState extends State<ProfileScreen>
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Image pick error: $e');
|
debugPrint('Image pick error: $e');
|
||||||
ScaffoldMessenger.of(context)
|
ScaffoldMessenger.of(context)
|
||||||
.showSnackBar(SnackBar(content: Text('Failed to pick image: $e')));
|
.showSnackBar(SnackBar(content: Text(userFriendlyError(e))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// lib/screens/search_screen.dart
|
// lib/screens/search_screen.dart
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../core/utils/error_utils.dart';
|
||||||
|
|
||||||
// Location packages
|
// Location packages
|
||||||
import 'package:geolocator/geolocator.dart';
|
import 'package:geolocator/geolocator.dart';
|
||||||
@@ -152,7 +153,7 @@ class _SearchScreenState extends State<SearchScreen> {
|
|||||||
if (mounted) Navigator.of(context).pop('Current Location');
|
if (mounted) Navigator.of(context).pop('Current Location');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Could not determine location: $e')));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(userFriendlyError(e))));
|
||||||
Navigator.of(context).pop('Current Location');
|
Navigator.of(context).pop('Current Location');
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user