a bunch of flutter updates
This commit is contained in:
+1
-184
@@ -1,184 +1 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../../../../core/constants/app_constants.dart';
|
||||
import '../../../../injection_container.dart';
|
||||
import '../../../../core/network/api_client.dart';
|
||||
|
||||
class ServerConnectScreen extends StatefulWidget {
|
||||
const ServerConnectScreen({super.key});
|
||||
|
||||
@override
|
||||
State<ServerConnectScreen> createState() => _ServerConnectScreenState();
|
||||
}
|
||||
|
||||
class _ServerConnectScreenState extends State<ServerConnectScreen> {
|
||||
final _urlCtrl = TextEditingController(text: 'http://202.46.28.160:8080');
|
||||
bool _testing = false;
|
||||
bool _connected = false;
|
||||
String? _errorMsg;
|
||||
String? _serverInfo;
|
||||
|
||||
Future<void> _testConnection() async {
|
||||
final url = _urlCtrl.text.trim();
|
||||
if (url.isEmpty) return;
|
||||
|
||||
setState(() { _testing = true; _connected = false; _errorMsg = null; _serverInfo = null; });
|
||||
|
||||
try {
|
||||
final tempDio = Dio(BaseOptions(
|
||||
connectTimeout: AppConstants.pingTimeout,
|
||||
receiveTimeout: AppConstants.pingTimeout,
|
||||
));
|
||||
final res = await tempDio.get('$url/api/v1/auth/ping');
|
||||
if (res.statusCode == 200 && res.data['success'] == true) {
|
||||
setState(() {
|
||||
_connected = true;
|
||||
_serverInfo = 'Server aktif — ${res.data['message'] ?? 'OK'}';
|
||||
});
|
||||
} else {
|
||||
setState(() => _errorMsg = 'Server merespons tapi tidak valid. Cek URL.');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
String msg;
|
||||
if (e.type == DioExceptionType.connectionTimeout ||
|
||||
e.type == DioExceptionType.receiveTimeout) {
|
||||
msg = 'Koneksi timeout. Pastikan server berjalan dan URL benar.';
|
||||
} else if (e.type == DioExceptionType.connectionError) {
|
||||
msg = 'Tidak bisa terhubung. Cek URL dan pastikan HP di jaringan yang sama dengan server.';
|
||||
} else {
|
||||
msg = 'Error: ${e.message}';
|
||||
}
|
||||
setState(() => _errorMsg = msg);
|
||||
} catch (e) {
|
||||
setState(() => _errorMsg = 'Error tidak dikenal: $e');
|
||||
} finally {
|
||||
setState(() => _testing = false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _continueToApp() async {
|
||||
final url = _urlCtrl.text.trim();
|
||||
await AppConstants.setServerUrl(url);
|
||||
await sl<ApiClient>().init(url);
|
||||
if (mounted) context.go('/splash');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF1F5F9),
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(maxWidth: 440),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.08), blurRadius: 40, offset: const Offset(0, 16))],
|
||||
),
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Logo
|
||||
Row(children: [
|
||||
Container(
|
||||
width: 40, height: 40,
|
||||
decoration: BoxDecoration(color: const Color(0xFF1A56DB), borderRadius: BorderRadius.circular(10)),
|
||||
child: const Icon(Icons.navigation_rounded, color: Colors.white, size: 22),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text('WalkGuide', style: GoogleFonts.outfit(fontSize: 20, fontWeight: FontWeight.w700, color: const Color(0xFF0F172A))),
|
||||
]),
|
||||
const SizedBox(height: 28),
|
||||
Text('Connect to Server', style: GoogleFonts.outfit(fontSize: 26, fontWeight: FontWeight.w700, color: const Color(0xFF0F172A))),
|
||||
const SizedBox(height: 6),
|
||||
Text('Masukkan URL server yang diberikan oleh dosen/instructor.', style: GoogleFonts.inter(fontSize: 13, color: const Color(0xFF64748B))),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// URL Input
|
||||
Text('Server URL', style: GoogleFonts.inter(fontSize: 12, fontWeight: FontWeight.w500, color: const Color(0xFF64748B))),
|
||||
const SizedBox(height: 6),
|
||||
TextField(
|
||||
controller: _urlCtrl,
|
||||
keyboardType: TextInputType.url,
|
||||
style: GoogleFonts.inter(fontSize: 14, color: const Color(0xFF0F172A)),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'http://202.46.28.160:8080',
|
||||
hintStyle: GoogleFonts.inter(color: const Color(0xFFCBD5E1)),
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: Color(0xFFE2E8F0))),
|
||||
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: Color(0xFFE2E8F0))),
|
||||
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: Color(0xFF1A56DB), width: 2)),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||
),
|
||||
onChanged: (_) => setState(() { _connected = false; _errorMsg = null; }),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Status
|
||||
if (_errorMsg != null)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(color: const Color(0xFFFEF2F2), borderRadius: BorderRadius.circular(8)),
|
||||
child: Row(children: [
|
||||
const Icon(Icons.error_outline, color: Color(0xFFDC2626), size: 18),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(child: Text(_errorMsg!, style: GoogleFonts.inter(fontSize: 12, color: const Color(0xFFDC2626)))),
|
||||
]),
|
||||
),
|
||||
if (_connected && _serverInfo != null)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(color: const Color(0xFFF0FDF4), borderRadius: BorderRadius.circular(8)),
|
||||
child: Row(children: [
|
||||
const Icon(Icons.check_circle_outline, color: Color(0xFF16A34A), size: 18),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(child: Text(_serverInfo!, style: GoogleFonts.inter(fontSize: 12, color: const Color(0xFF16A34A)))),
|
||||
]),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Test button
|
||||
SizedBox(
|
||||
width: double.infinity, height: 44,
|
||||
child: OutlinedButton(
|
||||
onPressed: _testing ? null : _testConnection,
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: const BorderSide(color: Color(0xFF1A56DB)),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
child: _testing
|
||||
? const SizedBox(width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2))
|
||||
: Text('Test Connection', style: GoogleFonts.inter(fontSize: 14, fontWeight: FontWeight.w500, color: const Color(0xFF1A56DB))),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
// Continue button (only after successful test)
|
||||
if (_connected)
|
||||
SizedBox(
|
||||
width: double.infinity, height: 44,
|
||||
child: ElevatedButton(
|
||||
onPressed: _continueToApp,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF1A56DB),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
child: Text('Continue to App', style: GoogleFonts.inter(fontSize: 14, fontWeight: FontWeight.w600, color: Colors.white)),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
Center(child: Text('v1.0.0 · For Testing Purposes', style: GoogleFonts.inter(fontSize: 11, color: const Color(0xFFCBD5E1)))),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
export '../screens.dart';
|
||||
|
||||
Reference in New Issue
Block a user