Settings finalizadas

This commit is contained in:
2026-03-18 20:03:32 +00:00
parent 6322e8d798
commit 1794208143
7 changed files with 399 additions and 401 deletions

View File

@@ -1,19 +1,31 @@
import 'package:flutter/material.dart';
enum AppLanguage { pt, en, es } enum AppLanguage { pt, en, es }
class AppStrings { class AppStrings {
static AppLanguage _currentLanguage = AppLanguage.pt; static final ValueNotifier<AppLanguage> languageNotifier = ValueNotifier(AppLanguage.pt);
static AppLanguage get currentLanguage => languageNotifier.value;
static void setLanguage(String language) { static void setLanguage(String language) {
if (language == 'English') { if (language == 'English') {
_currentLanguage = AppLanguage.en; languageNotifier.value = AppLanguage.en;
} else if (language == 'Español') { } else if (language == 'Español') {
_currentLanguage = AppLanguage.es; languageNotifier.value = AppLanguage.es;
} else { } else {
_currentLanguage = AppLanguage.pt; languageNotifier.value = AppLanguage.pt;
} }
} }
static String get _lang => _currentLanguage.name; static String get currentLanguageName {
switch (languageNotifier.value) {
case AppLanguage.en: return 'English';
case AppLanguage.es: return 'Español';
case AppLanguage.pt: return 'Português';
}
}
static String get _lang => languageNotifier.value.name;
static String _get(Map<String, String> values) => values[_lang] ?? values['pt']!; static String _get(Map<String, String> values) => values[_lang] ?? values['pt']!;
@@ -162,6 +174,7 @@ class AppStrings {
static String get btnOk => _get({'pt': "OK", 'en': "OK", 'es': "OK"}); static String get btnOk => _get({'pt': "OK", 'en': "OK", 'es': "OK"});
static String get btnCancel => _get({'pt': "Cancelar", 'en': "Cancel", 'es': "Cancelar"}); static String get btnCancel => _get({'pt': "Cancelar", 'en': "Cancel", 'es': "Cancelar"});
static String get btnDefine => _get({'pt': "DEFINIR", 'en': "DEFINE", 'es': "DEFINIR"}); static String get btnDefine => _get({'pt': "DEFINIR", 'en': "DEFINE", 'es': "DEFINIR"});
static String get profileUpdated => _get({'pt': "Perfil atualizado!", 'en': "Profile updated!", 'es': "¡Perfil actualizado!"});
// Logado Screen // Logado Screen
static String get defineDailyGoal => _get({'pt': "Definir Meta Diária", 'en': "Set Daily Goal", 'es': "Definir Meta Diaria"}); static String get defineDailyGoal => _get({'pt': "Definir Meta Diária", 'en': "Set Daily Goal", 'es': "Definir Meta Diaria"});

View File

@@ -15,33 +15,22 @@ void main() async {
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({super.key}); const MyApp({super.key});
// This widget is the root of your application.
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return ValueListenableBuilder<AppLanguage>(
title: AppStrings.appTitle, valueListenable: AppStrings.languageNotifier,
debugShowCheckedModeBanner: false, builder: (context, language, child) {
theme: ThemeData( return MaterialApp(
// This is the theme of your application. title: AppStrings.appTitle,
// debugShowCheckedModeBanner: false,
// TRY THIS: Try running your application with "flutter run". You'll see theme: ThemeData(
// the application has a purple toolbar. Then, without quitting the app, colorScheme: ColorScheme.fromSeed(
// try changing the seedColor in the colorScheme below to Colors.green seedColor: Colors.white.withValues(alpha: 0.1),
// and then invoke "hot reload" (save your changes or press the "hot ),
// reload" button in a Flutter-supported IDE, or press "r" if you used ),
// the command line to start the app). home: const InicialScreen(),
// );
// Notice that the counter didn't reset back to zero; the application },
// state is not lost during the reload. To reset the state, use hot
// restart instead.
//
// This works for code too, not just values: Most code changes can be
// tested with just a hot reload.
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.white.withValues(alpha: 0.1),
),
),
home: const InicialScreen(),
); );
} }
} }

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../constants/app_colors.dart'; import '../constants/app_colors.dart';
import '../constants/app_strings.dart'; import '../constants/app_strings.dart';
import '../services/supabase_service.dart'; import '../services/supabase_service.dart';
@@ -16,10 +17,10 @@ class LogadoScreen extends StatefulWidget {
class _LogadoScreenState extends State<LogadoScreen> { class _LogadoScreenState extends State<LogadoScreen> {
// Estado dinâmico do utilizador // Estado dinâmico do utilizador
double _dailyGoal = 0.0; // 0.0 significa que ainda não foi definida double _dailyGoal = 0.0;
double _currentDistance = 0.0; double _currentDistance = 0.0;
double _bestDistance = 12.4; // Exemplo de recorde double _bestDistance = 12.4;
double _bestSpeed = 16.8; // Exemplo de recorde double _bestSpeed = 16.8;
int _steps = 0; int _steps = 0;
int _totalTimeMinutes = 0; int _totalTimeMinutes = 0;
@@ -32,15 +33,38 @@ class _LogadoScreenState extends State<LogadoScreen> {
} }
Future<void> _loadUserData() async { Future<void> _loadUserData() async {
// No futuro, aqui buscaríamos os dados reais do Supabase ou Local Storage final prefs = await SharedPreferences.getInstance();
final lastGoalDate = prefs.getString('last_goal_date');
final today = DateTime.now().toIso8601String().split('T')[0];
setState(() { setState(() {
// Simulação de dados carregados // Reset meta se o dia mudou
_currentDistance = 0.0; // Começa o dia a zero if (lastGoalDate != today) {
_dailyGoal = 0.0;
prefs.remove('daily_goal');
} else {
_dailyGoal = prefs.getDouble('daily_goal') ?? 0.0;
}
// No futuro, estes viriam do Supabase ou histórico local
_currentDistance = 0.0;
_steps = 0; _steps = 0;
_totalTimeMinutes = 0; _totalTimeMinutes = 0;
}); });
} }
Future<void> _saveGoal(double goal) async {
final prefs = await SharedPreferences.getInstance();
final today = DateTime.now().toIso8601String().split('T')[0];
await prefs.setDouble('daily_goal', goal);
await prefs.setString('last_goal_date', today);
setState(() {
_dailyGoal = goal;
});
}
void _showGoalDialog() { void _showGoalDialog() {
showDialog( showDialog(
context: context, context: context,
@@ -54,9 +78,7 @@ class _LogadoScreenState extends State<LogadoScreen> {
...[5, 10, 15, 20].map((km) => ListTile( ...[5, 10, 15, 20].map((km) => ListTile(
title: Text("$km ${AppStrings.kmUnit}", style: const TextStyle(color: Colors.white)), title: Text("$km ${AppStrings.kmUnit}", style: const TextStyle(color: Colors.white)),
onTap: () { onTap: () {
setState(() { _saveGoal(km.toDouble());
_dailyGoal = km.toDouble();
});
Navigator.pop(context); Navigator.pop(context);
}, },
)), )),
@@ -107,9 +129,7 @@ class _LogadoScreenState extends State<LogadoScreen> {
onPressed: () { onPressed: () {
final value = double.tryParse(controller.text); final value = double.tryParse(controller.text);
if (value != null && value > 0) { if (value != null && value > 0) {
setState(() { _saveGoal(value);
_dailyGoal = value;
});
Navigator.pop(context); Navigator.pop(context);
} }
}, },
@@ -126,194 +146,187 @@ class _LogadoScreenState extends State<LogadoScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final user = SupabaseService.currentUser; return ValueListenableBuilder<AppLanguage>(
final userName = user?.userMetadata?['name'] ?? user?.email?.split('@')[0] ?? AppStrings.userPlaceholder; valueListenable: AppStrings.languageNotifier,
builder: (context, language, child) {
final user = SupabaseService.currentUser;
final userName = user?.userMetadata?['name'] ?? user?.email?.split('@')[0] ?? AppStrings.userPlaceholder;
return Scaffold( return Scaffold(
backgroundColor: AppColors.background, backgroundColor: AppColors.background,
body: Stack( body: Stack(
children: [ children: [
// Background Gradient Positioned.fill(
Positioned.fill( child: Container(
child: Container( decoration: const BoxDecoration(
decoration: const BoxDecoration( gradient: LinearGradient(
gradient: LinearGradient( begin: Alignment.topCenter,
begin: Alignment.topCenter, end: Alignment.bottomCenter,
end: Alignment.bottomCenter, colors: [Color(0xFF2D2D31), AppColors.background],
colors: [Color(0xFF2D2D31), AppColors.background],
),
),
),
),
SafeArea(
child: Column(
children: [
// Header Bar
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
AppStrings.welcome.toUpperCase(),
style: const TextStyle(
color: Colors.white38,
fontSize: 10,
fontWeight: FontWeight.w900,
letterSpacing: 2,
),
),
Text(
userName,
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.w900,
letterSpacing: -0.5,
),
),
],
),
Row(
children: [
_buildIconButton(
Icons.bluetooth_audio_rounded,
() => Navigator.push(context, MaterialPageRoute(builder: (context) => const BluetoothConnectionScreen())),
),
const SizedBox(width: 12),
_buildIconButton(
Icons.settings_rounded,
() => Navigator.push(context, MaterialPageRoute(builder: (context) => const SettingsScreen())),
),
],
),
],
),
),
Expanded(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
// Main Tracking Card (Meta Diária)
_buildMainTrackingCard(),
const SizedBox(height: 30),
// Personal Bests Section
Text(
AppStrings.personalRecords,
style: const TextStyle(
color: Colors.white38,
fontSize: 11,
fontWeight: FontWeight.w900,
letterSpacing: 1.5,
),
),
const SizedBox(height: 15),
Row(
children: [
Expanded(
child: _buildRecordCard(
AppStrings.bestDistance,
_bestDistance.toStringAsFixed(1),
AppStrings.kmUnit,
Icons.auto_graph_rounded,
AppColors.coral,
),
),
const SizedBox(width: 15),
Expanded(
child: _buildRecordCard(
AppStrings.bestSpeed,
_bestSpeed.toStringAsFixed(1),
AppStrings.kmhUnit,
Icons.speed_rounded,
Colors.cyanAccent,
),
),
],
),
const SizedBox(height: 25),
// Steps Section (Atividade Geral)
Text(
AppStrings.generalActivity,
style: const TextStyle(
color: Colors.white38,
fontSize: 11,
fontWeight: FontWeight.w900,
letterSpacing: 1.5,
),
),
const SizedBox(height: 15),
_buildWideRecordCard(
AppStrings.steps,
_steps.toString(),
AppStrings.stepsToday,
Icons.directions_walk_rounded,
AppColors.success,
),
const SizedBox(height: 120), // Espaço para o botão inferior
],
), ),
), ),
), ),
],
),
),
// Bottom Action Button
Positioned(
bottom: 30,
left: 50,
right: 50,
child: Container(
height: 70,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: AppColors.coral.withValues(alpha: 0.3),
blurRadius: 25,
spreadRadius: -5,
)
],
), ),
child: ElevatedButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => const GoogleMapScreen())), SafeArea(
style: ElevatedButton.styleFrom( child: Column(
backgroundColor: AppColors.coral,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)),
elevation: 0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon(Icons.play_arrow_rounded, size: 30), Padding(
const SizedBox(width: 10), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
Text( child: Row(
AppStrings.startTraining, mainAxisAlignment: MainAxisAlignment.spaceBetween,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w900, letterSpacing: 1.5), children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
AppStrings.welcome.toUpperCase(),
style: const TextStyle(
color: Colors.white38,
fontSize: 10,
fontWeight: FontWeight.w900,
letterSpacing: 2,
),
),
Text(
userName,
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.w900,
letterSpacing: -0.5,
),
),
],
),
Row(
children: [
_buildIconButton(
Icons.bluetooth_audio_rounded,
() => Navigator.push(context, MaterialPageRoute(builder: (context) => const BluetoothConnectionScreen())),
),
const SizedBox(width: 12),
_buildIconButton(
Icons.settings_rounded,
() => Navigator.push(context, MaterialPageRoute(builder: (context) => const SettingsScreen())),
),
],
),
],
),
),
Expanded(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
_buildMainTrackingCard(),
const SizedBox(height: 30),
Text(
AppStrings.personalRecords,
style: const TextStyle(
color: Colors.white38,
fontSize: 11,
fontWeight: FontWeight.w900,
letterSpacing: 1.5,
),
),
const SizedBox(height: 15),
Row(
children: [
Expanded(
child: _buildRecordCard(
AppStrings.bestDistance,
_bestDistance.toStringAsFixed(1),
AppStrings.kmUnit,
Icons.auto_graph_rounded,
AppColors.coral,
),
),
const SizedBox(width: 15),
Expanded(
child: _buildRecordCard(
AppStrings.bestSpeed,
_bestSpeed.toStringAsFixed(1),
AppStrings.kmhUnit,
Icons.speed_rounded,
Colors.cyanAccent,
),
),
],
),
const SizedBox(height: 25),
Text(
AppStrings.generalActivity,
style: const TextStyle(
color: Colors.white38,
fontSize: 11,
fontWeight: FontWeight.w900,
letterSpacing: 1.5,
),
),
const SizedBox(height: 15),
_buildWideRecordCard(
AppStrings.steps,
_steps.toString(),
AppStrings.stepsToday,
Icons.directions_walk_rounded,
AppColors.success,
),
const SizedBox(height: 120),
],
),
),
), ),
], ],
), ),
), ),
),
Positioned(
bottom: 30,
left: 50,
right: 50,
child: Container(
height: 70,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: AppColors.coral.withValues(alpha: 0.3),
blurRadius: 25,
spreadRadius: -5,
)
],
),
child: ElevatedButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => const GoogleMapScreen())),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.coral,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)),
elevation: 0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.play_arrow_rounded, size: 30),
const SizedBox(width: 10),
Text(
AppStrings.startTraining,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w900, letterSpacing: 1.5),
),
],
),
),
),
),
],
), ),
], );
), },
); );
} }
@@ -352,9 +365,25 @@ class _LogadoScreenState extends State<LogadoScreen> {
style: const TextStyle(color: Colors.white54, fontWeight: FontWeight.bold, letterSpacing: 1), style: const TextStyle(color: Colors.white54, fontWeight: FontWeight.bold, letterSpacing: 1),
), ),
if (_dailyGoal > 0) if (_dailyGoal > 0)
Text( GestureDetector(
"${(_progress * 100).toInt()}%", onTap: _showGoalDialog,
style: const TextStyle(color: AppColors.coral, fontWeight: FontWeight.w900), child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: AppColors.coral.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: [
Text(
"${(_progress * 100).toInt()}%",
style: const TextStyle(color: AppColors.coral, fontWeight: FontWeight.w900),
),
const SizedBox(width: 5),
const Icon(Icons.edit_rounded, color: AppColors.coral, size: 14),
],
),
),
), ),
], ],
), ),

View File

@@ -11,9 +11,13 @@ class SettingsScreen extends StatefulWidget {
} }
class _SettingsScreenState extends State<SettingsScreen> { class _SettingsScreenState extends State<SettingsScreen> {
bool _isNightMode = true; late String _selectedLanguage;
bool _notificationsEnabled = true;
String _selectedLanguage = 'Português'; @override
void initState() {
super.initState();
_selectedLanguage = AppStrings.currentLanguageName;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -93,12 +97,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
IconButton( IconButton(
icon: const Icon(Icons.edit, color: AppColors.buttonColor), icon: const Icon(Icons.edit, color: AppColors.buttonColor),
onPressed: () { onPressed: () {
ScaffoldMessenger.of(context).showSnackBar( _showEditProfileDialog(context, userName, userEmail);
SnackBar(
content: Text(AppStrings.editProfile),
backgroundColor: AppColors.buttonColor,
),
);
}, },
), ),
], ],
@@ -115,28 +114,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
), ),
child: Column( child: Column(
children: [ children: [
_buildSettingsItem(
icon: Icons.schedule,
title: AppStrings.adjustDateTime,
onTap: () {
_showDatePicker(context);
},
),
_buildDivider(),
_buildSettingsItem(
icon: Icons.dark_mode,
title: AppStrings.nightMode,
trailing: Switch(
value: _isNightMode,
activeThumbColor: AppColors.buttonColor,
onChanged: (value) {
setState(() {
_isNightMode = value;
});
},
),
),
_buildDivider(),
_buildSettingsItem( _buildSettingsItem(
icon: Icons.language, icon: Icons.language,
title: AppStrings.language, title: AppStrings.language,
@@ -152,59 +129,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
}, },
), ),
_buildDivider(), _buildDivider(),
_buildSettingsItem(
icon: Icons.accessibility,
title: AppStrings.accessibility,
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppStrings.accessibility),
backgroundColor: AppColors.buttonColor,
),
);
},
),
_buildDivider(),
_buildSettingsItem(
icon: Icons.notifications,
title: AppStrings.notifications,
trailing: Switch(
value: _notificationsEnabled,
onChanged: (value) {
setState(() {
_notificationsEnabled = value;
});
},
activeThumbColor: AppColors.buttonColor,
),
),
_buildDivider(),
_buildSettingsItem(
icon: Icons.privacy_tip,
title: AppStrings.privacySecurity,
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppStrings.privacySecurity),
backgroundColor: AppColors.buttonColor,
),
);
},
),
_buildDivider(),
_buildSettingsItem(
icon: Icons.description,
title: AppStrings.termsOfUse,
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppStrings.termsOfUse),
backgroundColor: AppColors.buttonColor,
),
);
},
),
_buildDivider(),
_buildSettingsItem( _buildSettingsItem(
icon: Icons.info, icon: Icons.info,
title: AppStrings.about, title: AppStrings.about,
@@ -278,39 +202,42 @@ class _SettingsScreenState extends State<SettingsScreen> {
); );
} }
void _showDatePicker(BuildContext context) { void _showEditProfileDialog(BuildContext context, String currentName, String currentEmail) {
showDatePicker( final nameController = TextEditingController(text: currentName);
context: context, final emailController = TextEditingController(text: currentEmail);
initialDate: DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime(2025),
).then((date) {
if (date != null && mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('${AppStrings.dateSelected}: ${date.toString().split(' ')[0]}'),
backgroundColor: AppColors.buttonColor,
),
);
}
});
}
void _showLanguageSelector(BuildContext context) {
showDialog( showDialog(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
backgroundColor: AppColors.backgroundGrey, backgroundColor: AppColors.backgroundGrey,
title: Text( title: Text(
AppStrings.selectLanguage, AppStrings.editProfile,
style: const TextStyle(color: Colors.white), style: const TextStyle(color: Colors.white),
), ),
content: Column( content: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
_buildLanguageOption('Português'), TextField(
_buildLanguageOption('English'), controller: nameController,
_buildLanguageOption('Español'), style: const TextStyle(color: Colors.white),
decoration: InputDecoration(
labelText: AppStrings.labelName,
labelStyle: const TextStyle(color: Colors.white70),
enabledBorder: const UnderlineInputBorder(borderSide: BorderSide(color: Colors.white24)),
focusedBorder: const UnderlineInputBorder(borderSide: BorderSide(color: AppColors.buttonColor)),
),
),
const SizedBox(height: 16),
TextField(
controller: emailController,
style: const TextStyle(color: Colors.white),
decoration: InputDecoration(
labelText: AppStrings.labelEmail,
labelStyle: const TextStyle(color: Colors.white70),
enabledBorder: const UnderlineInputBorder(borderSide: BorderSide(color: Colors.white24)),
focusedBorder: const UnderlineInputBorder(borderSide: BorderSide(color: AppColors.buttonColor)),
),
),
], ],
), ),
actions: [ actions: [
@@ -318,15 +245,74 @@ class _SettingsScreenState extends State<SettingsScreen> {
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
child: Text( child: Text(
AppStrings.btnCancel, AppStrings.btnCancel,
style: const TextStyle(color: AppColors.buttonColor), style: const TextStyle(color: Colors.white54),
), ),
), ),
ElevatedButton(
onPressed: () async {
try {
await SupabaseService.updateProfile(
name: nameController.text.trim(),
email: emailController.text.trim(),
);
if (context.mounted) {
Navigator.pop(context);
setState(() {});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Perfil atualizado!'), backgroundColor: Colors.green),
);
}
} catch (e) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString()), backgroundColor: Colors.red),
);
}
}
},
style: ElevatedButton.styleFrom(backgroundColor: AppColors.buttonColor),
child: Text(AppStrings.btnDefine),
),
], ],
), ),
); );
} }
Widget _buildLanguageOption(String language) { void _showLanguageSelector(BuildContext context) {
showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setDialogState) {
return AlertDialog(
backgroundColor: AppColors.backgroundGrey,
title: Text(
AppStrings.selectLanguage,
style: const TextStyle(color: Colors.white),
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildLanguageOption(context, setDialogState, 'Português'),
_buildLanguageOption(context, setDialogState, 'English'),
_buildLanguageOption(context, setDialogState, 'Español'),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(
AppStrings.btnCancel,
style: const TextStyle(color: AppColors.buttonColor),
),
),
],
);
}
),
);
}
Widget _buildLanguageOption(BuildContext context, StateSetter setDialogState, String language) {
return ListTile( return ListTile(
title: Text(language, style: const TextStyle(color: Colors.white)), title: Text(language, style: const TextStyle(color: Colors.white)),
trailing: Radio<String>( trailing: Radio<String>(
@@ -334,28 +320,26 @@ class _SettingsScreenState extends State<SettingsScreen> {
groupValue: _selectedLanguage, groupValue: _selectedLanguage,
onChanged: (value) { onChanged: (value) {
if (value != null) { if (value != null) {
setState(() { _updateLanguage(value);
_selectedLanguage = value;
AppStrings.setLanguage(value);
});
Navigator.pop(context); Navigator.pop(context);
// Force rebuild of current screen to apply changes
setState(() {});
} }
}, },
fillColor: WidgetStateProperty.all(AppColors.buttonColor), fillColor: WidgetStateProperty.all(AppColors.buttonColor),
), ),
onTap: () { onTap: () {
setState(() { _updateLanguage(language);
_selectedLanguage = language;
AppStrings.setLanguage(language);
});
Navigator.pop(context); Navigator.pop(context);
setState(() {});
}, },
); );
} }
void _updateLanguage(String language) {
setState(() {
_selectedLanguage = language;
AppStrings.setLanguage(language);
});
}
void _showAboutDialog(BuildContext context) { void _showAboutDialog(BuildContext context) {
showDialog( showDialog(
context: context, context: context,

View File

@@ -2,27 +2,19 @@ import 'package:supabase_flutter/supabase_flutter.dart';
import '../constants/app_constants.dart'; import '../constants/app_constants.dart';
class SupabaseService { class SupabaseService {
static final SupabaseClient _supabase = Supabase.instance.client; static SupabaseClient get _supabase => Supabase.instance.client;
// Initialize Supabase // Initialize Supabase
static Future<void> initialize() async { static Future<void> initialize() async {
try { try {
print('DEBUG: Inicializando Supabase...'); print('DEBUG: Inicializando Supabase...');
print('DEBUG: URL: ${AppConstants.supabaseUrl}');
print(
'DEBUG: AnonKey: ${AppConstants.supabaseAnonKey.substring(0, 10)}...',
);
await Supabase.initialize( await Supabase.initialize(
url: AppConstants.supabaseUrl, url: AppConstants.supabaseUrl,
anonKey: AppConstants.supabaseAnonKey, anonKey: AppConstants.supabaseAnonKey,
); );
print('DEBUG: Supabase inicializado com sucesso!'); print('DEBUG: Supabase inicializado com sucesso!');
// Test connection
final currentUser = _supabase.auth.currentUser;
print('DEBUG: Usuário atual: ${currentUser?.email ?? 'null'}');
} catch (e) { } catch (e) {
print('DEBUG: Erro ao inicializar Supabase: $e'); print('DEBUG: Erro ao inicializar Supabase: $e');
rethrow; rethrow;
@@ -39,26 +31,18 @@ class SupabaseService {
required String name, required String name,
}) async { }) async {
try { try {
print('DEBUG: Criando conta - Email: $email, Name: $name');
final response = await _supabase.auth.signUp( final response = await _supabase.auth.signUp(
email: email, email: email,
password: password, password: password,
data: {'name': name}, data: {'name': name},
); );
print('DEBUG: Conta criada! User ID: ${response.user?.id}');
// Check if user was created successfully
if (response.user != null) { if (response.user != null) {
print('DEBUG: Usuário criado com sucesso!');
return response; return response;
} else { } else {
print('DEBUG: Falha ao criar usuário - response.user é null'); throw Exception('Falha ao criar usuário.');
throw Exception('Falha ao criar usuário. Tente novamente.');
} }
} catch (e) { } catch (e) {
print('DEBUG: Erro no signUp: $e');
throw Exception('Erro ao criar conta: $e'); throw Exception('Erro ao criar conta: $e');
} }
} }
@@ -69,17 +53,11 @@ class SupabaseService {
required String password, required String password,
}) async { }) async {
try { try {
print('DEBUG: Fazendo login - Email: $email'); return await _supabase.auth.signInWithPassword(
final response = await _supabase.auth.signInWithPassword(
email: email, email: email,
password: password, password: password,
); );
print('DEBUG: Login realizado! User ID: ${response.user?.id}');
return response;
} catch (e) { } catch (e) {
print('DEBUG: Erro no signIn: $e');
throw Exception('Erro ao fazer login: $e'); throw Exception('Erro ao fazer login: $e');
} }
} }
@@ -88,9 +66,7 @@ class SupabaseService {
static Future<void> signOut() async { static Future<void> signOut() async {
try { try {
await _supabase.auth.signOut(); await _supabase.auth.signOut();
print('DEBUG: Logout realizado');
} catch (e) { } catch (e) {
print('DEBUG: Erro no signOut: $e');
throw Exception('Erro ao sair: $e'); throw Exception('Erro ao sair: $e');
} }
} }
@@ -99,27 +75,34 @@ class SupabaseService {
static Future<void> resetPassword(String email) async { static Future<void> resetPassword(String email) async {
try { try {
await _supabase.auth.resetPasswordForEmail(email); await _supabase.auth.resetPasswordForEmail(email);
print('DEBUG: Email de reset enviado para: $email');
} catch (e) { } catch (e) {
print('DEBUG: Erro no resetPassword: $e');
throw Exception('Erro ao redefinir senha: $e'); throw Exception('Erro ao redefinir senha: $e');
} }
} }
// Update user profile
static Future<void> updateProfile({String? name, String? email}) async {
try {
final updates = <String, dynamic>{};
if (name != null) updates['name'] = name;
final userAttributes = UserAttributes(
data: updates.isNotEmpty ? updates : null,
email: email,
);
await _supabase.auth.updateUser(userAttributes);
} catch (e) {
throw Exception('Erro ao atualizar perfil: $e');
}
}
// Test connection to Supabase // Test connection to Supabase
static Future<bool> testConnection() async { static Future<bool> testConnection() async {
try { try {
print('DEBUG: Testando conexão com Supabase...');
// Test with auth service instead of database
final session = _supabase.auth.currentSession; final session = _supabase.auth.currentSession;
print('DEBUG: Sessão atual: ${session != null ? 'ativa' : 'null'}');
// Try to get auth settings (this should work even without tables)
print('DEBUG: Conexão básica funcionando!');
return true; return true;
} catch (e) { } catch (e) {
print('DEBUG: Erro na conexão: $e');
return false; return false;
} }
} }

View File

@@ -33,7 +33,7 @@ class _EntrarSheetState extends State<EntrarSheet> {
// Show success message above the sheet // Show success message above the sheet
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( SnackBar(
content: Text(AppStrings.loginSuccess), content: Text(AppStrings.loginSuccess),
backgroundColor: Colors.green, backgroundColor: Colors.green,
), ),
@@ -66,7 +66,7 @@ class _EntrarSheetState extends State<EntrarSheet> {
final email = _emailController.text.trim(); final email = _emailController.text.trim();
if (email.isEmpty) { if (email.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( SnackBar(
content: Text(AppStrings.validatorEmailEmpty), content: Text(AppStrings.validatorEmailEmpty),
backgroundColor: Colors.orange, backgroundColor: Colors.orange,
), ),
@@ -82,7 +82,7 @@ class _EntrarSheetState extends State<EntrarSheet> {
// Show success message above the sheet // Show success message above the sheet
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( SnackBar(
content: Text(AppStrings.resetPasswordEmailSent), content: Text(AppStrings.resetPasswordEmailSent),
backgroundColor: Colors.green, backgroundColor: Colors.green,
), ),
@@ -115,7 +115,7 @@ class _EntrarSheetState extends State<EntrarSheet> {
minChildSize: 0.4, // 40% minimum minChildSize: 0.4, // 40% minimum
maxChildSize: 0.9, // 90% maximum maxChildSize: 0.9, // 90% maximum
snap: true, // Enable snap points snap: true, // Enable snap points
snapSizes: [0.6, 0.9], // Snap to 60% or 90% snapSizes: const [0.6, 0.9], // Snap to 60% or 90%
builder: (context, scrollController) { builder: (context, scrollController) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@@ -151,9 +151,9 @@ class _EntrarSheetState extends State<EntrarSheet> {
const SizedBox(height: 24), const SizedBox(height: 24),
// Title // Title
const Text( Text(
AppStrings.loginTitle, AppStrings.loginTitle,
style: TextStyle( style: const TextStyle(
fontSize: 28, fontSize: 28,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.white, color: Colors.white,
@@ -162,9 +162,9 @@ class _EntrarSheetState extends State<EntrarSheet> {
const SizedBox(height: 32), const SizedBox(height: 32),
// Email field // Email field
const Text( Text(
AppStrings.labelEmail, AppStrings.labelEmail,
style: TextStyle(fontSize: 16, color: Colors.white70), style: const TextStyle(fontSize: 16, color: Colors.white70),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Form( Form(
@@ -196,9 +196,9 @@ class _EntrarSheetState extends State<EntrarSheet> {
const SizedBox(height: 20), const SizedBox(height: 20),
// Password field // Password field
const Text( Text(
AppStrings.labelPassword, AppStrings.labelPassword,
style: TextStyle(fontSize: 16, color: Colors.white70), style: const TextStyle(fontSize: 16, color: Colors.white70),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
TextFormField( TextFormField(
@@ -245,9 +245,9 @@ class _EntrarSheetState extends State<EntrarSheet> {
? const CircularProgressIndicator( ? const CircularProgressIndicator(
color: Colors.white, color: Colors.white,
) )
: const Text( : Text(
AppStrings.btnLogin, AppStrings.btnLogin,
style: TextStyle( style: const TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
@@ -260,9 +260,9 @@ class _EntrarSheetState extends State<EntrarSheet> {
Center( Center(
child: TextButton( child: TextButton(
onPressed: _handlePasswordReset, onPressed: _handlePasswordReset,
child: const Text( child: Text(
AppStrings.forgotPassword, AppStrings.forgotPassword,
style: TextStyle(color: Colors.white70, fontSize: 16), style: const TextStyle(color: Colors.white70, fontSize: 16),
), ),
), ),
), ),

View File

@@ -85,10 +85,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: characters name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.1"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@@ -548,18 +548,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.17" version: "0.12.19"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.11.1" version: "0.13.0"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@@ -945,10 +945,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.7" version: "0.7.10"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description: