feat: Implement note encryption and synchronization features

- Added NoteEncryption class for encrypting and decrypting note content using AES-GCM.
- Updated NoteRepository to handle synchronization of notes and categories with the server, including encryption of note data before sending.
- Introduced SyncRequest and SyncResponse models for managing synchronization data.
- Enhanced LocalVaultService to store and retrieve the encryption key.
- Modified HomeScreen and SettingsScreen to trigger synchronization after note operations and manage API endpoint settings.
- Added SyncStatusIndicator to provide visual feedback on synchronization status in the app title bar.
- Created Category model to manage note categories with encryption support.
- Updated note model to include UUID, server version, deletion status, and category ID.
- Added necessary UI elements for displaying and managing the encryption key in SettingsScreen.
- Updated dependencies in pubspec.yaml for cryptography and HTTP handling.
This commit is contained in:
2026-05-18 16:11:19 +02:00
parent 516b3b9aa3
commit efe602a5da
18 changed files with 2531 additions and 71 deletions
+60 -2
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:notas/widgets/app_title_bar.dart';
import 'package:notas/data/api_client.dart';
class VaultAccessScreen extends StatefulWidget {
const VaultAccessScreen({
@@ -22,6 +23,29 @@ class VaultAccessScreen extends StatefulWidget {
class _VaultAccessScreenState extends State<VaultAccessScreen> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final TextEditingController _endpointController = TextEditingController();
bool _endpointLoading = true;
@override
void initState() {
super.initState();
_loadEndpoint();
}
Future<void> _loadEndpoint() async {
final String endpoint = await ApiConfig.getEndpoint();
if (!mounted) return;
_endpointController.text = endpoint;
setState(() {
_endpointLoading = false;
});
}
Future<void> _persistEndpointFromField() async {
final String value = _endpointController.text.trim();
if (value.isEmpty) return;
await ApiConfig.setEndpoint(value);
}
@override
void dispose() {
@@ -31,6 +55,8 @@ class _VaultAccessScreenState extends State<VaultAccessScreen> {
}
Future<void> _handleCreateAccount() async {
await _persistEndpointFromField();
await widget.onCreateAccountPressed(
_emailController.text.trim(),
_passwordController.text,
@@ -38,6 +64,8 @@ class _VaultAccessScreenState extends State<VaultAccessScreen> {
}
Future<void> _handleSignIn() async {
await _persistEndpointFromField();
await widget.onSignInPressed(
_emailController.text.trim(),
_passwordController.text,
@@ -112,13 +140,43 @@ class _VaultAccessScreenState extends State<VaultAccessScreen> {
),
),
const SizedBox(height: 28),
_endpointLoading
? const SizedBox(
height: 48,
child: Center(child: CircularProgressIndicator()),
)
: TextField(
controller: _endpointController,
enabled: !widget.isBusy,
keyboardType: TextInputType.url,
style: const TextStyle(color: Colors.white),
decoration: InputDecoration(
labelText: 'API endpoint',
labelStyle: TextStyle(color: Colors.white.withValues(alpha: 0.7)),
filled: true,
fillColor: Colors.white.withValues(alpha: 0.05),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide(color: Colors.white.withValues(alpha: 0.12)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide(color: Colors.white.withValues(alpha: 0.12)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(color: Colors.amber, width: 1.2),
),
),
),
const SizedBox(height: 16),
TextField(
controller: _emailController,
enabled: !widget.isBusy,
keyboardType: TextInputType.emailAddress,
keyboardType: TextInputType.text,
style: const TextStyle(color: Colors.white),
decoration: InputDecoration(
labelText: 'Email',
labelText: 'Usuario',
labelStyle: TextStyle(color: Colors.white.withValues(alpha: 0.7)),
filled: true,
fillColor: Colors.white.withValues(alpha: 0.05),