Files
anyway/frontend/lib/presentation/pages/create_trip.dart

242 lines
8.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:anyway/domain/entities/preferences.dart';
import 'package:anyway/presentation/providers/trip_provider.dart';
import 'package:anyway/presentation/pages/new_trip_preferences.dart';
import 'package:anyway/presentation/pages/trip_creation_flow.dart';
import 'package:anyway/domain/entities/landmark.dart';
import 'package:anyway/presentation/providers/landmark_providers.dart';
class NewTripPage extends ConsumerStatefulWidget {
const NewTripPage({super.key});
@override
ConsumerState<NewTripPage> createState() => _NewTripPageState();
}
class _NewTripPageState extends ConsumerState<NewTripPage> {
int _currentStep = 0;
bool _isCreating = false;
List<double>? _selectedStartLocation;
bool get _hasSelectedLocation => _selectedStartLocation != null;
Future<void> _pickLocation() async {
final result = await Navigator.of(context).push<List<double>>(
MaterialPageRoute(
builder: (_) =>
const TripLocationSelectionPage(autoNavigateToPreferences: false),
),
);
if (!mounted) return;
if (result != null) {
setState(() => _selectedStartLocation = result);
}
}
void _openPreferencesPage() {
if (!_hasSelectedLocation) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Select a start location first.')),
);
return;
}
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) =>
NewTripPreferencesPage(startLocation: _selectedStartLocation!),
),
);
}
void _showSelectLocationReminder() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Choose a start location to continue.')),
);
}
Widget _buildIntermediateLandmarks(List<Landmark> landmarks) {
return Card(
margin: EdgeInsets.zero,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Fetched landmarks',
style: TextStyle(fontWeight: FontWeight.bold),
),
if (landmarks.isEmpty)
const Padding(
padding: EdgeInsets.only(top: 8.0),
child: Text(
'No landmarks fetched yet. Create a trip to load candidates.',
),
)
else
SizedBox(
height: 160,
child: ListView.separated(
itemCount: landmarks.length,
itemBuilder: (context, index) {
final lm = landmarks[index];
final coords = lm.location;
final subtitle = coords.length >= 2
? 'Lat ${coords[0].toStringAsFixed(4)}, Lon ${coords[1].toStringAsFixed(4)}'
: 'Coordinates unavailable';
return ListTile(
leading: const Icon(Icons.place),
title: Text(lm.name),
subtitle: Text(subtitle),
);
},
separatorBuilder: (context, index) =>
const Divider(height: 0),
),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
final intermediateLandmarks = ref.watch(intermediateLandmarksProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Create New Trip'),
actions: [
IconButton(
tooltip: 'Preferences',
icon: const Icon(Icons.tune),
onPressed: _openPreferencesPage,
),
],
),
body: Column(
children: [
Expanded(
child: Stepper(
currentStep: _currentStep,
onStepContinue: () async {
if (_currentStep == 0) {
if (!_hasSelectedLocation) {
_showSelectLocationReminder();
return;
}
setState(() => _currentStep = 1);
return;
}
// final step: create trip with current preferences
if (_isCreating) return;
setState(() => _isCreating = true);
if (!_hasSelectedLocation) {
_showSelectLocationReminder();
setState(() {
_isCreating = false;
_currentStep = 0;
});
return;
}
// For now use a minimal Preferences object; UI should supply real values later.
final prefs = Preferences(
scores: {'sightseeing': 3, 'shopping': 1, 'nature': 2},
maxTimeMinutes: 120,
startLocation: _selectedStartLocation!,
);
final createTrip = ref.read(createTripProvider);
final messenger = ScaffoldMessenger.of(context);
final navigator = Navigator.of(context);
try {
final trip = await createTrip(prefs);
// Show success and (later) navigate to trip viewer
messenger.showSnackBar(
SnackBar(content: Text('Trip created: ${trip.uuid}')),
);
navigator.pop();
} catch (e) {
messenger.showSnackBar(
SnackBar(content: Text('Failed to create trip: $e')),
);
} finally {
if (mounted) {
setState(() => _isCreating = false);
}
}
},
onStepCancel: () {
if (_currentStep > 0) {
setState(() {
_currentStep -= 1;
});
}
},
steps: [
Step(
title: const Text('Select Location'),
state: _hasSelectedLocation
? StepState.complete
: StepState.indexed,
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_hasSelectedLocation
? 'Selected: ${_selectedStartLocation![0].toStringAsFixed(4)}, ${_selectedStartLocation![1].toStringAsFixed(4)}'
: 'Pick the starting point for your trip.',
),
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
icon: const Icon(Icons.map),
label: Text(
_hasSelectedLocation
? 'Change location'
: 'Pick on map',
),
onPressed: _pickLocation,
),
),
],
),
isActive: _currentStep >= 0,
),
Step(
title: const Text('Choose Options'),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
SizedBox(
height: 200,
child: Center(child: Text('Options placeholder')),
),
SizedBox(height: 8),
Text('Tap the tuner icon to fine-tune preferences.'),
],
),
isActive: _currentStep >= 1,
),
],
),
),
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: _buildIntermediateLandmarks(intermediateLandmarks),
),
const SizedBox(height: 16),
],
),
);
}
}