
Adds an `image` field to the user data structure to support profile pictures. Introduces a new profile page and sets up navigation from the home screen. Changes the product list page provider to auto dispose for improved resource management.
179 lines
5.6 KiB
Dart
179 lines
5.6 KiB
Dart
import 'package:barcode_scanner/backend/schema/user/user_struct.dart';
|
|
import 'package:barcode_scanner/components/primary_button_component.dart';
|
|
import 'package:barcode_scanner/pages/profile/profile_page_model.dart';
|
|
import 'package:barcode_scanner/themes/app_theme.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/scheduler.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
class ProfilePage extends ConsumerStatefulWidget {
|
|
const ProfilePage({super.key});
|
|
|
|
@override
|
|
ConsumerState<ConsumerStatefulWidget> createState() => _ProfilePageState();
|
|
}
|
|
|
|
class _ProfilePageState extends ConsumerState<ProfilePage> {
|
|
final _formKey = GlobalKey<FormState>();
|
|
|
|
final TextEditingController phone = TextEditingController();
|
|
final TextEditingController firstName = TextEditingController();
|
|
final TextEditingController lastName = TextEditingController();
|
|
final TextEditingController email = TextEditingController();
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
SchedulerBinding.instance.addPostFrameCallback((_) {
|
|
ref
|
|
.read(profilePageModelProvider.notifier)
|
|
.getMe(
|
|
onSuccess: (value) {
|
|
phone.text = value?.phone ?? '';
|
|
firstName.text = value?.firstName ?? '';
|
|
lastName.text = value?.lastName ?? '';
|
|
email.text = value?.email ?? '';
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
phone.dispose();
|
|
firstName.dispose();
|
|
lastName.dispose();
|
|
email.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
InputDecoration _inputStyle(String label) {
|
|
return InputDecoration(
|
|
labelText: label,
|
|
filled: true,
|
|
fillColor: AppTheme.of(context).secondaryBackground,
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final state = ref.watch(profilePageModelProvider);
|
|
final user = state.user;
|
|
|
|
return Scaffold(
|
|
backgroundColor: AppTheme.of(context).primaryBackground,
|
|
appBar: AppBar(
|
|
leading: IconButton(
|
|
icon: Icon(
|
|
Icons.arrow_back_ios,
|
|
color: AppTheme.of(context).primaryText,
|
|
),
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
),
|
|
title: Text('Mon Profil', style: AppTheme.of(context).titleLarge),
|
|
centerTitle: true,
|
|
backgroundColor: AppTheme.of(context).primaryBackground,
|
|
elevation: 0,
|
|
),
|
|
body: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Column(
|
|
children: [
|
|
// Avatar
|
|
CircleAvatar(
|
|
radius: 50,
|
|
backgroundImage: user?.image != null
|
|
? NetworkImage(user!.image!)
|
|
: const AssetImage('assets/images/default_avatar.png')
|
|
as ImageProvider,
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Nom
|
|
Text(
|
|
user?.fullName.isNotEmpty == true
|
|
? user?.fullName ?? ''
|
|
: 'Nom non renseigné',
|
|
style: AppTheme.of(context).titleLarge,
|
|
),
|
|
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
user?.email ?? 'Adresse email non renseignée',
|
|
style: AppTheme.of(
|
|
context,
|
|
).bodyMedium.override(color: AppTheme.of(context).secondaryText),
|
|
),
|
|
const SizedBox(height: 24),
|
|
Divider(),
|
|
const SizedBox(height: 24),
|
|
Form(
|
|
key: _formKey,
|
|
child: Column(
|
|
children: [
|
|
/// Nom du produit
|
|
TextFormField(
|
|
controller: firstName,
|
|
decoration: _inputStyle("Prénom"),
|
|
validator: (value) => (value == null || value.isEmpty)
|
|
? 'Champ requis'
|
|
: null,
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
/// Code barre / code produit
|
|
TextFormField(
|
|
controller: lastName,
|
|
decoration: _inputStyle("Nom"),
|
|
validator: (value) => (value == null || value.isEmpty)
|
|
? 'Champ requis'
|
|
: null,
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
/// Description
|
|
TextFormField(
|
|
controller: phone,
|
|
decoration: _inputStyle("Phone"),
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
/// Prix
|
|
TextFormField(
|
|
controller: email,
|
|
decoration: _inputStyle("Email"),
|
|
keyboardType: TextInputType.emailAddress,
|
|
validator: (value) => (value == null || value.isEmpty)
|
|
? 'Champ requis'
|
|
: null,
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
/// Bouton sauvegarder
|
|
SizedBox(
|
|
width: 200,
|
|
child: PrimaryButtonComponent(
|
|
onPressed: () {
|
|
if (_formKey.currentState!.validate()) {
|
|
// Traitement ici
|
|
}
|
|
},
|
|
text: 'Modifier',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|