Registrar filho atualizado
This commit is contained in:
@@ -2,7 +2,6 @@ import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
@@ -35,10 +34,7 @@ class _AnimatedAuthSheet extends StatelessWidget {
|
||||
builder: (context, t, w) {
|
||||
return Opacity(
|
||||
opacity: t,
|
||||
child: Transform.translate(
|
||||
offset: Offset(0, (1 - t) * 12),
|
||||
child: w,
|
||||
),
|
||||
child: Transform.translate(offset: Offset(0, (1 - t) * 12), child: w),
|
||||
);
|
||||
},
|
||||
child: ClipRRect(
|
||||
@@ -54,10 +50,7 @@ class _AnimatedAuthSheet extends StatelessWidget {
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Color(0xFFFFE6F1),
|
||||
Color(0xFFFFC9DF),
|
||||
],
|
||||
colors: [Color(0xFFFFE6F1), Color(0xFFFFC9DF)],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -106,42 +99,22 @@ class _RegisterBottomSheetState extends State<RegisterBottomSheet> {
|
||||
final _emailController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
|
||||
final _childNameController = TextEditingController();
|
||||
final _childAgeController = TextEditingController();
|
||||
String? _childGender;
|
||||
|
||||
bool _loading = false;
|
||||
|
||||
static const String _kPendingQuizScopeKey = 'pending_quiz_scope_v1';
|
||||
|
||||
Future<void> _persistRegistrationData({
|
||||
required String uid,
|
||||
required String name,
|
||||
required String email,
|
||||
required String childId,
|
||||
required String childName,
|
||||
required int childAge,
|
||||
required String childGender,
|
||||
}) async {
|
||||
await Future.wait([
|
||||
FirebaseFirestore.instance.collection('users').doc(uid).set({
|
||||
'name': name,
|
||||
'email': email,
|
||||
'createdAt': FieldValue.serverTimestamp(),
|
||||
}, SetOptions(merge: true)).timeout(const Duration(seconds: 20)),
|
||||
FirebaseFirestore.instance
|
||||
.collection('users')
|
||||
.doc(uid)
|
||||
.collection('children')
|
||||
.doc(childId)
|
||||
.set({
|
||||
'id': childId,
|
||||
'name': childName,
|
||||
'age': childAge,
|
||||
'gender': childGender,
|
||||
'createdAt': FieldValue.serverTimestamp(),
|
||||
}, SetOptions(merge: true)).timeout(const Duration(seconds: 20)),
|
||||
]);
|
||||
await FirebaseFirestore.instance
|
||||
.collection('users')
|
||||
.doc(uid)
|
||||
.set({
|
||||
'name': name,
|
||||
'email': email,
|
||||
'createdAt': FieldValue.serverTimestamp(),
|
||||
}, SetOptions(merge: true))
|
||||
.timeout(const Duration(seconds: 20));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -149,8 +122,6 @@ class _RegisterBottomSheetState extends State<RegisterBottomSheet> {
|
||||
_nameController.dispose();
|
||||
_emailController.dispose();
|
||||
_passwordController.dispose();
|
||||
_childNameController.dispose();
|
||||
_childAgeController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -208,13 +179,23 @@ class _RegisterBottomSheetState extends State<RegisterBottomSheet> {
|
||||
border: underlineBorder,
|
||||
enabledBorder: underlineBorder,
|
||||
focusedBorder: underlineBorder.copyWith(
|
||||
borderSide: const BorderSide(color: primaryTeal, width: 1.6),
|
||||
borderSide: const BorderSide(
|
||||
color: primaryTeal,
|
||||
width: 1.6,
|
||||
),
|
||||
),
|
||||
floatingLabelStyle: const TextStyle(
|
||||
color: primaryTeal,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
floatingLabelStyle: const TextStyle(color: primaryTeal, fontWeight: FontWeight.w700),
|
||||
),
|
||||
validator: (v) {
|
||||
if (v == null || v.trim().isEmpty) return 'Informe seu nome';
|
||||
if (v.trim().length < 2) return 'Nome muito curto';
|
||||
if (v == null || v.trim().isEmpty) {
|
||||
return 'Informe seu nome';
|
||||
}
|
||||
if (v.trim().length < 2) {
|
||||
return 'Nome muito curto';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
@@ -228,9 +209,15 @@ class _RegisterBottomSheetState extends State<RegisterBottomSheet> {
|
||||
border: underlineBorder,
|
||||
enabledBorder: underlineBorder,
|
||||
focusedBorder: underlineBorder.copyWith(
|
||||
borderSide: const BorderSide(color: primaryTeal, width: 1.6),
|
||||
borderSide: const BorderSide(
|
||||
color: primaryTeal,
|
||||
width: 1.6,
|
||||
),
|
||||
),
|
||||
floatingLabelStyle: const TextStyle(
|
||||
color: primaryTeal,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
floatingLabelStyle: const TextStyle(color: primaryTeal, fontWeight: FontWeight.w700),
|
||||
),
|
||||
validator: (v) {
|
||||
final value = (v ?? '').trim();
|
||||
@@ -249,9 +236,15 @@ class _RegisterBottomSheetState extends State<RegisterBottomSheet> {
|
||||
border: underlineBorder,
|
||||
enabledBorder: underlineBorder,
|
||||
focusedBorder: underlineBorder.copyWith(
|
||||
borderSide: const BorderSide(color: primaryTeal, width: 1.6),
|
||||
borderSide: const BorderSide(
|
||||
color: primaryTeal,
|
||||
width: 1.6,
|
||||
),
|
||||
),
|
||||
floatingLabelStyle: const TextStyle(
|
||||
color: primaryTeal,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
floatingLabelStyle: const TextStyle(color: primaryTeal, fontWeight: FontWeight.w700),
|
||||
),
|
||||
validator: (v) {
|
||||
final value = (v ?? '');
|
||||
@@ -260,72 +253,6 @@ class _RegisterBottomSheetState extends State<RegisterBottomSheet> {
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
TextFormField(
|
||||
controller: _childNameController,
|
||||
textInputAction: TextInputAction.next,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Nome do filho(a)',
|
||||
border: underlineBorder,
|
||||
enabledBorder: underlineBorder,
|
||||
focusedBorder: underlineBorder.copyWith(
|
||||
borderSide: const BorderSide(color: primaryTeal, width: 1.6),
|
||||
),
|
||||
floatingLabelStyle: const TextStyle(color: primaryTeal, fontWeight: FontWeight.w700),
|
||||
),
|
||||
validator: (v) {
|
||||
final value = (v ?? '').trim();
|
||||
if (value.isEmpty) return 'Informe o nome do filho(a)';
|
||||
if (value.length < 2) return 'Nome muito curto';
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
TextFormField(
|
||||
controller: _childAgeController,
|
||||
keyboardType: TextInputType.number,
|
||||
textInputAction: TextInputAction.next,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Idade do filho(a)',
|
||||
border: underlineBorder,
|
||||
enabledBorder: underlineBorder,
|
||||
focusedBorder: underlineBorder.copyWith(
|
||||
borderSide: const BorderSide(color: primaryTeal, width: 1.6),
|
||||
),
|
||||
floatingLabelStyle: const TextStyle(color: primaryTeal, fontWeight: FontWeight.w700),
|
||||
),
|
||||
validator: (v) {
|
||||
final raw = (v ?? '').trim();
|
||||
if (raw.isEmpty) return 'Informe a idade do filho(a)';
|
||||
final age = int.tryParse(raw);
|
||||
if (age == null) return 'Idade inválida';
|
||||
if (age < 0 || age > 25) return 'Idade inválida';
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
DropdownButtonFormField<String>(
|
||||
initialValue: _childGender,
|
||||
items: const [
|
||||
DropdownMenuItem(value: 'Masculino', child: Text('Masculino')),
|
||||
DropdownMenuItem(value: 'Feminino', child: Text('Feminino')),
|
||||
DropdownMenuItem(value: 'Outro', child: Text('Outro')),
|
||||
],
|
||||
onChanged: (v) => setState(() => _childGender = v),
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Gênero do filho(a)',
|
||||
border: underlineBorder,
|
||||
enabledBorder: underlineBorder,
|
||||
focusedBorder: underlineBorder.copyWith(
|
||||
borderSide: const BorderSide(color: primaryTeal, width: 1.6),
|
||||
),
|
||||
floatingLabelStyle: const TextStyle(color: primaryTeal, fontWeight: FontWeight.w700),
|
||||
),
|
||||
validator: (v) {
|
||||
if (v == null || v.trim().isEmpty) return 'Selecione o gênero';
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
height: 46,
|
||||
@@ -348,7 +275,9 @@ class _RegisterBottomSheetState extends State<RegisterBottomSheet> {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextButton(
|
||||
onPressed: _loading ? null : () => Navigator.of(context).pop(),
|
||||
onPressed: _loading
|
||||
? null
|
||||
: () => Navigator.of(context).pop(),
|
||||
child: const Text('Fechar'),
|
||||
),
|
||||
],
|
||||
@@ -368,15 +297,8 @@ class _RegisterBottomSheetState extends State<RegisterBottomSheet> {
|
||||
final email = _emailController.text.trim();
|
||||
final password = _passwordController.text;
|
||||
|
||||
final childName = _childNameController.text.trim();
|
||||
final childAge = int.parse(_childAgeController.text.trim());
|
||||
final childGender = (_childGender ?? '').trim();
|
||||
|
||||
final credential = await FirebaseAuth.instance
|
||||
.createUserWithEmailAndPassword(
|
||||
email: email,
|
||||
password: password,
|
||||
)
|
||||
.createUserWithEmailAndPassword(email: email, password: password)
|
||||
.timeout(const Duration(seconds: 20));
|
||||
|
||||
final user = credential.user;
|
||||
@@ -386,19 +308,6 @@ class _RegisterBottomSheetState extends State<RegisterBottomSheet> {
|
||||
|
||||
final uid = user.uid;
|
||||
|
||||
// Gera o childId antes de fechar o sheet para termos um scopeId determinístico.
|
||||
final childId = FirebaseFirestore.instance
|
||||
.collection('users')
|
||||
.doc(uid)
|
||||
.collection('children')
|
||||
.doc()
|
||||
.id;
|
||||
final scopeId = '${uid}_$childId';
|
||||
|
||||
// Marca para o LoggedHome abrir automaticamente o quiz desta criança.
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_kPendingQuizScopeKey, scopeId);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
// Fecha o sheet imediatamente após autenticar.
|
||||
@@ -410,27 +319,27 @@ class _RegisterBottomSheetState extends State<RegisterBottomSheet> {
|
||||
uid: uid,
|
||||
name: name,
|
||||
email: email,
|
||||
childId: childId,
|
||||
childName: childName,
|
||||
childAge: childAge,
|
||||
childGender: childGender,
|
||||
).catchError((_) {}),
|
||||
);
|
||||
} on FirebaseAuthException catch (e) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(_friendlyAuthError(e))),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(_friendlyAuthError(e))));
|
||||
} on TimeoutException {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Tempo esgotado. Verifique sua conexão e tente novamente.')),
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Tempo esgotado. Verifique sua conexão e tente novamente.',
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Erro: $e')),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Erro: $e')));
|
||||
} finally {
|
||||
if (mounted && _loading) setState(() => _loading = false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user