Provide reusable models, injectable service contracts, lifecycle-safe providers and explicit dummy implementations for login, signup and verification. Add focused error and async lifecycle tests. Related to #4 and #5.
47 lines
1.3 KiB
Dart
47 lines
1.3 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
|
|
import '../models/auth.dart';
|
|
|
|
/// Shared operation lifecycle adapted from Sendico's ChangeNotifier providers.
|
|
/// Reset invalidates pending results; it does not cancel service-side work.
|
|
abstract class AuthProvider extends ChangeNotifier {
|
|
AuthState _state = const AuthState.idle();
|
|
int _generation = 0;
|
|
bool _disposed = false;
|
|
AuthState get state => _state;
|
|
|
|
@protected
|
|
Future<void> run(Future<AuthResult> Function() operation) async {
|
|
if (_disposed || _state.isLoading) return;
|
|
final generation = ++_generation;
|
|
_state = const AuthState.loading();
|
|
notifyListeners();
|
|
try {
|
|
final result = await operation();
|
|
if (_disposed || generation != _generation) return;
|
|
_state = AuthState.success(result);
|
|
} catch (error) {
|
|
if (_disposed || generation != _generation) return;
|
|
// Never retain or expose arbitrary exceptions that may contain credentials.
|
|
_state = AuthState.failure(
|
|
error is AuthException ? error.code : AuthError.unexpected,
|
|
);
|
|
}
|
|
if (!_disposed && generation == _generation) notifyListeners();
|
|
}
|
|
|
|
void reset() {
|
|
if (_disposed) return;
|
|
++_generation;
|
|
_state = const AuthState.idle();
|
|
notifyListeners();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_disposed = true;
|
|
++_generation;
|
|
super.dispose();
|
|
}
|
|
}
|