300 lines
11 KiB
Dart
300 lines
11 KiB
Dart
import 'dart:ui';
|
|
import 'dart:convert';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import '../widgets/custom_text_field.dart';
|
|
import '../utils/api_config.dart';
|
|
import 'route_recommendation_screen.dart';
|
|
|
|
class SearchRouteScreen extends StatefulWidget {
|
|
const SearchRouteScreen({super.key});
|
|
|
|
@override
|
|
State<SearchRouteScreen> createState() => _SearchRouteScreenState();
|
|
}
|
|
|
|
class _SearchRouteScreenState extends State<SearchRouteScreen> {
|
|
String? selectedBikeType;
|
|
String? selectedUserType;
|
|
String selectedSpeed = "Sedang"; // Default: Sedang
|
|
bool _isLoading = false;
|
|
|
|
// Fungsi untuk memanggil API Rekomendasi SAW
|
|
Future<void> _findRoutes() async {
|
|
if (selectedUserType == null || selectedBikeType == null) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Tolong pilih tipe pengguna dan jenis sepeda!')),
|
|
);
|
|
return;
|
|
}
|
|
|
|
setState(() => _isLoading = true);
|
|
|
|
try {
|
|
final response = await http.post(
|
|
Uri.parse('${ApiConfig.baseUrl}/recommend_routes.php'),
|
|
body: {
|
|
'jenis_sepeda': selectedBikeType,
|
|
'tipe_pengguna': selectedUserType,
|
|
},
|
|
);
|
|
|
|
final data = jsonDecode(response.body);
|
|
if (!mounted) return;
|
|
|
|
if (data['status'] == 'success') {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => RouteRecommendationScreen(
|
|
routes: data['data'],
|
|
selectedSpeed: selectedSpeed,
|
|
),
|
|
),
|
|
);
|
|
} else {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Gagal: ${data['message']}')),
|
|
);
|
|
}
|
|
} catch (e) {
|
|
if (!mounted) return;
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Error: $e')),
|
|
);
|
|
} finally {
|
|
if (mounted) setState(() => _isLoading = false);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
body: Container(
|
|
width: double.infinity,
|
|
height: double.infinity,
|
|
decoration: const BoxDecoration(
|
|
gradient: LinearGradient(
|
|
colors: [Color(0xFF3949AB), Color(0xFF00BCD4)],
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
),
|
|
),
|
|
child: Stack(
|
|
children: [
|
|
SafeArea(
|
|
child: SingleChildScrollView(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 24.0, vertical: 20.0),
|
|
child: Column(
|
|
children: [
|
|
const SizedBox(height: 40),
|
|
// Header Section
|
|
const Icon(Icons.explore_outlined,
|
|
size: 60, color: Colors.white),
|
|
const SizedBox(height: 16),
|
|
const Text(
|
|
"Cari Rute Terbaik",
|
|
style: TextStyle(
|
|
fontSize: 28,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
fontFamily: 'Montserrat',
|
|
),
|
|
),
|
|
const Text(
|
|
"Tentukan kriteria perjalanan Anda",
|
|
style: TextStyle(fontSize: 15, color: Colors.white70),
|
|
),
|
|
const SizedBox(height: 40),
|
|
|
|
// GLASSMORPHISM FORM CARD
|
|
ClipRRect(
|
|
borderRadius: BorderRadius.circular(30),
|
|
child: BackdropFilter(
|
|
filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15),
|
|
child: Container(
|
|
padding: const EdgeInsets.all(24.0),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.15),
|
|
borderRadius: BorderRadius.circular(30),
|
|
border: Border.all(
|
|
color: Colors.white.withOpacity(0.3),
|
|
width: 1.5,
|
|
),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// 1. TIPE PENGGUNA
|
|
_buildLabel("TIPE PENGGUNA"),
|
|
_buildGlassDropdown(
|
|
['Profesional', 'Amatir'],
|
|
Icons.person_outline,
|
|
"Pilih Tipe Pengguna",
|
|
(val) => setState(() => selectedUserType = val),
|
|
selectedUserType,
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
// 2. JENIS SEPEDA
|
|
_buildLabel("JENIS SEPEDA"),
|
|
_buildGlassDropdown(
|
|
['Road Bike', 'MTB', 'Hybrid'],
|
|
Icons.pedal_bike,
|
|
"Pilih Jenis Sepeda",
|
|
(val) => setState(() => selectedBikeType = val),
|
|
selectedBikeType,
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
// 3. KECEPATAN (3 Pilihan: Lambat, Sedang, Cepat)
|
|
_buildLabel("MODE BERSEPEDA"),
|
|
const SizedBox(height: 8),
|
|
Row(
|
|
children: [
|
|
_buildSpeedCard(
|
|
"Lambat", Icons.directions_walk),
|
|
const SizedBox(width: 10),
|
|
_buildSpeedCard(
|
|
"Sedang", Icons.directions_bike),
|
|
const SizedBox(width: 10),
|
|
_buildSpeedCard("Cepat", Icons.flash_on),
|
|
],
|
|
),
|
|
const SizedBox(height: 32),
|
|
|
|
// SUBMIT BUTTON
|
|
SizedBox(
|
|
width: double.infinity,
|
|
height: 55,
|
|
child: ElevatedButton(
|
|
onPressed: _isLoading ? null : _findRoutes,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.white,
|
|
foregroundColor: const Color(0xFF3949AB),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(15),
|
|
),
|
|
elevation: 0,
|
|
),
|
|
child: _isLoading
|
|
? const SizedBox(
|
|
height: 20,
|
|
width: 20,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 2,
|
|
color: Color(0xFF3949AB),
|
|
),
|
|
)
|
|
: const Text(
|
|
"TEMUKAN RUTE",
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 16,
|
|
letterSpacing: 1.2,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 40),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildLabel(String text) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(left: 4, bottom: 8),
|
|
child: Text(
|
|
text,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white.withOpacity(0.8),
|
|
letterSpacing: 1.5,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildGlassDropdown(List<String> items, IconData icon,
|
|
String hint, void Function(String?) onChanged, String? value) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(15),
|
|
border: Border.all(color: Colors.white.withOpacity(0.3)),
|
|
),
|
|
child: DropdownButtonHideUnderline(
|
|
child: DropdownButtonFormField<String>(
|
|
value: value,
|
|
dropdownColor: const Color(0xFF1E2140).withOpacity(0.9),
|
|
style:
|
|
const TextStyle(color: Colors.white, fontWeight: FontWeight.w500),
|
|
decoration: InputDecoration(
|
|
border: InputBorder.none,
|
|
icon: Icon(icon, color: Colors.white70, size: 22),
|
|
),
|
|
items: items.map((String value) {
|
|
return DropdownMenuItem<String>(
|
|
value: value,
|
|
child: Text(value, style: const TextStyle(color: Colors.white)),
|
|
);
|
|
}).toList(),
|
|
onChanged: onChanged,
|
|
hint: Text(hint, style: const TextStyle(color: Colors.white54)),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSpeedCard(String label, IconData icon) {
|
|
bool isSelected = selectedSpeed == label;
|
|
return Expanded(
|
|
child: GestureDetector(
|
|
onTap: () => setState(() => selectedSpeed = label),
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
decoration: BoxDecoration(
|
|
color: isSelected ? Colors.white : Colors.white.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(15),
|
|
border: Border.all(
|
|
color: isSelected ? Colors.white : Colors.white.withOpacity(0.3),
|
|
width: 1.5,
|
|
),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Icon(
|
|
icon,
|
|
color: isSelected ? const Color(0xFF3949AB) : Colors.white70,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
label,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
color: isSelected ? const Color(0xFF3949AB) : Colors.white70,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|