flutter-mvvm-patterns — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited flutter-mvvm-patterns (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user_model.freezed.dart';
part 'user_model.g.dart';
@freezed
class UserModel with _$UserModel {
const factory UserModel({
required String id,
required String name,
required String email,
String? avatarUrl,
}) = _UserModel;
factory UserModel.fromJson(Map<String, dynamic> json) =>
_$UserModelFromJson(json);
}class AuthRepository {
AuthRepository(this._apiClient, this._localStorage);
final ApiClient _apiClient;
final LocalStorage _localStorage;
Future<UserModel> login(String email, String password) async {
final response = await _apiClient.login(email, password);
await _localStorage.saveToken(response.token);
return response.user;
}
}
// Riverpod provider
@riverpod
AuthRepository authRepository(Ref ref) {
return AuthRepository(ref.watch(apiClientProvider), ref.watch(localStorageProvider));
}@riverpod
class AuthViewModel extends _$AuthViewModel {
@override
FutureOr<UserModel?> build() => null;
Future<void> login(String email, String password) async {
state = const AsyncLoading();
state = await AsyncValue.guard(() =>
ref.read(authRepositoryProvider).login(email, password),
);
}
}class LoginScreen extends ConsumerWidget {
const LoginScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final authState = ref.watch(authViewModelProvider);
ref.listen(authViewModelProvider, (_, next) {
next.whenOrNull(
data: (user) {
if (user != null) context.go('/home');
},
error: (e, _) => ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(e.toString()))),
);
});
return Scaffold(
body: authState.isLoading
? const Center(child: CircularProgressIndicator())
: LoginForm(
onSubmit: (email, password) =>
ref.read(authViewModelProvider.notifier).login(email, password),
),
);
}
}final routerProvider = Provider<GoRouter>((ref) {
return GoRouter(
initialLocation: '/login',
routes: [
GoRoute(path: '/login', builder: (_, __) => const LoginScreen()),
GoRoute(path: '/home', builder: (_, __) => const HomeScreen()),
ShellRoute(
builder: (_, __, child) => ScaffoldWithNavBar(child: child),
routes: [
GoRoute(path: '/feed', builder: (_, __) => const FeedScreen()),
GoRoute(path: '/profile', builder: (_, __) => const ProfileScreen()),
],
),
],
redirect: (context, state) {
final isLoggedIn = ref.read(authStateProvider) != null;
if (!isLoggedIn && state.matchedLocation != '/login') return '/login';
return null;
},
);
});@RestApi()
abstract class ApiClient {
factory ApiClient(Dio dio) = _ApiClient;
@POST('/auth/login')
Future<LoginResponse> login(@Body() LoginRequest request);
@GET('/users/{id}')
Future<UserModel> getUser(@Path('id') String id);
}
@riverpod
ApiClient apiClient(Ref ref) {
final dio = Dio(BaseOptions(baseUrl: Env.apiBaseUrl))
..interceptors.addAll([
AuthInterceptor(ref),
LogInterceptor(requestBody: true, responseBody: true),
]);
return ApiClient(dio);
}~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.