MIF_E31231027/smart_cycling/lib/screens/search_route_screen.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,
),
),
],
),
),
),
);
}
}