]> git.proxmox.com Git - flutter/proxmox_login_manager.git/commitdiff
migrate to sound null safety
authorThomas Lamprecht <t.lamprecht@proxmox.com>
Fri, 12 Mar 2021 14:12:26 +0000 (15:12 +0100)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Fri, 12 Mar 2021 14:12:29 +0000 (15:12 +0100)
tried to strike a balance between auto migration and checking what
can actually get null, or improve that by promoting some variables.

Learnt that for class fields dart cannot promote nullable fields to
null-safe after a if (field == null) return 'early'; check, so we
need to introduce a intermediate varibable and check on that - a bit
of a bummer..

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
lib/extension.dart
lib/proxmox_general_settings_form.dart
lib/proxmox_general_settings_model.dart
lib/proxmox_login_form.dart
lib/proxmox_login_model.dart
lib/proxmox_login_selector.dart
lib/proxmox_tfa_form.dart
pubspec.yaml

index 785aba77033949c46a29a00b9e8cc92152cba661..2915bf67bfb5caf9fb94eb09b05166eca3e98a44 100644 (file)
@@ -5,7 +5,7 @@ extension BuiltValueListBuilderExtension<V extends Built<V, B>,
     B extends Builder<V, B>> on ListBuilder<Built<V, B>> {
   void rebuildWhere(bool Function(V) test, void Function(B) updates) {
     for (var i = 0; i != this.length; ++i) {
-      if (test(this[i])) this[i] = this[i].rebuild(updates);
+      if (test(this[i] as V)) this[i] = this[i].rebuild(updates);
     }
   }
 }
index f3fdd4459155954ede7a8a3115392ab854bfc9ef..8183fb85bf3312b370b5d83f0acee738eb6c98c4 100644 (file)
@@ -9,7 +9,7 @@ class ProxmoxGeneralSettingsForm extends StatefulWidget {
 
 class _ProxmoxGeneralSettingsFormState
     extends State<ProxmoxGeneralSettingsForm> {
-  Future<ProxmoxGeneralSettingsModel> _settings;
+  Future<ProxmoxGeneralSettingsModel>? _settings;
   @override
   void initState() {
     super.initState();
@@ -26,14 +26,14 @@ class _ProxmoxGeneralSettingsFormState
           future: _settings,
           builder: (context, snaptshot) {
             if (snaptshot.hasData) {
-              final settings = snaptshot.data;
+              final settings = snaptshot.data!;
               return SingleChildScrollView(
                 child: Column(
                   children: [
                     SwitchListTile(
                       title: Text('Validate SSL connections'),
                       subtitle: Text('e.g. validates certificates'),
-                      value: settings.sslValidation,
+                      value: settings.sslValidation!,
                       onChanged: (value) async {
                         await settings
                             .rebuild((b) => b.sslValidation = value)
index 72fc0f74c3256c915da3f4fc63d2be65755a859f..767bdaf9d328318f1eee5a7024dc7d9575a65b7d 100644 (file)
@@ -10,11 +10,11 @@ part 'proxmox_general_settings_model.g.dart';
 abstract class ProxmoxGeneralSettingsModel
     implements
         Built<ProxmoxGeneralSettingsModel, ProxmoxGeneralSettingsModelBuilder> {
-  bool get sslValidation;
+  bool? get sslValidation;
 
   ProxmoxGeneralSettingsModel._();
   factory ProxmoxGeneralSettingsModel(
-          [void Function(ProxmoxGeneralSettingsModelBuilder) updates]) =
+          [void Function(ProxmoxGeneralSettingsModelBuilder)? updates]) =
       _$ProxmoxGeneralSettingsModel;
 
   factory ProxmoxGeneralSettingsModel.defaultValues() =>
@@ -22,12 +22,14 @@ abstract class ProxmoxGeneralSettingsModel
 
   Object toJson() {
     return serializers.serializeWith(
-        ProxmoxGeneralSettingsModel.serializer, this);
+            ProxmoxGeneralSettingsModel.serializer, this) ??
+        {};
   }
 
-  static ProxmoxGeneralSettingsModel fromJson(Object json) {
+  static ProxmoxGeneralSettingsModel fromJson(Object? json) {
     return serializers.deserializeWith(
-        ProxmoxGeneralSettingsModel.serializer, json);
+            ProxmoxGeneralSettingsModel.serializer, json) ??
+        ProxmoxGeneralSettingsModel();
   }
 
   static Serializer<ProxmoxGeneralSettingsModel> get serializer =>
@@ -37,7 +39,7 @@ abstract class ProxmoxGeneralSettingsModel
     final SharedPreferences prefs = await SharedPreferences.getInstance();
     if (prefs.containsKey('ProxmoxGeneralSettings')) {
       final decodedJson =
-          json.decode(prefs.getString('ProxmoxGeneralSettings'));
+          json.decode(prefs.getString('ProxmoxGeneralSettings')!);
       return fromJson(decodedJson);
     }
     return ProxmoxGeneralSettingsModel.defaultValues();
index 3115ffdca79873983dfe4f3ea8ce3771014240a0..512d7412b7aca471f0169f6bd950bb6dd0db2335 100644 (file)
@@ -1,4 +1,5 @@
 import 'dart:io';
+import 'dart:async';
 
 import 'package:flutter/material.dart';
 import 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart'
@@ -11,8 +12,8 @@ import 'package:proxmox_login_manager/proxmox_tfa_form.dart';
 import 'package:proxmox_login_manager/extension.dart';
 
 class ProxmoxProgressModel {
-  bool inProgress;
-  String message;
+  bool inProgress = false;
+  String message = 'Loading...';
   ProxmoxProgressModel({
     this.inProgress = false,
     this.message = 'Loading...',
@@ -24,21 +25,21 @@ class ProxmoxLoginForm extends StatefulWidget {
   final FormFieldValidator<String> originValidator;
   final TextEditingController usernameController;
   final TextEditingController passwordController;
-  final List<PveAccessDomainModel> accessDomains;
-  final PveAccessDomainModel selectedDomain;
-  final ValueChanged<PveAccessDomainModel> onDomainChanged;
-  final Function onPasswordSubmitted;
-  final Function onOriginSubmitted;
+  final List<PveAccessDomainModel?>? accessDomains;
+  final PveAccessDomainModel? selectedDomain;
+  final ValueChanged<PveAccessDomainModel?> onDomainChanged;
+  final Function? onPasswordSubmitted;
+  final Function? onOriginSubmitted;
 
   const ProxmoxLoginForm({
-    Key key,
-    @required this.originController,
-    @required this.usernameController,
-    @required this.passwordController,
-    @required this.accessDomains,
-    @required this.originValidator,
+    Key? key,
+    /*required*/ required this.originController,
+    /*required*/ /*required*/ required this.usernameController,
+    /*required*/ required this.passwordController,
+    /*required*/ required this.accessDomains,
+    /*required*/ required this.originValidator,
     this.selectedDomain,
-    @required this.onDomainChanged,
+    /*required*/ required this.onDomainChanged,
     this.onPasswordSubmitted,
     this.onOriginSubmitted,
   }) : super(key: key);
@@ -49,7 +50,7 @@ class ProxmoxLoginForm extends StatefulWidget {
 
 class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
   bool _obscure = true;
-  FocusNode passwordFocusNode;
+  FocusNode? passwordFocusNode;
 
   @override
   Widget build(BuildContext context) {
@@ -62,7 +63,7 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
             helperText: 'Protocol (https) and default port (8006) implied'),
         controller: widget.originController,
         validator: widget.originValidator,
-        onFieldSubmitted: (value) => widget.onOriginSubmitted(),
+        onFieldSubmitted: (value) => widget.onOriginSubmitted!(),
       );
     }
 
@@ -85,7 +86,7 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
             ),
             controller: widget.usernameController,
             validator: (value) {
-              if (value.isEmpty) {
+              if (value!.isEmpty) {
                 return 'Please enter username';
               }
               return null;
@@ -94,10 +95,10 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
           ),
           DropdownButtonFormField(
             decoration: InputDecoration(icon: Icon(Icons.domain)),
-            items: widget.accessDomains
+            items: widget.accessDomains!
                 .map((e) => DropdownMenuItem(
                       child: ListTile(
-                        title: Text(e.realm),
+                        title: Text(e!.realm!),
                         subtitle: Text(e.comment ?? ''),
                       ),
                       value: e,
@@ -105,7 +106,7 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
                 .toList(),
             onChanged: widget.onDomainChanged,
             selectedItemBuilder: (context) =>
-                widget.accessDomains.map((e) => Text(e.realm)).toList(),
+                widget.accessDomains!.map((e) => Text(e!.realm!)).toList(),
             value: widget.selectedDomain,
           ),
           Stack(
@@ -120,12 +121,12 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
                 autocorrect: false,
                 focusNode: passwordFocusNode,
                 validator: (value) {
-                  if (value.isEmpty) {
+                  if (value!.isEmpty) {
                     return 'Please enter password';
                   }
                   return null;
                 },
-                onFieldSubmitted: (value) => widget.onPasswordSubmitted(),
+                onFieldSubmitted: (value) => widget.onPasswordSubmitted!(),
                 autofillHints: [AutofillHints.password],
               ),
               Align(
@@ -155,12 +156,12 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
 }
 
 class ProxmoxLoginPage extends StatefulWidget {
-  final ProxmoxLoginModel userModel;
-  final bool isCreate;
-  final String ticket;
+  final ProxmoxLoginModel? userModel;
+  final bool? isCreate;
+  final String? ticket;
 
   const ProxmoxLoginPage({
-    Key key,
+    Key? key,
     this.userModel,
     this.isCreate,
     this.ticket = '',
@@ -173,10 +174,10 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
   final _originController = TextEditingController();
   final _usernameController = TextEditingController();
   final _passwordController = TextEditingController();
-  Future<List<PveAccessDomainModel>> _accessDomains;
-  PveAccessDomainModel _selectedDomain;
+  Future<List<PveAccessDomainModel?>?>? _accessDomains;
+  PveAccessDomainModel? _selectedDomain;
   final _formKey = GlobalKey<FormState>();
-  ProxmoxProgressModel _progressModel;
+  ProxmoxProgressModel _progressModel = ProxmoxProgressModel();
   bool _submittButtonEnabled = true;
 
   @override
@@ -184,16 +185,16 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
     super.initState();
     final userModel = widget.userModel;
     _progressModel = ProxmoxProgressModel();
-    if (!widget.isCreate && userModel != null) {
+    if (!widget.isCreate! && userModel != null) {
       _progressModel
         ..inProgress = true
         ..message = 'Connection test...';
       _originController.text =
           '${userModel.origin?.host}:${userModel.origin?.port}';
       _accessDomains = _getAccessDomains();
-      _usernameController.text = userModel.username;
-      if (widget.ticket.isNotEmpty && userModel.activeSession) {
-        _onLoginButtonPressed(ticket: widget.ticket, mRealm: userModel.realm);
+      _usernameController.text = userModel.username!;
+      if (widget.ticket!.isNotEmpty && userModel.activeSession) {
+        _onLoginButtonPressed(ticket: widget.ticket!, mRealm: userModel.realm);
       }
     }
   }
@@ -221,7 +222,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                     height: MediaQuery.of(context).size.height),
                 child: Padding(
                   padding: const EdgeInsets.all(8.0),
-                  child: FutureBuilder<List<PveAccessDomainModel>>(
+                  child: FutureBuilder<List<PveAccessDomainModel?>?>(
                       future: _accessDomains,
                       builder: (context, snapshot) {
                         return Form(
@@ -229,7 +230,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                           onChanged: () {
                             setState(() {
                               _submittButtonEnabled =
-                                  _formKey.currentState.validate();
+                                  _formKey.currentState!.validate();
                             });
                           },
                           child: Column(
@@ -251,7 +252,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                               ProxmoxLoginForm(
                                 originController: _originController,
                                 originValidator: (value) {
-                                  if (value.isEmpty) {
+                                  if (value == null || value.isEmpty) {
                                     return 'Please enter origin';
                                   }
                                   if (value.startsWith('https://') ||
@@ -277,7 +278,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                 onOriginSubmitted: _submittButtonEnabled
                                     ? () {
                                         final isValid =
-                                            _formKey.currentState.validate();
+                                            _formKey.currentState!.validate();
                                         setState(() {
                                           _submittButtonEnabled = isValid;
                                         });
@@ -292,7 +293,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                 onPasswordSubmitted: _submittButtonEnabled
                                     ? () {
                                         final isValid =
-                                            _formKey.currentState.validate();
+                                            _formKey.currentState!.validate();
                                         setState(() {
                                           _submittButtonEnabled = isValid;
                                         });
@@ -312,7 +313,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                         onPressed: _submittButtonEnabled
                                             ? () {
                                                 final isValid = _formKey
-                                                    .currentState
+                                                    .currentState!
                                                     .validate();
                                                 setState(() {
                                                   _submittButtonEnabled =
@@ -340,7 +341,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
                                         onPressed: _submittButtonEnabled
                                             ? () {
                                                 final isValid = _formKey
-                                                    .currentState
+                                                    .currentState!
                                                     .validate();
                                                 setState(() {
                                                   _submittButtonEnabled =
@@ -377,7 +378,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
   }
 
   Future<void> _onLoginButtonPressed(
-      {String ticket = '', String mRealm}) async {
+      {String ticket = '', String? mRealm}) async {
     setState(() {
       _progressModel
         ..inProgress = true
@@ -395,29 +396,22 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
       final realm = _selectedDomain?.realm ?? mRealm;
 
       var client = await proxclient.authenticate(
-          '$username@$realm', password, origin, settings.sslValidation);
+          '$username@$realm', password, origin, settings.sslValidation!);
 
       if (client.credentials.tfa) {
-        client = await Navigator.of(context).push(MaterialPageRoute(
+        client = await (Navigator.of(context).push(MaterialPageRoute(
           builder: (context) => ProxmoxTfaForm(
             apiClient: client,
           ),
-        ));
-
-        if (client == null) {
-          setState(() {
-            _progressModel.inProgress = false;
-          });
-          return;
-        }
+        )) as FutureOr<ProxmoxApiClient>);
       }
 
       final status = await client.getClusterStatus();
       final hostname =
-          status.singleWhere((element) => element.local ?? false).name;
+          status.singleWhere((element) => element!.local ?? false)!.name;
       var loginStorage = await ProxmoxLoginStorage.fromLocalStorage();
 
-      if (widget.isCreate) {
+      if (widget.isCreate!) {
         final newLogin = ProxmoxLoginModel((b) => b
           ..origin = origin
           ..username = username
@@ -426,9 +420,9 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
           ..ticket = client.credentials.ticket
           ..hostname = hostname);
 
-        loginStorage = loginStorage.rebuild((b) => b..logins.add(newLogin));
+        loginStorage = loginStorage!.rebuild((b) => b..logins.add(newLogin));
       } else {
-        loginStorage = loginStorage.rebuild((b) => b
+        loginStorage = loginStorage!.rebuild((b) => b
           ..logins.rebuildWhere(
               (m) => m == widget.userModel,
               (b) => b
@@ -478,7 +472,7 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
     });
   }
 
-  Future<List<PveAccessDomainModel>> _getAccessDomains() async {
+  Future<List<PveAccessDomainModel?>?> _getAccessDomains() async {
     setState(() {
       _progressModel
         ..inProgress = true
@@ -495,10 +489,10 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
     }
 
     final settings = await ProxmoxGeneralSettingsModel.fromLocalStorage();
-    List<PveAccessDomainModel> response;
+    List<PveAccessDomainModel?>? response;
     try {
       response =
-          await proxclient.accessDomains(apiBaseUrl, settings.sslValidation);
+          await proxclient.accessDomains(apiBaseUrl, settings.sslValidation!);
     } on proxclient.ProxmoxApiException catch (e) {
       showDialog(
         context: context,
@@ -531,10 +525,10 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
       }
     }
 
-    response?.sort((a, b) => a.realm.compareTo(b.realm));
+    response?.sort((a, b) => a!.realm!.compareTo(b!.realm!));
 
     final selection = response?.singleWhere(
-      (e) => e.realm == widget.userModel?.realm,
+      (e) => e!.realm == widget.userModel?.realm,
       orElse: () => response?.first,
     );
 
@@ -557,8 +551,8 @@ class _ProxmoxLoginPageState extends State<ProxmoxLoginPage> {
 
 class ProxmoxProgressOverlay extends StatelessWidget {
   const ProxmoxProgressOverlay({
-    Key key,
-    @required this.message,
+    Key? key,
+    required this.message,
   }) : super(key: key);
 
   final String message;
@@ -593,8 +587,8 @@ class ProxmoxApiErrorDialog extends StatelessWidget {
   final proxclient.ProxmoxApiException exception;
 
   const ProxmoxApiErrorDialog({
-    Key key,
-    @required this.exception,
+    Key? key,
+    required this.exception,
   }) : super(key: key);
 
   @override
@@ -616,7 +610,7 @@ class ProxmoxApiErrorDialog extends StatelessWidget {
 
 class ProxmoxCertificateErrorDialog extends StatelessWidget {
   const ProxmoxCertificateErrorDialog({
-    Key key,
+    Key? key,
   }) : super(key: key);
 
   @override
index af7c4fd09529fcad942401e047ad7121fc46d76a..ac10b222a9295ab7ac5c9aa555aac4802cef2f2a 100644 (file)
@@ -11,28 +11,28 @@ part 'proxmox_login_model.g.dart';
 
 abstract class ProxmoxLoginStorage
     implements Built<ProxmoxLoginStorage, ProxmoxLoginStorageBuilder> {
-  BuiltList<ProxmoxLoginModel> get logins;
+  BuiltList<ProxmoxLoginModel>? get logins;
 
   ProxmoxLoginStorage._();
   factory ProxmoxLoginStorage(
-          [void Function(ProxmoxLoginStorageBuilder) updates]) =
+          [void Function(ProxmoxLoginStorageBuilder)? updates]) =
       _$ProxmoxLoginStorage;
 
-  Object toJson() {
+  Object? toJson() {
     return serializers.serializeWith(ProxmoxLoginStorage.serializer, this);
   }
 
-  static ProxmoxLoginStorage fromJson(Object json) {
+  static ProxmoxLoginStorage? fromJson(Object? json) {
     return serializers.deserializeWith(ProxmoxLoginStorage.serializer, json);
   }
 
   static Serializer<ProxmoxLoginStorage> get serializer =>
       _$proxmoxLoginStorageSerializer;
 
-  static Future<ProxmoxLoginStorage> fromLocalStorage() async {
+  static Future<ProxmoxLoginStorage?> fromLocalStorage() async {
     final SharedPreferences prefs = await SharedPreferences.getInstance();
     if (prefs.containsKey('ProxmoxLoginList')) {
-      final decodedJson = json.decode(prefs.getString('ProxmoxLoginList'));
+      final decodedJson = json.decode(prefs.getString('ProxmoxLoginList')!);
       return fromJson(decodedJson);
     }
     return ProxmoxLoginStorage();
@@ -44,62 +44,66 @@ abstract class ProxmoxLoginStorage
   }
 
   Future<proxclient.ProxmoxApiClient> recoverLatestSession() async {
-    final latestSession = logins.singleWhere((e) => e.ticket.isNotEmpty);
+    final latestSession = logins!.singleWhere((e) => e.ticket!.isNotEmpty);
     final settings = await ProxmoxGeneralSettingsModel.fromLocalStorage();
     final apiClient = await proxclient.authenticate(latestSession.fullUsername,
-        latestSession.ticket, latestSession.origin, settings.sslValidation);
+        latestSession.ticket!, latestSession.origin!, settings.sslValidation!);
     return apiClient;
   }
 
   Future<void> invalidateAllSessions() async {
     final invalidatedList =
-        logins.map((e) => e.rebuild((login) => login..ticket = ''));
+        logins!.map((e) => e.rebuild((login) => login..ticket = ''));
     await rebuild((e) => e.logins.replace(invalidatedList)).saveToDisk();
   }
 }
 
 abstract class ProxmoxLoginModel
     implements Built<ProxmoxLoginModel, ProxmoxLoginModelBuilder> {
-  Uri get origin;
+  Uri? get origin;
 
-  String get username;
+  String? get username;
 
-  String get realm;
+  String? get realm;
 
-  ProxmoxProductType get productType;
+  ProxmoxProductType? get productType;
 
-  String get ticket;
+  String? get ticket;
 
   /// The username with the corresponding realm e.g. root@pam
   String get fullUsername => '$username@$realm';
 
   bool get activeSession =>
-      ticket != null && ticket.isNotEmpty && !ticketExpired();
+      ticket != null && ticket!.isNotEmpty && !ticketExpired();
 
-  @nullable
-  String get hostname;
+  String? get hostname;
 
   String get fullHostname {
-    if (origin.host == hostname) {
-      return hostname;
+    var location = origin;
+    if (location == null) {
+      return 'Unknown';
     }
-    if (hostname == null || hostname.isEmpty) {
-      return origin.host;
+    var name = hostname;
+    if (name == null || name.isEmpty) {
+      return location.host;
     }
-
-    return '${origin.host} - $hostname';
+    if (location.host == name) {
+      return name;
+    }
+    return '${location.host} - $hostname';
   }
 
   ProxmoxLoginModel._();
 
-  factory ProxmoxLoginModel([void Function(ProxmoxLoginModelBuilder) updates]) =
-      _$ProxmoxLoginModel;
+  factory ProxmoxLoginModel(
+      [void Function(ProxmoxLoginModelBuilder)? updates]) = _$ProxmoxLoginModel;
 
-  Map<String, dynamic> toJson() {
-    return serializers.serializeWith(ProxmoxLoginModel.serializer, this);
+  Map<String, dynamic>? toJson() {
+    return serializers.serializeWith(ProxmoxLoginModel.serializer, this)
+        as Map<String, dynamic>?;
   }
 
-  static ProxmoxLoginModel fromJson(Map<String, dynamic> json) {
+  static ProxmoxLoginModel? fromJson(Map<String, dynamic> json) {
     return serializers.deserializeWith(ProxmoxLoginModel.serializer, json);
   }
 
@@ -108,9 +112,9 @@ abstract class ProxmoxLoginModel
 
   bool ticketExpired() {
     final ticketRegex = RegExp(r'(PVE|PMG)(?:QUAR)?:(?:(\S+):)?([A-Z0-9]{8})::')
-        .firstMatch(ticket);
+        .firstMatch(ticket!)!;
     final time = DateTime.fromMillisecondsSinceEpoch(
-        int.parse(ticketRegex.group(3), radix: 16) * 1000);
+        int.parse(ticketRegex.group(3)!, radix: 16) * 1000);
     return DateTime.now().isAfter(time.add(Duration(hours: 1)));
   }
 }
index f6ca2fac9d774abdd875f16aa75539a22da93a2b..52655231b0fa813e3ddceade733eb033a3ed6511 100644 (file)
@@ -1,4 +1,5 @@
 import 'package:flutter/material.dart';
+import 'package:built_collection/built_collection.dart';
 import 'package:proxmox_login_manager/proxmox_general_settings_form.dart';
 import 'package:proxmox_login_manager/proxmox_login_form.dart';
 import 'package:proxmox_login_manager/proxmox_login_model.dart';
@@ -9,16 +10,16 @@ import 'package:proxmox_login_manager/extension.dart';
 typedef OnLoginCallback = Function(proxclient.ProxmoxApiClient client);
 
 class ProxmoxLoginSelector extends StatefulWidget {
-  final OnLoginCallback onLogin;
+  final OnLoginCallback? onLogin;
 
-  const ProxmoxLoginSelector({Key key, this.onLogin}) : super(key: key);
+  const ProxmoxLoginSelector({Key? key, this.onLogin}) : super(key: key);
 
   @override
   _ProxmoxLoginSelectorState createState() => _ProxmoxLoginSelectorState();
 }
 
 class _ProxmoxLoginSelectorState extends State<ProxmoxLoginSelector> {
-  Future<ProxmoxLoginStorage> loginStorage;
+  Future<ProxmoxLoginStorage?>? loginStorage;
   @override
   void initState() {
     super.initState();
@@ -57,7 +58,7 @@ class _ProxmoxLoginSelectorState extends State<ProxmoxLoginSelector> {
                 })
           ],
         ),
-        body: FutureBuilder<ProxmoxLoginStorage>(
+        body: FutureBuilder<ProxmoxLoginStorage?>(
             future: loginStorage,
             builder: (context, snapshot) {
               if (!snapshot.hasData) {
@@ -65,13 +66,15 @@ class _ProxmoxLoginSelectorState extends State<ProxmoxLoginSelector> {
                   child: CircularProgressIndicator(),
                 );
               }
-              if (snapshot.hasData && (snapshot.data.logins?.isEmpty ?? true)) {
+              if (snapshot.hasData &&
+                  (snapshot.data!.logins?.isEmpty ?? true)) {
                 return Center(
                   child: Text('Add an account'),
                 );
               }
               var items = <Widget>[];
-              final logins = snapshot.data?.logins;
+              final BuiltList<ProxmoxLoginModel> logins =
+                  snapshot.data?.logins ?? BuiltList<ProxmoxLoginModel>();
 
               final activeSessions =
                   logins.rebuild((b) => b.where((b) => b.activeSession));
@@ -101,7 +104,7 @@ class _ProxmoxLoginSelectorState extends State<ProxmoxLoginSelector> {
                                       leading: Icon(Icons.logout),
                                       title: Text('Logout'),
                                       onTap: () async {
-                                        await snapshot.data
+                                        await snapshot.data!
                                             .rebuild((b) => b.logins
                                                 .rebuildWhere((m) => s == m,
                                                     (b) => b..ticket = ''))
@@ -127,7 +130,7 @@ class _ProxmoxLoginSelectorState extends State<ProxmoxLoginSelector> {
                     ),
                   ),
                 ),
-                ...logins.where((b) => !b.activeSession)?.map((l) => ListTile(
+                ...logins.where((b) => !b.activeSession).map((l) => ListTile(
                       title: Text(l.fullHostname),
                       subtitle: Text(l.fullUsername),
                       trailing: Icon(Icons.navigate_next),
@@ -139,7 +142,7 @@ class _ProxmoxLoginSelectorState extends State<ProxmoxLoginSelector> {
                                     leading: Icon(Icons.delete),
                                     title: Text('Delete'),
                                     onTap: () async {
-                                      await snapshot.data
+                                      await snapshot.data!
                                           .rebuild((b) => b.logins.remove(l))
                                           .saveToDisk();
                                       refreshFromStorage();
@@ -164,7 +167,7 @@ class _ProxmoxLoginSelectorState extends State<ProxmoxLoginSelector> {
     );
   }
 
-  Future<void> _login({ProxmoxLoginModel user, bool isCreate = false}) async {
+  Future<void> _login({ProxmoxLoginModel? user, bool isCreate = false}) async {
     final client = await Navigator.of(context).push(MaterialPageRoute(
         builder: (context) => ProxmoxLoginPage(
               userModel: user,
@@ -173,7 +176,7 @@ class _ProxmoxLoginSelectorState extends State<ProxmoxLoginSelector> {
             )));
     refreshFromStorage();
     if (client != null) {
-      widget.onLogin(client);
+      widget.onLogin!(client);
     }
   }
 
index 001cc52c226e37dc13b76e92254a180d460572c5..db3cfa73cd6a4e08029400901ab062ee81c63517 100644 (file)
@@ -3,9 +3,9 @@ import 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart';
 import 'package:proxmox_login_manager/proxmox_login_form.dart';
 
 class ProxmoxTfaForm extends StatefulWidget {
-  final ProxmoxApiClient apiClient;
+  final ProxmoxApiClient? apiClient;
 
-  const ProxmoxTfaForm({Key key, this.apiClient}) : super(key: key);
+  const ProxmoxTfaForm({Key? key, this.apiClient}) : super(key: key);
 
   @override
   _ProxmoxTfaFormState createState() => _ProxmoxTfaFormState();
@@ -108,7 +108,7 @@ class _ProxmoxTfaFormState extends State<ProxmoxTfaForm> {
     });
     try {
       final client =
-          await widget.apiClient.finishTfaChallenge(_codeController.text);
+          await widget.apiClient!.finishTfaChallenge(_codeController.text);
       Navigator.of(context).pop(client);
     } on ProxmoxApiException catch (e) {
       showDialog(
index f6db36f5fb3d9254b72262e367f09b4fd83ff6f4..3ea9d83726f2fbaf2f7dd3594522917ef44231d1 100644 (file)
@@ -4,7 +4,7 @@ version: 0.0.1
 author: Tim Marx <t.marx@proxmox.com>
 
 environment:
-  sdk: ">=2.7.0 <3.0.0"
+  sdk: '>=2.12.0 <3.0.0'
 
 dependencies:
   flutter: