This commit is contained in:
Riccardo Usardi committed 2025-07-18 22:24:01 +02:00
commit e833bcaec4
224 files changed
+11812

No files matched your search

+287
View File
@@ -0,0 +1,287 @@
// import '../../../widgets/responsive_widgets/responsive.widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:u_vineyard/services/api.service.dart';
import 'package:u_vineyard/template/src/classes/enums.class.dart';
import 'package:u_vineyard/template/src/classes/functions.class.dart';
import 'package:u_vineyard/template/src/providers/alert.provider.dart';
import 'package:u_vineyard/template/src/providers/auth.provider.dart';
import 'package:u_vineyard/template/src/widgets/responsive_widgets/responsive.widget.dart';
import 'package:u_vineyard/template/src/widgets/simple_loader.widget.dart';
// import '../../../widgets/simple_loader.widget.dart';
class LoginPage extends StatefulWidget {
static const route = '/login';
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
bool isVisible = false;
bool isLoading = false;
String? error;
@override
void dispose() {
emailController.dispose();
passwordController.dispose();
super.dispose();
}
update(VoidCallback func) {
if (mounted) setState(func);
}
bool validate() {
final email = emailController.text;
final pass = passwordController.text;
if(email != "admin"){
if (email.isEmpty) {
return !setError('The email address is required');
}
if (!email.isEmail) {
return !setError('The email address is inviled');
}
if (pass.isEmpty) {
return !setError('The password is required');
}
}
return true;
}
login() async {
setError('');
update(() => isLoading = true);
final state = validate();
if (state) {
TextInput.finishAutofillContext();
await Future.delayed(const Duration(seconds: 1), () async {
ApiService.login(
emailController.text,
passwordController.text,
).then((value) {
if (value == '200') {
Get.find<AuthProvider>().toggleAuth(true);
Get.offAllNamed('/');
} else {
setError('Login failed: $value');
}
}).catchError((e) {
setError('Login failed: $e');
});
Get.offAllNamed('/');
});
initializeProviders();
}
update(() => isLoading = false);
}
togglePass() {
if (mounted) setState(() => isVisible = !isVisible);
}
bool setError(String error) {
final alerter = Get.find<AlertProvider>();
alerter.setAlert(
error,
type: AlertType.danger,
dismissable: false,
);
if (mounted) {
setState(() {});
}
if (error.isEmpty) return false;
return true;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ResponsiveWidget(
builder: (cnx, screen) => Container(
width: double.infinity,
height: double.infinity,
decoration: !screen.isMobile
? BoxDecoration(
image: DecorationImage(
image: AssetImage(imgUrl('login.jpg')),
fit: BoxFit.cover,
),
)
: null,
child: Center(
child: SingleChildScrollView(
physics:
screen.isBig ? const NeverScrollableScrollPhysics() : null,
child: Container(
width: !screen.isMobile ? 550 : null,
decoration: BoxDecoration(
color: !screen.isMobile ? Colors.white : null,
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 50),
Text(
'Welcome Back',
style:
Theme.of(context).textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.w600,
color: const Color(0xff0f0f0f),
),
),
const SizedBox(height: 50),
GetBuilder<AlertProvider>(
init: AlertProvider(),
builder: (alerter) {
return Padding(
padding: const EdgeInsets.only(
bottom: 20,
left: 20,
right: 20,
),
child: alerter.alert ?? const SizedBox(),
);
},
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: AutofillGroup(
child: Column(
children: [
TextField(
controller: emailController,
autofocus: true,
autofillHints: const [AutofillHints.email],
decoration: InputDecoration(
labelText: 'Email',
icon: SizedBox(
width: 20,
child: svg(
'profile',
color: Colors.grey.shade800,
),
),
filled: true,
fillColor: const Color(0xFFf0f1f5),
border: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFf0f1f5),
),
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFf0f1f5),
),
),
),
onChanged: (value) => setError(''),
onEditingComplete:
TextInput.finishAutofillContext,
textInputAction: TextInputAction.next,
keyboardType: TextInputType.emailAddress,
onSubmitted: (_) => login(),
),
const SizedBox(height: 20),
TextField(
autofillHints: const [AutofillHints.password],
controller: passwordController,
autofocus: false,
obscureText: !isVisible,
decoration: InputDecoration(
labelText: 'Password',
icon: SizedBox(
width: 20,
child: svg(
'password',
color: Colors.grey.shade800,
),
),
suffixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10),
child: IconButton(
onPressed: togglePass,
icon: isVisible
? svg('eye.slash.fill',
size: 16,
color: Colors.grey.shade800)
: svg('eye.fill',
size: 15,
color: Colors.grey.shade800),
),
),
filled: true,
fillColor: const Color(0xFFf0f1f5),
border: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFf0f1f5),
),
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFf0f1f5),
),
),
),
onChanged: (value) => setError(''),
onEditingComplete:
TextInput.finishAutofillContext,
textInputAction: TextInputAction.go,
keyboardType: TextInputType.visiblePassword,
onSubmitted: (_) => login(),
),
const SizedBox(height: 50),
SizedBox(
width: 300,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
),
onPressed: isLoading ? null : login,
child: AnimatedCrossFade(
crossFadeState: !isLoading
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
duration: const Duration(milliseconds: 300),
alignment: Alignment.center,
firstChild: const Text('Login'),
secondChild: const Center(
child: SimpleLoader(
strokeWidth: 3,
size: 20,
),
),
),
),
)
],
),
),
),
const SizedBox(height: 50, width: 200),
],
),
),
),
),
),
),
);
}
}
+250
View File
@@ -0,0 +1,250 @@
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:u_vineyard/services/polygon.area.calculator.service.dart';
import 'package:u_vineyard/template/src/widgets/card.widget.dart';
import 'package:u_vineyard/template/src/widgets/responsive_widgets/repesponsive_grid_item.widget.dart';
import 'package:u_vineyard/template/src/widgets/responsive_widgets/responsive_grid.widget.dart';
import 'package:u_vineyard/widgets/map.widget.dart';
class NewVineyardScreen extends StatefulWidget {
const NewVineyardScreen({super.key});
@override
State<NewVineyardScreen> createState() => _NewVineyardScreenState(); // Correzione: nome della classe State
}
class _NewVineyardScreenState extends State<NewVineyardScreen> { // Correzione: nome della classe State
// Aggiungi un TextEditingController per il TextFormField
final TextEditingController _nomeVignaController = TextEditingController();
final TextEditingController _superficieController = TextEditingController();
List<LatLng> _polygonPoints = [];
double _calculatedArea = 0.0; // Area calcolata, inizialmente impostata a 0
final TextEditingController _localitaController = TextEditingController();
@override
void dispose() {
_nomeVignaController.dispose(); // Libera il controller quando il widget viene eliminato
_superficieController.dispose(); // Libera il controller quando il widget viene eliminato
_localitaController.dispose(); // Libera il controller quando il widget viene eliminato
super.dispose();
}
void _handleMapTap(TapPosition tapPosition, LatLng latLng) {
setState(() {
_polygonPoints.add(latLng);
_calculatedArea = PolygonAreaCalculator.calculateAreaInHectares(_polygonPoints);
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Punto aggiunto: ${latLng.latitude.toStringAsFixed(4)}, ${latLng.longitude.toStringAsFixed(4)}'),
duration: const Duration(milliseconds: 800),
),
);
}
void _clearPolygon() {
setState(() {
_polygonPoints.clear();
_calculatedArea = 0.0;
_superficieController.clear();
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Poligono e superficie resettati.'),
duration: Duration(milliseconds: 800),
),
);
}
void _showAreaConfirmationDialog() {
if (_calculatedArea > 0) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Conferma Superficie'),
content: Text(
'L\'area calcolata del poligono è di ${_calculatedArea.toStringAsFixed(2)} ettari.\n\nVuoi utilizzare questo valore o inserirlo manualmente?'),
actions: <Widget>[
TextButton(
child: const Text('Inserisci Manualmente'),
onPressed: () {
Navigator.of(context).pop();
_superficieController.clear();
},
),
FilledButton(
child: const Text('Usa Valore Calcolato'),
onPressed: () {
setState(() {
_superficieController.text = _calculatedArea.toStringAsFixed(2);
});
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
void _submitForm() {
//if (_formKey.currentState!.validate()) {
if (true) {
if (_polygonPoints.length < 3) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Disegna almeno 3 punti per il poligono della vigna.')),
);
return;
}
print('--- Dati Vigna ---');
print('Nome Vigna: ${_nomeVignaController.text}');
print('Località: ${_localitaController.text}');
print('Superficie (ha): ${_superficieController.text}');
print('Coordinate Poligono:');
_polygonPoints.forEach((point) {
print(' Lat: ${point.latitude}, Lng: ${point.longitude}');
});
print('-----------------');
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Dati inviati (simulato)! Controlla la console.')),
);
}
}
@override
Widget build(BuildContext context) {
return ResponsiveGrid(
childrenBuilder: (context, screen) {
return [
ResponsiveGridItem(
crossAxisCellCount: screen.setAxis(12, 12, 12, 12),
child: Container(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CardWidget(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Aggiungi Nuova Vigna',
style: Theme.of(context).textTheme.titleLarge,
),
),
),
const SizedBox(height: 16.0), // Spazio dopo il CardWidget
// *** INIZIO DELLA CORREZIONE ***
Row(
children: [
Expanded( // <--- AVVOLGI IL TextFormField IN UN Expanded
child: TextFormField(
controller: _nomeVignaController, // Collega il controller
decoration: const InputDecoration(
labelText: 'Nome Vigna',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.grass),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Inserisci il nome della vigna';
}
return null;
},
),
),
],
),
const SizedBox(height: 12),
TextFormField(
controller: _localitaController,
decoration: const InputDecoration(
labelText: 'Località',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.location_on),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Inserisci la località';
}
return null;
},
),
const SizedBox(height: 12),
TextFormField(
//controller: superficieController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
labelText: 'Superficie (ettari)',
border: const OutlineInputBorder(),
prefixIcon: const Icon(Icons.area_chart),
suffixIcon: _calculatedArea > 0
? IconButton(
icon: const Icon(Icons.auto_mode, color: Colors.green),
onPressed: _showAreaConfirmationDialog, // Mostra il dialogo di conferma
tooltip: 'Usa area calcolata',
)
: null,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Inserisci la superficie';
}
if (double.tryParse(value) == null) {
return 'Inserisci un numero valido';
}
return null;
},
),
// *** FINE DELLA CORREZIONE ***
// Add your form or content here
Row(
children: [
Expanded(
child: VignaMapWidget(
polygonPoints: _polygonPoints,
onMapTap: _handleMapTap,
onClearPolygon: _clearPolygon,
calculatedArea: _calculatedArea,
),
),
],
),
Row(
children: [
Expanded(
child: SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: _submitForm,
icon: const Icon(Icons.send),
label: const Text(
'Invia Dati Vigna',
style: TextStyle(fontSize: 18),
),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green[800],
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 15),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
),
),
],
),
],
),
),
),
];
},
);
}
}