Settings finalizadas
This commit is contained in:
@@ -1,19 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum AppLanguage { pt, en, es }
|
||||
|
||||
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) {
|
||||
if (language == 'English') {
|
||||
_currentLanguage = AppLanguage.en;
|
||||
languageNotifier.value = AppLanguage.en;
|
||||
} else if (language == 'Español') {
|
||||
_currentLanguage = AppLanguage.es;
|
||||
languageNotifier.value = AppLanguage.es;
|
||||
} 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']!;
|
||||
|
||||
@@ -162,6 +174,7 @@ class AppStrings {
|
||||
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 btnDefine => _get({'pt': "DEFINIR", 'en': "DEFINE", 'es': "DEFINIR"});
|
||||
static String get profileUpdated => _get({'pt': "Perfil atualizado!", 'en': "Profile updated!", 'es': "¡Perfil actualizado!"});
|
||||
|
||||
// Logado Screen
|
||||
static String get defineDailyGoal => _get({'pt': "Definir Meta Diária", 'en': "Set Daily Goal", 'es': "Definir Meta Diaria"});
|
||||
|
||||
@@ -15,33 +15,22 @@ void main() async {
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: AppStrings.appTitle,
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
// This is the theme of your application.
|
||||
//
|
||||
// TRY THIS: Try running your application with "flutter run". You'll see
|
||||
// the application has a purple toolbar. Then, without quitting the app,
|
||||
// try changing the seedColor in the colorScheme below to Colors.green
|
||||
// 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).
|
||||
//
|
||||
// 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(),
|
||||
return ValueListenableBuilder<AppLanguage>(
|
||||
valueListenable: AppStrings.languageNotifier,
|
||||
builder: (context, language, child) {
|
||||
return MaterialApp(
|
||||
title: AppStrings.appTitle,
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: Colors.white.withValues(alpha: 0.1),
|
||||
),
|
||||
),
|
||||
home: const InicialScreen(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../constants/app_colors.dart';
|
||||
import '../constants/app_strings.dart';
|
||||
import '../services/supabase_service.dart';
|
||||
@@ -16,10 +17,10 @@ class LogadoScreen extends StatefulWidget {
|
||||
|
||||
class _LogadoScreenState extends State<LogadoScreen> {
|
||||
// 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 _bestDistance = 12.4; // Exemplo de recorde
|
||||
double _bestSpeed = 16.8; // Exemplo de recorde
|
||||
double _bestDistance = 12.4;
|
||||
double _bestSpeed = 16.8;
|
||||
int _steps = 0;
|
||||
int _totalTimeMinutes = 0;
|
||||
|
||||
@@ -32,15 +33,38 @@ class _LogadoScreenState extends State<LogadoScreen> {
|
||||
}
|
||||
|
||||
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(() {
|
||||
// Simulação de dados carregados
|
||||
_currentDistance = 0.0; // Começa o dia a zero
|
||||
// Reset meta se o dia mudou
|
||||
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;
|
||||
_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() {
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -54,9 +78,7 @@ class _LogadoScreenState extends State<LogadoScreen> {
|
||||
...[5, 10, 15, 20].map((km) => ListTile(
|
||||
title: Text("$km ${AppStrings.kmUnit}", style: const TextStyle(color: Colors.white)),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_dailyGoal = km.toDouble();
|
||||
});
|
||||
_saveGoal(km.toDouble());
|
||||
Navigator.pop(context);
|
||||
},
|
||||
)),
|
||||
@@ -107,9 +129,7 @@ class _LogadoScreenState extends State<LogadoScreen> {
|
||||
onPressed: () {
|
||||
final value = double.tryParse(controller.text);
|
||||
if (value != null && value > 0) {
|
||||
setState(() {
|
||||
_dailyGoal = value;
|
||||
});
|
||||
_saveGoal(value);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
@@ -126,194 +146,187 @@ class _LogadoScreenState extends State<LogadoScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final user = SupabaseService.currentUser;
|
||||
final userName = user?.userMetadata?['name'] ?? user?.email?.split('@')[0] ?? AppStrings.userPlaceholder;
|
||||
return ValueListenableBuilder<AppLanguage>(
|
||||
valueListenable: AppStrings.languageNotifier,
|
||||
builder: (context, language, child) {
|
||||
final user = SupabaseService.currentUser;
|
||||
final userName = user?.userMetadata?['name'] ?? user?.email?.split('@')[0] ?? AppStrings.userPlaceholder;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.background,
|
||||
body: Stack(
|
||||
children: [
|
||||
// Background Gradient
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
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
|
||||
],
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.background,
|
||||
body: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [Color(0xFF2D2D31), AppColors.background],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// 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())),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.coral,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)),
|
||||
elevation: 0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
SafeArea(
|
||||
child: Column(
|
||||
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),
|
||||
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),
|
||||
_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),
|
||||
),
|
||||
if (_dailyGoal > 0)
|
||||
Text(
|
||||
"${(_progress * 100).toInt()}%",
|
||||
style: const TextStyle(color: AppColors.coral, fontWeight: FontWeight.w900),
|
||||
GestureDetector(
|
||||
onTap: _showGoalDialog,
|
||||
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),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -11,9 +11,13 @@ class SettingsScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SettingsScreenState extends State<SettingsScreen> {
|
||||
bool _isNightMode = true;
|
||||
bool _notificationsEnabled = true;
|
||||
String _selectedLanguage = 'Português';
|
||||
late String _selectedLanguage;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedLanguage = AppStrings.currentLanguageName;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -93,12 +97,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit, color: AppColors.buttonColor),
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppStrings.editProfile),
|
||||
backgroundColor: AppColors.buttonColor,
|
||||
),
|
||||
);
|
||||
_showEditProfileDialog(context, userName, userEmail);
|
||||
},
|
||||
),
|
||||
],
|
||||
@@ -115,28 +114,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
),
|
||||
child: Column(
|
||||
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(
|
||||
icon: Icons.language,
|
||||
title: AppStrings.language,
|
||||
@@ -152,59 +129,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
},
|
||||
),
|
||||
_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(
|
||||
icon: Icons.info,
|
||||
title: AppStrings.about,
|
||||
@@ -278,39 +202,42 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showDatePicker(BuildContext context) {
|
||||
showDatePicker(
|
||||
context: context,
|
||||
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 _showEditProfileDialog(BuildContext context, String currentName, String currentEmail) {
|
||||
final nameController = TextEditingController(text: currentName);
|
||||
final emailController = TextEditingController(text: currentEmail);
|
||||
|
||||
void _showLanguageSelector(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
backgroundColor: AppColors.backgroundGrey,
|
||||
title: Text(
|
||||
AppStrings.selectLanguage,
|
||||
AppStrings.editProfile,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildLanguageOption('Português'),
|
||||
_buildLanguageOption('English'),
|
||||
_buildLanguageOption('Español'),
|
||||
TextField(
|
||||
controller: nameController,
|
||||
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: [
|
||||
@@ -318,15 +245,74 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(
|
||||
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(
|
||||
title: Text(language, style: const TextStyle(color: Colors.white)),
|
||||
trailing: Radio<String>(
|
||||
@@ -334,28 +320,26 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
groupValue: _selectedLanguage,
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
_selectedLanguage = value;
|
||||
AppStrings.setLanguage(value);
|
||||
});
|
||||
_updateLanguage(value);
|
||||
Navigator.pop(context);
|
||||
// Force rebuild of current screen to apply changes
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
fillColor: WidgetStateProperty.all(AppColors.buttonColor),
|
||||
),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedLanguage = language;
|
||||
AppStrings.setLanguage(language);
|
||||
});
|
||||
_updateLanguage(language);
|
||||
Navigator.pop(context);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _updateLanguage(String language) {
|
||||
setState(() {
|
||||
_selectedLanguage = language;
|
||||
AppStrings.setLanguage(language);
|
||||
});
|
||||
}
|
||||
|
||||
void _showAboutDialog(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
|
||||
@@ -2,27 +2,19 @@ import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import '../constants/app_constants.dart';
|
||||
|
||||
class SupabaseService {
|
||||
static final SupabaseClient _supabase = Supabase.instance.client;
|
||||
static SupabaseClient get _supabase => Supabase.instance.client;
|
||||
|
||||
// Initialize Supabase
|
||||
static Future<void> initialize() async {
|
||||
try {
|
||||
print('DEBUG: Inicializando Supabase...');
|
||||
print('DEBUG: URL: ${AppConstants.supabaseUrl}');
|
||||
print(
|
||||
'DEBUG: AnonKey: ${AppConstants.supabaseAnonKey.substring(0, 10)}...',
|
||||
);
|
||||
|
||||
|
||||
await Supabase.initialize(
|
||||
url: AppConstants.supabaseUrl,
|
||||
anonKey: AppConstants.supabaseAnonKey,
|
||||
);
|
||||
|
||||
print('DEBUG: Supabase inicializado com sucesso!');
|
||||
|
||||
// Test connection
|
||||
final currentUser = _supabase.auth.currentUser;
|
||||
print('DEBUG: Usuário atual: ${currentUser?.email ?? 'null'}');
|
||||
} catch (e) {
|
||||
print('DEBUG: Erro ao inicializar Supabase: $e');
|
||||
rethrow;
|
||||
@@ -39,26 +31,18 @@ class SupabaseService {
|
||||
required String name,
|
||||
}) async {
|
||||
try {
|
||||
print('DEBUG: Criando conta - Email: $email, Name: $name');
|
||||
|
||||
final response = await _supabase.auth.signUp(
|
||||
email: email,
|
||||
password: password,
|
||||
data: {'name': name},
|
||||
);
|
||||
|
||||
print('DEBUG: Conta criada! User ID: ${response.user?.id}');
|
||||
|
||||
// Check if user was created successfully
|
||||
if (response.user != null) {
|
||||
print('DEBUG: Usuário criado com sucesso!');
|
||||
return response;
|
||||
} else {
|
||||
print('DEBUG: Falha ao criar usuário - response.user é null');
|
||||
throw Exception('Falha ao criar usuário. Tente novamente.');
|
||||
throw Exception('Falha ao criar usuário.');
|
||||
}
|
||||
} catch (e) {
|
||||
print('DEBUG: Erro no signUp: $e');
|
||||
throw Exception('Erro ao criar conta: $e');
|
||||
}
|
||||
}
|
||||
@@ -69,17 +53,11 @@ class SupabaseService {
|
||||
required String password,
|
||||
}) async {
|
||||
try {
|
||||
print('DEBUG: Fazendo login - Email: $email');
|
||||
|
||||
final response = await _supabase.auth.signInWithPassword(
|
||||
return await _supabase.auth.signInWithPassword(
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
|
||||
print('DEBUG: Login realizado! User ID: ${response.user?.id}');
|
||||
return response;
|
||||
} catch (e) {
|
||||
print('DEBUG: Erro no signIn: $e');
|
||||
throw Exception('Erro ao fazer login: $e');
|
||||
}
|
||||
}
|
||||
@@ -88,9 +66,7 @@ class SupabaseService {
|
||||
static Future<void> signOut() async {
|
||||
try {
|
||||
await _supabase.auth.signOut();
|
||||
print('DEBUG: Logout realizado');
|
||||
} catch (e) {
|
||||
print('DEBUG: Erro no signOut: $e');
|
||||
throw Exception('Erro ao sair: $e');
|
||||
}
|
||||
}
|
||||
@@ -99,27 +75,34 @@ class SupabaseService {
|
||||
static Future<void> resetPassword(String email) async {
|
||||
try {
|
||||
await _supabase.auth.resetPasswordForEmail(email);
|
||||
print('DEBUG: Email de reset enviado para: $email');
|
||||
} catch (e) {
|
||||
print('DEBUG: Erro no resetPassword: $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
|
||||
static Future<bool> testConnection() async {
|
||||
try {
|
||||
print('DEBUG: Testando conexão com Supabase...');
|
||||
|
||||
// Test with auth service instead of database
|
||||
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;
|
||||
} catch (e) {
|
||||
print('DEBUG: Erro na conexão: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class _EntrarSheetState extends State<EntrarSheet> {
|
||||
|
||||
// Show success message above the sheet
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppStrings.loginSuccess),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
@@ -66,7 +66,7 @@ class _EntrarSheetState extends State<EntrarSheet> {
|
||||
final email = _emailController.text.trim();
|
||||
if (email.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppStrings.validatorEmailEmpty),
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
@@ -82,7 +82,7 @@ class _EntrarSheetState extends State<EntrarSheet> {
|
||||
|
||||
// Show success message above the sheet
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppStrings.resetPasswordEmailSent),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
@@ -115,7 +115,7 @@ class _EntrarSheetState extends State<EntrarSheet> {
|
||||
minChildSize: 0.4, // 40% minimum
|
||||
maxChildSize: 0.9, // 90% maximum
|
||||
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) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
@@ -151,9 +151,9 @@ class _EntrarSheetState extends State<EntrarSheet> {
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Title
|
||||
const Text(
|
||||
Text(
|
||||
AppStrings.loginTitle,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
@@ -162,9 +162,9 @@ class _EntrarSheetState extends State<EntrarSheet> {
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Email field
|
||||
const Text(
|
||||
Text(
|
||||
AppStrings.labelEmail,
|
||||
style: TextStyle(fontSize: 16, color: Colors.white70),
|
||||
style: const TextStyle(fontSize: 16, color: Colors.white70),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Form(
|
||||
@@ -196,9 +196,9 @@ class _EntrarSheetState extends State<EntrarSheet> {
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Password field
|
||||
const Text(
|
||||
Text(
|
||||
AppStrings.labelPassword,
|
||||
style: TextStyle(fontSize: 16, color: Colors.white70),
|
||||
style: const TextStyle(fontSize: 16, color: Colors.white70),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(
|
||||
@@ -245,9 +245,9 @@ class _EntrarSheetState extends State<EntrarSheet> {
|
||||
? const CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
)
|
||||
: const Text(
|
||||
: Text(
|
||||
AppStrings.btnLogin,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
@@ -260,9 +260,9 @@ class _EntrarSheetState extends State<EntrarSheet> {
|
||||
Center(
|
||||
child: TextButton(
|
||||
onPressed: _handlePasswordReset,
|
||||
child: const Text(
|
||||
child: Text(
|
||||
AppStrings.forgotPassword,
|
||||
style: TextStyle(color: Colors.white70, fontSize: 16),
|
||||
style: const TextStyle(color: Colors.white70, fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
16
pubspec.lock
16
pubspec.lock
@@ -85,10 +85,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.4.1"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -548,18 +548,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
version: "0.12.19"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
version: "0.13.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -945,10 +945,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
version: "0.7.10"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
Reference in New Issue
Block a user