Add Android OAuth support and fix map camera crash

- Add appAuthRedirectScheme manifest placeholder for flutter_appauth on Android
- Fix Google Maps camera animation crash on Android ("No valid view found")
- Add safety checks and retry mechanism for camera initialization
- Make action buttons always visible regardless of delivery selection

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jean-Philippe Brule 2025-11-23 12:39:23 -05:00
parent b2be3ec4ae
commit 44500835d7
2 changed files with 43 additions and 15 deletions

View File

@ -24,10 +24,13 @@ android {
applicationId = "com.goutezplanb.planb_logistic" applicationId = "com.goutezplanb.planb_logistic"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = 23 // Required for Google Navigation Flutter minSdk = flutter.minSdkVersion // Required for Google Navigation Flutter
targetSdk = flutter.targetSdkVersion targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode versionCode = flutter.versionCode
versionName = flutter.versionName versionName = flutter.versionName
// OAuth redirect scheme for flutter_appauth
manifestPlaceholders["appAuthRedirectScheme"] = "com.goutezplanb.delivery"
} }
packagingOptions { packagingOptions {

View File

@ -312,7 +312,7 @@ class _DarkModeMapComponentState extends State<DarkModeMapComponent> {
// Calculate dynamic padding for bottom button bar // Calculate dynamic padding for bottom button bar
final topPadding = 0.0; final topPadding = 0.0;
final bottomPadding = widget.selectedDelivery != null ? 110.0 : 0.0; final bottomPadding = 110.0;
return Stack( return Stack(
children: [ children: [
@ -327,10 +327,34 @@ class _DarkModeMapComponentState extends State<DarkModeMapComponent> {
_navigationController = controller; _navigationController = controller;
// Apply dark mode style with a small delay to ensure map is ready // Apply dark mode style with a small delay to ensure map is ready
await Future.delayed(const Duration(milliseconds: 500)); await Future.delayed(const Duration(milliseconds: 500));
// Safety check: ensure widget is still mounted before proceeding
if (!mounted) return;
await _applyDarkModeStyle(); await _applyDarkModeStyle();
controller.animateCamera(
// Wrap camera animation in try-catch to handle "No valid view found" errors
// This can happen on Android when the view isn't fully ready
try {
if (mounted && _navigationController != null) {
await controller.animateCamera(
CameraUpdate.newLatLngZoom(initialPosition, 12), CameraUpdate.newLatLngZoom(initialPosition, 12),
); );
}
} catch (e) {
debugPrint('Camera animation error (view may not be ready): $e');
// Retry once after a longer delay
await Future.delayed(const Duration(milliseconds: 1000));
if (mounted && _navigationController != null) {
try {
await controller.animateCamera(
CameraUpdate.newLatLngZoom(initialPosition, 12),
);
} catch (e2) {
debugPrint('Camera animation retry failed: $e2');
}
}
}
}, },
initialCameraPosition: CameraPosition( initialCameraPosition: CameraPosition(
target: initialPosition, target: initialPosition,
@ -338,8 +362,7 @@ class _DarkModeMapComponentState extends State<DarkModeMapComponent> {
), ),
), ),
), ),
// Bottom action button bar - 4 equal-width buttons // Bottom action button bar - 4 equal-width buttons (always visible)
if (widget.selectedDelivery != null)
Positioned( Positioned(
bottom: 0, bottom: 0,
left: 0, left: 0,
@ -366,7 +389,7 @@ class _DarkModeMapComponentState extends State<DarkModeMapComponent> {
child: _buildBottomActionButton( child: _buildBottomActionButton(
label: _isNavigating ? 'Stop' : 'Start', label: _isNavigating ? 'Stop' : 'Start',
icon: _isNavigating ? Icons.stop : Icons.navigation, icon: _isNavigating ? Icons.stop : Icons.navigation,
onPressed: _isStartingNavigation || _isInitializing onPressed: _isStartingNavigation || _isInitializing || (widget.selectedDelivery == null && !_isNavigating)
? null ? null
: (_isNavigating ? _stopNavigation : _startNavigation), : (_isNavigating ? _stopNavigation : _startNavigation),
isDanger: _isNavigating, isDanger: _isNavigating,
@ -394,12 +417,14 @@ class _DarkModeMapComponentState extends State<DarkModeMapComponent> {
// Completed button // Completed button
Expanded( Expanded(
child: _buildBottomActionButton( child: _buildBottomActionButton(
label: widget.selectedDelivery!.delivered ? 'Undo' : 'Completed', label: widget.selectedDelivery?.delivered == true ? 'Undo' : 'Completed',
icon: widget.selectedDelivery!.delivered ? Icons.undo : Icons.check_circle, icon: widget.selectedDelivery?.delivered == true ? Icons.undo : Icons.check_circle,
onPressed: () => widget.onAction?.call( onPressed: widget.selectedDelivery != null
? () => widget.onAction?.call(
widget.selectedDelivery!.delivered ? 'uncomplete' : 'complete', widget.selectedDelivery!.delivered ? 'uncomplete' : 'complete',
), )
isPrimary: !widget.selectedDelivery!.delivered, : null,
isPrimary: widget.selectedDelivery != null && !widget.selectedDelivery!.delivered,
), ),
), ),
], ],