From 0ba83858fe438baf752b88b49dcd6c6bbe8dc80f Mon Sep 17 00:00:00 2001 From: usbe Date: Fri, 11 Jul 2025 15:07:36 +0200 Subject: [PATCH] bio login --- lib/entities/auth_token.dart | 67 +++- lib/main.dart | 21 +- lib/pages/login_page.dart | 178 ++++++++--- lib/services/api_service.dart | 5 +- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 8 + pubspec.lock | 291 +++++++++++++++++- pubspec.yaml | 5 + test/widget_test.dart | 14 +- .../flutter/generated_plugin_registrant.cc | 6 + windows/flutter/generated_plugins.cmake | 2 + 12 files changed, 522 insertions(+), 80 deletions(-) diff --git a/lib/entities/auth_token.dart b/lib/entities/auth_token.dart index 7feb3a8..2b53a35 100644 --- a/lib/entities/auth_token.dart +++ b/lib/entities/auth_token.dart @@ -1,26 +1,59 @@ -class auth_token { - // Istanza privata statica - static final auth_token _instance = auth_token._internal(); +import 'package:flutter/foundation.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:local_auth/local_auth.dart'; +import 'package:device_info_plus/device_info_plus.dart'; - // Variabile per salvare il token +class AuthToken { + static final AuthToken _instance = AuthToken._internal(); + static AuthToken get instance => _instance; + AuthToken._internal(); + + final _storage = const FlutterSecureStorage(); String? _token; - // Costruttore privato - auth_token._internal(); - - // Factory per ritornare sempre la stessa istanza - factory auth_token() { - return _instance; - } - - // Getter e setter per il token String? get token => _token; - set token(String? value) { - _token = value; + + Future isMobilePlatform() async { + if (kIsWeb) return false; + final deviceInfo = DeviceInfoPlugin(); + if (defaultTargetPlatform == TargetPlatform.android) return true; + if (defaultTargetPlatform == TargetPlatform.iOS) return true; + return false; } - // Metodo per cancellare il token (es. logout) - void clear() { + Future saveToken(String token) async { + _token = token; + if (await isMobilePlatform()) { + await _storage.write(key: 'AuthToken', value: token); + } + } + + Future loadTokenWithBiometric() async { + if (!await isMobilePlatform()) return false; + + final auth = LocalAuthentication(); + final canAuth = + await auth.canCheckBiometrics || await auth.isDeviceSupported(); + if (!canAuth) return false; + + final authenticated = await auth.authenticate( + localizedReason: 'Autenticati per accedere', + options: const AuthenticationOptions(biometricOnly: true), + ); + + if (authenticated) { + final stored = await _storage.read(key: 'AuthToken'); + _token = stored; + return stored != null; + } + + return false; + } + + Future clear() async { _token = null; + if (await isMobilePlatform()) { + await _storage.delete(key: 'AuthToken'); + } } } diff --git a/lib/main.dart b/lib/main.dart index e7a52df..bb9091c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,21 +1,30 @@ import 'package:flutter/material.dart'; import 'pages/login_page.dart'; +import 'entities/auth_token.dart'; +import 'pages/dashboard_page.dart'; -void main() { - runApp(const MyApp()); +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + final authToken = AuthToken.instance; + + final isLoggedIn = await authToken.loadTokenWithBiometric(); + //runApp(const MyApp()); + runApp(MyApp(startInDashboard: isLoggedIn)); } class MyApp extends StatelessWidget { - const MyApp({super.key}); + final bool startInDashboard; + const MyApp({super.key, required this.startInDashboard}); @override Widget build(BuildContext context) { return MaterialApp( - title: 'Login App', + title: 'Gestione Vigne', theme: ThemeData( - primarySwatch: Colors.deepPurple, + colorScheme: ColorScheme.fromSeed(seedColor: Colors.green), + useMaterial3: true, ), - home: const LoginPage(), + home: startInDashboard ? const DashboardPage() : const LoginPage(), ); } } diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index bcba96a..6e31f26 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb; +import 'package:shared_preferences/shared_preferences.dart'; import '../services/api_service.dart'; -import 'dashboard_page.dart'; import '../entities/auth_token.dart'; +import 'dashboard_page.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @@ -14,33 +16,65 @@ class _LoginPageState extends State { final _emailController = TextEditingController(); final _passwordController = TextEditingController(); bool _loading = false; + bool _rememberMe = false; String? _error; - void _login() async { + bool get isMobile { + final platform = defaultTargetPlatform; + return platform == TargetPlatform.android || + platform == TargetPlatform.iOS || + (kIsWeb && MediaQuery.of(context).size.width < 600); + } + + @override + void initState() { + super.initState(); + _checkRememberedUser(); + } + + Future _checkRememberedUser() async { + final prefs = await SharedPreferences.getInstance(); + final token = prefs.getString('token'); + if (token != null && prefs.getBool('rememberMe') == true) { + AuthToken.instance.saveToken(token); + if (mounted) { + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (_) => const DashboardPage()), + ); + } + } + } + + Future _login() async { setState(() { _loading = true; _error = null; }); - final email = _emailController.text.trim(); - final password = _passwordController.text; - try { - final token = await ApiService.login(email, password); - auth_token().token = token; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Login riuscito! Token: $token')), + final token = await ApiService.login( + _emailController.text.trim(), + _passwordController.text, ); + await AuthToken.instance.saveToken(token); + final prefs = await SharedPreferences.getInstance(); + if (_rememberMe) { + await prefs.setString('token', token); + await prefs.setBool('rememberMe', true); + } else { + await prefs.remove('token'); + await prefs.setBool('rememberMe', false); + } + if (!mounted) return; - Navigator.of(context).pushReplacement( + Navigator.pushReplacement( + context, MaterialPageRoute(builder: (_) => const DashboardPage()), ); } catch (e) { setState(() { _error = e.toString(); - }); - } finally { - setState(() { _loading = false; }); } @@ -48,36 +82,102 @@ class _LoginPageState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Login')), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( + final content = Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.eco, size: 64, color: Color(0xFF388E3C)), + const SizedBox(height: 12), + const Text( + 'Gestione Vigna', + style: TextStyle( + fontSize: 26, + fontWeight: FontWeight.bold, + color: Color(0xFF2E7D32)), + ), + const SizedBox(height: 24), + TextField( + controller: _emailController, + decoration: const InputDecoration( + labelText: 'Email o Username', + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.person), + ), + ), + const SizedBox(height: 16), + TextField( + controller: _passwordController, + obscureText: true, + decoration: const InputDecoration( + labelText: 'Password', + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.lock), + ), + ), + const SizedBox(height: 12), + Row( children: [ - TextField( - controller: _emailController, - decoration: const InputDecoration(labelText: 'Email'), + Checkbox( + value: _rememberMe, + activeColor: const Color(0xFF43A047), + onChanged: (value) { + setState(() { + _rememberMe = value ?? false; + }); + }, ), - const SizedBox(height: 12), - TextField( - controller: _passwordController, - obscureText: true, - decoration: const InputDecoration(labelText: 'Password'), - ), - const SizedBox(height: 24), - if (_error != null) - Text( - _error!, - style: const TextStyle(color: Colors.red), - ), - _loading - ? const CircularProgressIndicator() - : ElevatedButton( - onPressed: _login, - child: const Text('Login'), - ), + const Text("Ricorda accesso"), ], ), + if (_error != null) + Text( + _error!, + style: const TextStyle(color: Colors.red), + ), + const SizedBox(height: 10), + _loading + ? const CircularProgressIndicator() + : SizedBox( + width: double.infinity, + child: ElevatedButton.icon( + icon: const Icon(Icons.login), + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF43A047), + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8)), + ), + label: const Text('Login', style: TextStyle(fontSize: 18)), + onPressed: _login, + ), + ), + ], + ); + + return Scaffold( + backgroundColor: Colors.white, + body: LayoutBuilder( + builder: (context, constraints) { + return Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(24), + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: isMobile ? double.infinity : 400, + ), + child: Card( + elevation: isMobile ? 0 : 8, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16)), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 24, vertical: 32), + child: content, + ), + ), + ), + ), + ); + }, ), ); } diff --git a/lib/services/api_service.dart b/lib/services/api_service.dart index 0f9dc12..cd67149 100644 --- a/lib/services/api_service.dart +++ b/lib/services/api_service.dart @@ -29,7 +29,8 @@ class ApiService { if (response.statusCode == 200) { final data = jsonDecode(response.body); _token = data['access_token']; // salva il token in memoria - return _token!; + AuthToken.instance.saveToken(_token!); + return '${response.statusCode}'; } else { throw Exception( 'Login fallito: ${response.statusCode} - ${response.body}'); @@ -37,7 +38,7 @@ class ApiService { } static Future checkLogin() async { - final token = auth_token().token; + final token = AuthToken.instance.token; if (token == null) { throw Exception('Token non disponibile. Effettua il login prima.'); } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index e71a16d..d0e7f79 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); + flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..b29e9ba 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + flutter_secure_storage_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..a05e955 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,14 @@ import FlutterMacOS import Foundation +import device_info_plus +import flutter_secure_storage_macos +import path_provider_foundation +import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) + FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index f8bab7a..d99b07a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -49,6 +49,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + device_info_plus: + dependency: "direct main" + description: + name: device_info_plus + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" + url: "https://pub.dev" + source: hosted + version: "7.0.2" fake_async: dependency: transitive description: @@ -57,6 +73,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" flutter: dependency: "direct main" description: flutter @@ -78,11 +110,72 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" + url: "https://pub.dev" + source: hosted + version: "2.0.19" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" + source: hosted + version: "9.2.4" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" http: dependency: transitive description: @@ -103,10 +196,18 @@ packages: dependency: transitive description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.19.0" + version: "0.18.1" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" latlong2: dependency: "direct main" description: @@ -131,6 +232,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + local_auth: + dependency: "direct main" + description: + name: local_auth + sha256: "27679ed8e0d7daab2357db6bb7076359e083a56b295c0c59723845301da6aed9" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + local_auth_android: + dependency: transitive + description: + name: local_auth_android + sha256: e0e5b1ea247c5a0951c13a7ee13dc1beae69750e6a2e1910d1ed6a3cd4d56943 + url: "https://pub.dev" + source: hosted + version: "1.0.38" + local_auth_ios: + dependency: transitive + description: + name: local_auth_ios + sha256: eb283b530029b334698918f1e282d4483737cbca972ff21b9193be3d6de8e2b8 + url: "https://pub.dev" + source: hosted + version: "1.1.6" + local_auth_platform_interface: + dependency: transitive + description: + name: local_auth_platform_interface + sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a" + url: "https://pub.dev" + source: hosted + version: "1.0.10" + local_auth_windows: + dependency: transitive + description: + name: local_auth_windows + sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5 + url: "https://pub.dev" + source: hosted + version: "1.0.11" logger: dependency: transitive description: @@ -179,6 +320,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + url: "https://pub.dev" + source: hosted + version: "2.1.4" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d + url: "https://pub.dev" + source: hosted + version: "2.2.4" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" polylabel: dependency: transitive description: @@ -195,6 +400,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + url: "https://pub.dev" + source: hosted + version: "2.2.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" + url: "https://pub.dev" + source: hosted + version: "2.3.5" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "2ba0510d3017f91655b7543e9ee46d48619de2a2af38e5c790423f7007c7ccc1" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "398084b47b7f92110683cac45c6dc4aae853db47e470e5ddcd52cab7f7196ab2" + url: "https://pub.dev" + source: hosted + version: "2.4.0" sky_engine: dependency: transitive description: flutter @@ -280,6 +541,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.0" + win32: + dependency: transitive + description: + name: win32 + sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" + url: "https://pub.dev" + source: hosted + version: "5.2.0" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" + url: "https://pub.dev" + source: hosted + version: "1.1.2" wkt_parser: dependency: transitive description: @@ -288,6 +565,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" sdks: dart: ">=3.2.2 <4.0.0" - flutter: ">=3.10.0" + flutter: ">=3.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index 325fefa..696d7f6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,6 +32,11 @@ dependencies: sdk: flutter flutter_map: ^6.1.0 latlong2: ^0.9.0 + #http: ^0.13.6 + flutter_secure_storage: ^9.0.0 + local_auth: ^2.1.6 + device_info_plus: ^9.1.2 + shared_preferences: ^2.2.2 # The following adds the Cupertino Icons font to your application. diff --git a/test/widget_test.dart b/test/widget_test.dart index 0c571ac..a622ed1 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -13,18 +13,6 @@ import 'package:uwine_portal/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); + await tester.pumpWidget(MyApp(startInDashboard: false)); }); } diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..011734d 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,12 @@ #include "generated_plugin_registrant.h" +#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + FlutterSecureStorageWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); + LocalAuthPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("LocalAuthPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..11485fc 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,8 @@ # list(APPEND FLUTTER_PLUGIN_LIST + flutter_secure_storage_windows + local_auth_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST