CV Express
CV Express
---
Exécutez :
```bash
flutter pub get
```
---
---
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFF6A11CB), Color(0xFF2575FC)], // Violet -> Rose
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: child,
);
}
}
```
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
useMaterial3: true,
fontFamily: GoogleFonts.poppins().fontFamily,
),
home: const GradientBackground(child: HomeScreen()),
);
}
}
```
---
## **4. Authentification**
### **4.1 Service d'authentification (`auth_service.dart`)**
```dart
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Mon CV")),
body: SingleChildScrollView(
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextFormField(decoration: InputDecoration(labelText: "Nom")),
// Ajoutez d'autres champs...
ElevatedButton(
onPressed: () => _saveCV(),
child: const Text("Enregistrer"),
),
],
),
),
),
),
);
}
---
## **6. Export PDF**
Utilisez le package `pdf` pour générer un CV en PDF :
```dart
void generatePDF(CVModel cv) {
final pdf = pw.Document();
pdf.addPage(
pw.Page(
build: (pw.Context context) {
return pw.Column(
children: [
pw.Text("CV de ${cv.prenom} ${cv.nom}", style: pw.TextStyle(fontSize:
24)),
// Ajoutez d'autres éléments...
],
);
},
),
);
Printing.layoutPdf(onLayout: (format) => pdf.save());
}
```
---
---
## **Prochaines étapes**
1. Ajoutez des animations (ex: FadeIn pour les transitions).
2. Personnalisez les polices avec `google_fonts`.
3. Testez l'app sur un émulateur et un appareil physique.
---
@override
_LoginScreenState createState() => _LoginScreenState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: GradientBackground(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"CV Express",
style: GoogleFonts.poppins(
fontSize: 40,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 40),
CustomInputField(
controller: _emailController,
hintText: "Email",
icon: Icons.email,
),
const SizedBox(height: 20),
CustomInputField(
controller: _passwordController,
hintText: "Mot de passe",
obscureText: true,
icon: Icons.lock,
),
const SizedBox(height: 30),
ElevatedButton(
onPressed: () async {
await _auth.login(
_emailController.text.trim(),
_passwordController.text.trim(),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepPurple[200],
padding: const EdgeInsets.symmetric(horizontal: 50, vertical:
15),
),
child: Text(
"Connexion",
style: GoogleFonts.poppins(fontSize: 18),
),
),
TextButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const RegisterScreen()),
),
child: Text(
"Créer un compte",
style: GoogleFonts.poppins(color: Colors.white70),
),
),
],
),
),
),
),
);
}
}
```
---
@override
_RegisterScreenState createState() => _RegisterScreenState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: GradientBackground(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Inscription",
style: GoogleFonts.poppins(
fontSize: 40,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 40),
CustomInputField(
controller: _emailController,
hintText: "Email",
icon: Icons.email,
),
const SizedBox(height: 20),
CustomInputField(
controller: _passwordController,
hintText: "Mot de passe",
obscureText: true,
icon: Icons.lock,
),
const SizedBox(height: 30),
ElevatedButton(
onPressed: () async {
await _auth.register(
_emailController.text.trim(),
_passwordController.text.trim(),
);
if (mounted) Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.pink[200],
padding: const EdgeInsets.symmetric(horizontal: 50, vertical:
15),
),
child: Text(
"S'inscrire",
style: GoogleFonts.poppins(fontSize: 18),
),
),
],
),
),
),
),
);
}
}
```
---
ForgotPasswordScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Mot de passe oublié",
style: GoogleFonts.poppins(),
),
),
body: GradientBackground(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Réinitialisation",
style: GoogleFonts.poppins(fontSize: 30, color: Colors.white),
),
const SizedBox(height: 20),
TextField(
controller: _emailController,
decoration: InputDecoration(
hintText: "Email",
filled: true,
fillColor: Colors.white.withOpacity(0.3),
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () => _auth.resetPassword(_emailController.text.trim()),
child: Text("Envoyer le lien", style: GoogleFonts.poppins()),
),
],
),
),
),
);
}
}
```
---
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("CV Express", style: GoogleFonts.poppins()),
),
body: GradientBackground(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => Navigator.pushNamed(context, '/form_cv'),
child: Text("Créer mon CV", style: GoogleFonts.poppins()),
),
],
),
),
),
);
}
}
```
---
@override
_FormCVScreenState createState() => _FormCVScreenState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Mon CV", style: GoogleFonts.poppins())),
body: GradientBackground(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: ListView(
children: [
TextFormField(
decoration: InputDecoration(labelText: "Prénom"),
onSaved: (value) => _cvData.prenom = value,
),
// Ajoutez tous les champs nécessaires...
ElevatedButton(
onPressed: _submitForm,
child: Text("Enregistrer", style: GoogleFonts.poppins()),
),
],
),
),
),
),
);
}
void _submitForm() async {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
await FirestoreService().saveCV(_cvData);
Navigator.pushNamed(context, '/view_cv');
}
}
}
```
---
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("CV de ${cvData.prenom}", style: GoogleFonts.poppins()),
),
body: GradientBackground(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("${cvData.prenom} ${cvData.nom}",
style: GoogleFonts.poppins(fontSize: 24)),
// Affichez toutes les données du CV...
ElevatedButton(
onPressed: () => _generatePDF(),
child: Text("Exporter en PDF", style: GoogleFonts.poppins()),
),
],
),
),
),
);
}
void _generatePDF() {
// Implémentez la génération PDF ici
}
}
```
---
---
### **`firestore_service.dart`**
```dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import '../models/cv_model.dart';
class FirestoreService {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final FirebaseAuth _auth = FirebaseAuth.instance;
} catch (e) {
throw Exception("Erreur lors de la récupération : ${e.toString()}");
}
}
---
```dart
class CVModel {
String? userId;
String? prenom;
String? nom;
String? email;
String? telephone;
List<Formation>? formations;
List<Experience>? experiences;
List<String>? competences;
String? photoUrl;
---
2. **Récupération du CV** :
- Convertit les données Firestore en objet `CVModel`.
---
Dans `view_cv_screen.dart` :
```dart
// Chargement des données
final cvData = await FirestoreService().getCV();
if (cvData == null) {
// Afficher un message d'erreur
}
```
---
---
### **`input_fields.dart`**
```dart
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
const CustomInputField({
super.key,
this.controller,
required this.hintText,
this.obscureText = false,
this.icon,
this.keyboardType,
this.onChanged,
this.validator,
});
@override
Widget build(BuildContext context) {
return TextFormField(
controller: controller,
obscureText: obscureText,
keyboardType: keyboardType,
onChanged: onChanged,
validator: validator,
style: GoogleFonts.poppins(color: Colors.white),
decoration: InputDecoration(
hintText: hintText,
hintStyle: GoogleFonts.poppins(color: Colors.white70),
prefixIcon: icon != null
? Icon(icon, color: Colors.purple.shade200)
: null,
filled: true,
fillColor: Colors.white.withOpacity(0.1),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: BorderSide(
color: Colors.purple.shade200,
width: 1.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: BorderSide(
color: Colors.pink.shade200,
width: 2.0,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: const BorderSide(
color: Colors.red,
width: 1.5,
),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: const BorderSide(
color: Colors.red,
width: 2.0,
),
),
contentPadding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 20,
),
),
);
}
}
const MultiLineInputField({
super.key,
required this.controller,
required this.label,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: GoogleFonts.poppins(
color: Colors.purple.shade200,
fontSize: 16,
),
),
const SizedBox(height: 8),
TextFormField(
controller: controller,
maxLines: 3,
style: GoogleFonts.poppins(color: Colors.white),
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.purple.shade200),
),
),
),
],
);
}
}
```
---
2. **Personnalisation avancée** :
- Icônes optionnelles (par défaut : email, lock)
- Gestion du texte masqué (pour les mots de passe)
- Validation intégrée via `validator`
- Types de clavier spécifiques (email, numéro, etc.)
3. **Composants spécialisés** :
- `MultiLineInputField` pour les champs longs (descriptions, formations)
---
---
---
---