From dbe67c2f817637d6e76009b59d83769663b61586 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Wed, 21 Sep 2022 15:27:35 -0500 Subject: [PATCH] Upgrade to flutter 3 (#70) Co-authored-by: John Maguire --- README.md | 29 ++- android/.gitignore | 1 + android/app/build.gradle | 63 +++--- android/app/src/main/AndroidManifest.xml | 4 +- .../kotlin/net/defined/mobile_nebula/Share.kt | 6 +- android/build.gradle | 8 +- android/gradle.properties | 1 - .../gradle/wrapper/gradle-wrapper.properties | 3 +- android/mobileNebula/build.gradle | 6 + android/settings.gradle | 20 +- android/settings_aar.gradle | 1 - gen-artifacts.sh | 6 +- ios/Flutter/AppFrameworkInfo.plist | 2 +- ios/Podfile.lock | 85 ++++---- ios/Runner.xcodeproj/project.pbxproj | 26 +-- ios/Runner/Info.plist | 2 + ios/Runner/Sites.swift | 4 +- lib/components/CIDRField.dart | 30 ++- lib/components/CIDRFormField.dart | 54 ++--- lib/components/FormPage.dart | 20 +- lib/components/IPAndPortField.dart | 16 +- lib/components/IPAndPortFormField.dart | 52 ++--- lib/components/IPField.dart | 16 +- lib/components/IPFormField.dart | 26 +-- lib/components/PlatformTextFormField.dart | 34 +-- lib/components/SimplePage.dart | 22 +- lib/components/SiteItem.dart | 6 +- lib/components/SpecialButton.dart | 27 ++- lib/components/SpecialTextField.dart | 53 ++--- lib/components/config/ConfigButtonItem.dart | 4 +- lib/components/config/ConfigCheckboxItem.dart | 11 +- lib/components/config/ConfigHeader.dart | 6 +- lib/components/config/ConfigItem.dart | 8 +- lib/components/config/ConfigPageItem.dart | 10 +- lib/components/config/ConfigSection.dart | 11 +- lib/components/config/ConfigTextItem.dart | 8 +- lib/models/CIDR.dart | 10 +- lib/models/Certificate.dart | 12 +- lib/models/HostInfo.dart | 37 +++- lib/models/Hostmap.dart | 2 +- lib/models/IPAndPort.dart | 15 +- lib/models/Site.dart | 199 ++++++++++-------- lib/models/StaticHosts.dart | 11 +- lib/models/UnsafeRoute.dart | 12 +- lib/screens/AboutScreen.dart | 15 +- lib/screens/HostInfoScreen.dart | 26 ++- lib/screens/MainScreen.dart | 69 +++--- lib/screens/SiteDetailScreen.dart | 32 +-- lib/screens/SiteLogsScreen.dart | 2 +- lib/screens/SiteTunnelsScreen.dart | 18 +- .../siteConfig/AddCertificateScreen.dart | 93 ++++---- lib/screens/siteConfig/AdvancedScreen.dart | 45 ++-- lib/screens/siteConfig/CAListScreen.dart | 54 ++--- .../siteConfig/CertificateDetailsScreen.dart | 63 +++--- lib/screens/siteConfig/CipherScreen.dart | 12 +- .../siteConfig/LogVerbosityScreen.dart | 12 +- .../siteConfig/RenderedConfigScreen.dart | 6 +- lib/screens/siteConfig/SiteConfigScreen.dart | 49 +++-- .../siteConfig/StaticHostmapScreen.dart | 47 +++-- lib/screens/siteConfig/StaticHostsScreen.dart | 28 ++- lib/screens/siteConfig/UnsafeRouteScreen.dart | 25 ++- .../siteConfig/UnsafeRoutesScreen.dart | 15 +- lib/services/settings.dart | 4 - lib/services/share.dart | 34 +-- lib/services/storage.dart | 2 +- lib/services/utils.dart | 18 +- lib/validators/ipValidator.dart | 6 +- nebula/go.mod | 3 +- nebula/go.sum | 12 +- pubspec.lock | 90 ++++---- pubspec.yaml | 12 +- 71 files changed, 953 insertions(+), 818 deletions(-) create mode 100644 android/mobileNebula/build.gradle delete mode 100644 android/settings_aar.gradle diff --git a/README.md b/README.md index bf3b1fa..833f966 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,30 @@ -# Dependencies +## Setting up dev environment -- [`flutter`](https://flutter.dev/docs/get-started/install) -- [`gomobile`](https://godoc.org/golang.org/x/mobile/cmd/gomobile) +Install all of the following things: + +- [`xcode`](https://apps.apple.com/us/app/xcode/) - [`android-studio`](https://developer.android.com/studio) -- [Enable NDK](https://developer.android.com/studio/projects/install-ndk) Check local.properties for current NDK version +- [`flutter` 3.3.2](https://docs.flutter.dev/get-started/install) +- [`gomobile`](https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile) +- [Flutter Android Studio Extension](https://docs.flutter.dev/get-started/editor?tab=androidstudio) -Currently using flutter 2.0.5 +Ensure your path is set up correctly to execute flutter -Copy env.sh.example to env.sh and update your PATH variable to expose both flutter and go bin directories +Run `flutter doctor` and fix everything it complains before proceeding - ```export PATH="$PATH:/path/to/go/bin:/path/to/flutter/bin"``` +*NOTE* on iOS, always open `Runner.xcworkspace` and NOT the `Runner.xccodeproj` +### Before first compile + +- Copy `env.sh.example` and set it up for your machine +- Ensure you have run `gomobile init` +- In Android Studio, make sure you have the current ndk installed by going to Tools -> SDK Manager, go to the SDK Tools tab, check the `Show package details` box, expand the NDK section and select `21.1.6352462` version. +- Ensure you have downloaded an ndk via android studio, this is likely not the default one and you need to check the + `Show package details` box to select the correct version. The correct version comes from the error when you try and compile +- Make sure you have `gem` installed with `sudo gem install` +- If on MacOS arm, `sudo gem install ffi -- --enable-libffi-alloc` + +If you are having issues with iOS pods, try blowing it all away! `cd ios && rm -rf Pods/ Podfile.lock && pod install --repo-update` # Formatting @@ -21,7 +35,6 @@ Use: flutter format lib/ test/ -l 120 ``` - # Release Update `version` in `pubspec.yaml` to reflect this release, then diff --git a/android/.gitignore b/android/.gitignore index e71e2bf..5e071fd 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -6,3 +6,4 @@ gradle-wrapper.jar /local.properties GeneratedPluginRegistrant.java /build/build-attribution/ +/mobileNebula/mobileNebula.aar diff --git a/android/app/build.gradle b/android/app/build.gradle index e1c0bcb..0bf0c5c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -32,23 +32,28 @@ if (keystorePropertiesFile.exists()) { } android { - compileSdkVersion 30 + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } sourceSets { main.java.srcDirs += 'src/main/kotlin' } - lintOptions { - disable 'InvalidPackage' - } - defaultConfig { applicationId "net.defined.mobile_nebula" - minSdkVersion 29 - targetSdkVersion 30 + minSdkVersion 29 //flutter.minSdkVersion + targetSdkVersion 30 //flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } signingConfigs { @@ -62,17 +67,18 @@ android { buildTypes { release { - signingConfig signingConfigs.release - - // We are disabling minification and proguard because it wrecks the crypto for storing keys - // Ideally we would turn these on. We had issues with gson as well but resolved those with proguardFiles - shrinkResources false - minifyEnabled false - useProguard false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - +// signingConfig signingConfigs.release +// +// // We are disabling minification and proguard because it wrecks the crypto for storing keys +// // Ideally we would turn these on. We had issues with gson as well but resolved those with proguardFiles +// shrinkResources false +// minifyEnabled false +// useProguard false +// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' +// resValue 'string', 'app_name', '"Nebula"' } + debug { resValue 'string', 'app_name', '"Nebula-DEBUG"' applicationIdSuffix '.debug' @@ -84,26 +90,9 @@ flutter { source '../..' } -repositories { - flatDir { - dirs 'src/main/libs' - } -} - dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation "androidx.security:security-crypto:1.0.0-rc02" + implementation "androidx.security:security-crypto:1.0.0" implementation 'com.google.code.gson:gson:2.8.6' - - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' - implementation (name:'mobileNebula', ext:'aar') { - exec { - workingDir '../../' - environment("ANDROID_NDK_HOME", android.ndkDirectory) - environment("ANDROID_HOME", android.sdkDirectory) - commandLine './gen-artifacts.sh', 'android' - } - } -} + implementation project(':mobileNebula') +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6459ffc..5a9c21e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -9,11 +9,12 @@ @@ -21,6 +22,7 @@ plugins.load(reader) } -} +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" \ No newline at end of file diff --git a/android/settings_aar.gradle b/android/settings_aar.gradle deleted file mode 100644 index e7b4def..0000000 --- a/android/settings_aar.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':app' diff --git a/gen-artifacts.sh b/gen-artifacts.sh index 2c8893b..c915adc 100755 --- a/gen-artifacts.sh +++ b/gen-artifacts.sh @@ -16,9 +16,9 @@ if [ "$1" = "ios" ]; then elif [ "$1" = "android" ]; then # Build nebula for android make mobileNebula.aar - mkdir -p ../android/app/src/main/libs - rm -rf ../android/app/src/main/libs/mobileNebula.aar - cp mobileNebula.aar ../android/app/src/main/libs/mobileNebula.aar + mkdir -p ../android/mobileNebula + rm -rf ../android/mobileNebula/mobileNebula.aar + cp mobileNebula.aar ../android/mobileNebula/mobileNebula.aar else echo "Error: unsupported target os $1" diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index f2872cf..4f8d4d2 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 9e51132..2537f26 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,64 +1,57 @@ PODS: - - barcode_scan (0.0.1): - - Flutter - - MTBBarcodeScanner - - SwiftProtobuf - - DKImagePickerController/Core (4.3.0): + - DKImagePickerController/Core (4.3.4): - DKImagePickerController/ImageDataManager - DKImagePickerController/Resource - - DKImagePickerController/ImageDataManager (4.3.0) - - DKImagePickerController/PhotoGallery (4.3.0): + - DKImagePickerController/ImageDataManager (4.3.4) + - DKImagePickerController/PhotoGallery (4.3.4): - DKImagePickerController/Core - DKPhotoGallery - - DKImagePickerController/Resource (4.3.0) - - DKPhotoGallery (0.0.15): - - DKPhotoGallery/Core (= 0.0.15) - - DKPhotoGallery/Model (= 0.0.15) - - DKPhotoGallery/Preview (= 0.0.15) - - DKPhotoGallery/Resource (= 0.0.15) + - DKImagePickerController/Resource (4.3.4) + - DKPhotoGallery (0.0.17): + - DKPhotoGallery/Core (= 0.0.17) + - DKPhotoGallery/Model (= 0.0.17) + - DKPhotoGallery/Preview (= 0.0.17) + - DKPhotoGallery/Resource (= 0.0.17) - SDWebImage - - SDWebImageFLPlugin - - DKPhotoGallery/Core (0.0.15): + - SwiftyGif + - DKPhotoGallery/Core (0.0.17): - DKPhotoGallery/Model - DKPhotoGallery/Preview - SDWebImage - - SDWebImageFLPlugin - - DKPhotoGallery/Model (0.0.15): + - SwiftyGif + - DKPhotoGallery/Model (0.0.17): - SDWebImage - - SDWebImageFLPlugin - - DKPhotoGallery/Preview (0.0.15): + - SwiftyGif + - DKPhotoGallery/Preview (0.0.17): - DKPhotoGallery/Model - DKPhotoGallery/Resource - SDWebImage - - SDWebImageFLPlugin - - DKPhotoGallery/Resource (0.0.15): + - SwiftyGif + - DKPhotoGallery/Resource (0.0.17): - SDWebImage - - SDWebImageFLPlugin + - SwiftyGif - file_picker (0.0.1): - DKImagePickerController/PhotoGallery - Flutter - - FLAnimatedImage (1.0.12) - Flutter (1.0.0) - - MTBBarcodeScanner (5.0.11) + - flutter_barcode_scanner (2.0.0): + - Flutter - package_info (0.0.1): - Flutter - path_provider_ios (0.0.1): - Flutter - - SDWebImage (5.8.0): - - SDWebImage/Core (= 5.8.0) - - SDWebImage/Core (5.8.0) - - SDWebImageFLPlugin (0.4.0): - - FLAnimatedImage (>= 1.0.11) - - SDWebImage/Core (~> 5.6) - - SwiftProtobuf (1.9.0) + - SDWebImage (5.13.3): + - SDWebImage/Core (= 5.13.3) + - SDWebImage/Core (5.13.3) + - SwiftyGif (5.4.3) - SwiftyJSON (5.0.1) - url_launcher_ios (0.0.1): - Flutter DEPENDENCIES: - - barcode_scan (from `.symlinks/plugins/barcode_scan/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) + - flutter_barcode_scanner (from `.symlinks/plugins/flutter_barcode_scanner/ios`) - package_info (from `.symlinks/plugins/package_info/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - SwiftyJSON (~> 5.0) @@ -68,20 +61,17 @@ SPEC REPOS: trunk: - DKImagePickerController - DKPhotoGallery - - FLAnimatedImage - - MTBBarcodeScanner - SDWebImage - - SDWebImageFLPlugin - - SwiftProtobuf + - SwiftyGif - SwiftyJSON EXTERNAL SOURCES: - barcode_scan: - :path: ".symlinks/plugins/barcode_scan/ios" file_picker: :path: ".symlinks/plugins/file_picker/ios" Flutter: :path: Flutter + flutter_barcode_scanner: + :path: ".symlinks/plugins/flutter_barcode_scanner/ios" package_info: :path: ".symlinks/plugins/package_info/ios" path_provider_ios: @@ -90,21 +80,18 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/url_launcher_ios/ios" SPEC CHECKSUMS: - barcode_scan: a5c27959edfafaa0c771905bad0b29d6d39e4479 - DKImagePickerController: 397702a3590d4958fad336e9a77079935c500ddb - DKPhotoGallery: e880aef16c108333240e1e7327896f2ea380f4f0 - file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 - FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31 - Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb + DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac + DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 + file_picker: 817ab1d8cd2da9d2da412a417162deee3500fc95 + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + flutter_barcode_scanner: 7a1144744c28dc0c57a8de7218ffe5ec59a9e4bf package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 - SDWebImage: 84000f962cbfa70c07f19d2234cbfcf5d779b5dc - SDWebImageFLPlugin: 6c2295fb1242d44467c6c87dc5db6b0a13228fd8 - SwiftProtobuf: ecbec1be9036d15655f6b3443a1c4ea693c97932 + SDWebImage: af5bbffef2cde09f148d826f9733dcde1a9414cd + SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780 SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af PODFILE CHECKSUM: 92e176614f91c6517d4254a0edec8b66f076c77e -COCOAPODS: 1.10.1 +COCOAPODS: 1.11.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 57ebed7..a584599 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -339,14 +339,11 @@ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework", "${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework", - "${BUILT_PRODUCTS_DIR}/FLAnimatedImage/FLAnimatedImage.framework", - "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImageFLPlugin/SDWebImageFLPlugin.framework", - "${BUILT_PRODUCTS_DIR}/SwiftProtobuf/SwiftProtobuf.framework", + "${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework", "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework", - "${BUILT_PRODUCTS_DIR}/barcode_scan/barcode_scan.framework", "${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework", + "${BUILT_PRODUCTS_DIR}/flutter_barcode_scanner/flutter_barcode_scanner.framework", "${BUILT_PRODUCTS_DIR}/package_info/package_info.framework", "${BUILT_PRODUCTS_DIR}/path_provider_ios/path_provider_ios.framework", "${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework", @@ -355,14 +352,11 @@ outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FLAnimatedImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageFLPlugin.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftProtobuf.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/barcode_scan.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_barcode_scanner.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_ios.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework", @@ -558,7 +552,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = 576H3XS7FP; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -596,7 +590,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = NebulaNetworkExtension/NebulaNetworkExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = 576H3XS7FP; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -637,7 +631,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = NebulaNetworkExtension/NebulaNetworkExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = 576H3XS7FP; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -675,7 +669,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = NebulaNetworkExtension/NebulaNetworkExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = 576H3XS7FP; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -816,7 +810,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = 576H3XS7FP; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -852,7 +846,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = 576H3XS7FP; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index ebbc462..05db08d 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -47,5 +47,7 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + diff --git a/ios/Runner/Sites.swift b/ios/Runner/Sites.swift index a8cf879..a21479e 100644 --- a/ios/Runner/Sites.swift +++ b/ios/Runner/Sites.swift @@ -133,13 +133,13 @@ class SiteUpdater: NSObject, FlutterStreamHandler { let connected = self.site.connected self.site.status = statusString[self.site.manager!.connection.status] self.site.connected = statusMap[self.site.manager!.connection.status] - + // Check to see if we just moved to connected and if we have a start function to call when that happens if self.site.connected! && connected != self.site.connected && self.startFunc != nil { self.startFunc!() self.startFunc = nil } - + let d: Dictionary = [ "connected": self.site.connected!, "status": self.site.status!, diff --git a/lib/components/CIDRField.dart b/lib/components/CIDRField.dart index 5a98321..1f3d472 100644 --- a/lib/components/CIDRField.dart +++ b/lib/components/CIDRField.dart @@ -8,7 +8,7 @@ import 'IPField.dart'; //TODO: Support initialValue class CIDRField extends StatefulWidget { const CIDRField({ - Key key, + Key? key, this.ipHelp = "ip address", this.autoFocus = false, this.focusNode, @@ -21,12 +21,12 @@ class CIDRField extends StatefulWidget { final String ipHelp; final bool autoFocus; - final FocusNode focusNode; - final FocusNode nextFocusNode; - final ValueChanged onChanged; - final TextInputAction textInputAction; - final TextEditingController ipController; - final TextEditingController bitsController; + final FocusNode? focusNode; + final FocusNode? nextFocusNode; + final ValueChanged? onChanged; + final TextInputAction? textInputAction; + final TextEditingController? ipController; + final TextEditingController? bitsController; @override _CIDRFieldState createState() => _CIDRFieldState(); @@ -44,7 +44,7 @@ class _CIDRFieldState extends State { void initState() { //TODO: this won't track external controller changes appropriately cidr.ip = widget.ipController?.text ?? ""; - cidr.bits = int.tryParse(widget.bitsController?.text ?? ""); + cidr.bits = int.tryParse(widget.bitsController?.text ?? "") ?? 0; super.initState(); } @@ -66,8 +66,12 @@ class _CIDRFieldState extends State { focusNode: widget.focusNode, nextFocusNode: bitsFocus, onChanged: (val) { + if (widget.onChanged == null) { + return; + } + cidr.ip = val; - widget.onChanged(cidr); + widget.onChanged!(cidr); }, controller: widget.ipController, ))), @@ -81,8 +85,12 @@ class _CIDRFieldState extends State { nextFocusNode: widget.nextFocusNode, controller: widget.bitsController, onChanged: (val) { - cidr.bits = int.tryParse(val ?? ""); - widget.onChanged(cidr); + if (widget.onChanged == null) { + return; + } + + cidr.bits = int.tryParse(val) ?? 0; + widget.onChanged!(cidr); }, maxLength: 2, inputFormatters: [FilteringTextInputFormatter.digitsOnly], diff --git a/lib/components/CIDRFormField.dart b/lib/components/CIDRFormField.dart index 0a18742..ff00553 100644 --- a/lib/components/CIDRFormField.dart +++ b/lib/components/CIDRFormField.dart @@ -6,15 +6,15 @@ import 'package:mobile_nebula/validators/ipValidator.dart'; class CIDRFormField extends FormField { //TODO: onSaved, validator, auto-validate, enabled? CIDRFormField({ - Key key, + Key? key, autoFocus = false, enableIPV6 = false, focusNode, nextFocusNode, - ValueChanged onChanged, - FormFieldSetter onSaved, + ValueChanged? onChanged, + FormFieldSetter? onSaved, textInputAction, - CIDR initialValue, + CIDR? initialValue, this.ipController, this.bitsController, }) : super( @@ -30,14 +30,14 @@ class CIDRFormField extends FormField { return 'Please enter a valid ip address'; } - if (cidr.bits == null || cidr.bits > 32 || cidr.bits < 0) { + if (cidr.bits > 32 || cidr.bits < 0) { return "Please enter a valid number of bits"; } return null; }, builder: (FormFieldState field) { - final _CIDRFormField state = field; + final _CIDRFormField state = field as _CIDRFormField; void onChangedHandler(CIDR value) { if (onChanged != null) { @@ -57,50 +57,50 @@ class CIDRFormField extends FormField { bitsController: state._effectiveBitsController, ), field.hasError - ? Text(field.errorText, + ? Text(field.errorText ?? "Unknown error", style: TextStyle(color: CupertinoColors.systemRed.resolveFrom(field.context), fontSize: 13), textAlign: TextAlign.end) : Container(height: 0) ]); }); - final TextEditingController ipController; - final TextEditingController bitsController; + final TextEditingController? ipController; + final TextEditingController? bitsController; @override _CIDRFormField createState() => _CIDRFormField(); } class _CIDRFormField extends FormFieldState { - TextEditingController _ipController; - TextEditingController _bitsController; + TextEditingController? _ipController = TextEditingController(); + TextEditingController? _bitsController = TextEditingController(); - TextEditingController get _effectiveIPController => widget.ipController ?? _ipController; - TextEditingController get _effectiveBitsController => widget.bitsController ?? _bitsController; + TextEditingController get _effectiveIPController => widget.ipController ?? _ipController!; + TextEditingController get _effectiveBitsController => widget.bitsController ?? _bitsController!; @override - CIDRFormField get widget => super.widget; + CIDRFormField get widget => super.widget as CIDRFormField; @override void initState() { super.initState(); if (widget.ipController == null) { - _ipController = TextEditingController(text: widget.initialValue.ip); + _ipController = TextEditingController(text: widget.initialValue?.ip); } else { - widget.ipController.addListener(_handleControllerChanged); + widget.ipController!.addListener(_handleControllerChanged); } if (widget.bitsController == null) { _bitsController = TextEditingController(text: widget.initialValue?.bits?.toString() ?? ""); } else { - widget.bitsController.addListener(_handleControllerChanged); + widget.bitsController!.addListener(_handleControllerChanged); } } @override void didUpdateWidget(CIDRFormField oldWidget) { super.didUpdateWidget(oldWidget); - var update = CIDR(ip: widget.ipController?.text, bits: int.tryParse(widget.bitsController?.text ?? "") ?? null); + var update = CIDR(ip: widget.ipController?.text ?? "", bits: int.tryParse(widget.bitsController?.text ?? "") ?? 0); bool shouldUpdate = false; if (widget.ipController != oldWidget.ipController) { @@ -108,12 +108,12 @@ class _CIDRFormField extends FormFieldState { widget.ipController?.addListener(_handleControllerChanged); if (oldWidget.ipController != null && widget.ipController == null) { - _ipController = TextEditingController.fromValue(oldWidget.ipController.value); + _ipController = TextEditingController.fromValue(oldWidget.ipController!.value); } if (widget.ipController != null) { shouldUpdate = true; - update.ip = widget.ipController.text; + update.ip = widget.ipController!.text; if (oldWidget.ipController == null) _ipController = null; } } @@ -123,12 +123,12 @@ class _CIDRFormField extends FormFieldState { widget.bitsController?.addListener(_handleControllerChanged); if (oldWidget.bitsController != null && widget.bitsController == null) { - _bitsController = TextEditingController.fromValue(oldWidget.bitsController.value); + _bitsController = TextEditingController.fromValue(oldWidget.bitsController!.value); } if (widget.bitsController != null) { shouldUpdate = true; - update.bits = int.parse(widget.bitsController.text); + update.bits = int.parse(widget.bitsController!.text); if (oldWidget.bitsController == null) _bitsController = null; } } @@ -149,8 +149,8 @@ class _CIDRFormField extends FormFieldState { void reset() { super.reset(); setState(() { - _effectiveIPController.text = widget.initialValue.ip; - _effectiveBitsController.text = widget.initialValue.bits.toString(); + _effectiveIPController.text = widget.initialValue?.ip ?? ""; + _effectiveBitsController.text = widget.initialValue?.bits.toString() ?? ""; }); } @@ -163,7 +163,11 @@ class _CIDRFormField extends FormFieldState { // example, the reset() method. In such cases, the FormField value will // already have been set. final effectiveBits = int.parse(_effectiveBitsController.text); - if (_effectiveIPController.text != value.ip || effectiveBits != value.bits) { + if (value == null) { + return; + } + + if (_effectiveIPController.text != value!.ip || effectiveBits != value!.bits) { didChange(CIDR(ip: _effectiveIPController.text, bits: effectiveBits)); } } diff --git a/lib/components/FormPage.dart b/lib/components/FormPage.dart index 3634f72..fcfcfb8 100644 --- a/lib/components/FormPage.dart +++ b/lib/components/FormPage.dart @@ -8,11 +8,11 @@ import 'package:mobile_nebula/services/utils.dart'; /// SimplePage with a form and built in validation and confirmation to discard changes if any are made class FormPage extends StatefulWidget { const FormPage( - {Key key, - this.title, - @required this.child, - @required this.onSave, - @required this.changed, + {Key? key, + required this.title, + required this.child, + required this.onSave, + required this.changed, this.hideSave = false, this.scrollController}) : super(key: key); @@ -20,7 +20,7 @@ class FormPage extends StatefulWidget { final String title; final Function onSave; final Widget child; - final ScrollController scrollController; + final ScrollController? scrollController; /// If you need the page to progress to a certain point before saving, control it here final bool hideSave; @@ -90,11 +90,15 @@ class _FormPageState extends State { Utils.trailingSaveWidget( context, () { - if (!_formKey.currentState.validate()) { + if (_formKey.currentState == null) { return; } - _formKey.currentState.save(); + if (!_formKey.currentState!.validate()) { + return; + } + + _formKey.currentState!.save(); widget.onSave(); }, ) diff --git a/lib/components/IPAndPortField.dart b/lib/components/IPAndPortField.dart index f70a564..463000d 100644 --- a/lib/components/IPAndPortField.dart +++ b/lib/components/IPAndPortField.dart @@ -8,13 +8,13 @@ import 'IPField.dart'; //TODO: Support initialValue class IPAndPortField extends StatefulWidget { const IPAndPortField({ - Key key, + Key? key, this.ipOnly = false, this.ipHelp = "ip address", this.autoFocus = false, this.focusNode, this.nextFocusNode, - this.onChanged, + required this.onChanged, this.textInputAction, this.noBorder = false, this.ipTextAlign, @@ -25,14 +25,14 @@ class IPAndPortField extends StatefulWidget { final String ipHelp; final bool ipOnly; final bool autoFocus; - final FocusNode focusNode; - final FocusNode nextFocusNode; + final FocusNode? focusNode; + final FocusNode? nextFocusNode; final ValueChanged onChanged; - final TextInputAction textInputAction; + final TextInputAction? textInputAction; final bool noBorder; - final TextAlign ipTextAlign; - final TextEditingController ipController; - final TextEditingController portController; + final TextAlign? ipTextAlign; + final TextEditingController? ipController; + final TextEditingController? portController; @override _IPAndPortFieldState createState() => _IPAndPortFieldState(); diff --git a/lib/components/IPAndPortFormField.dart b/lib/components/IPAndPortFormField.dart index 0171db0..c9e8c4c 100644 --- a/lib/components/IPAndPortFormField.dart +++ b/lib/components/IPAndPortFormField.dart @@ -8,17 +8,17 @@ import 'IPAndPortField.dart'; class IPAndPortFormField extends FormField { //TODO: onSaved, validator, auto-validate, enabled? IPAndPortFormField({ - Key key, + Key? key, ipOnly = false, enableIPV6 = false, ipHelp = "ip address", autoFocus = false, focusNode, nextFocusNode, - ValueChanged onChanged, - FormFieldSetter onSaved, + ValueChanged? onChanged, + FormFieldSetter? onSaved, textInputAction, - IPAndPort initialValue, + IPAndPort? initialValue, noBorder, ipTextAlign = TextAlign.center, this.ipController, @@ -36,14 +36,14 @@ class IPAndPortFormField extends FormField { return ipOnly ? 'Please enter a valid ip address' : 'Please enter a valid ip address or dns name'; } - if (ipAndPort.port == null || ipAndPort.port > 65535 || ipAndPort.port < 0) { + if (ipAndPort.port == null || ipAndPort.port! > 65535 || ipAndPort.port! < 0) { return "Please enter a valid port"; } return null; }, builder: (FormFieldState field) { - final _IPAndPortFormField state = field; + final _IPAndPortFormField state = field as _IPAndPortFormField; void onChangedHandler(IPAndPort value) { if (onChanged != null) { @@ -67,42 +67,42 @@ class IPAndPortFormField extends FormField { ipTextAlign: ipTextAlign, ), field.hasError - ? Text(field.errorText, + ? Text(field.errorText!, style: TextStyle(color: CupertinoColors.systemRed.resolveFrom(field.context), fontSize: 13)) : Container(height: 0) ]); }); - final TextEditingController ipController; - final TextEditingController portController; + final TextEditingController? ipController; + final TextEditingController? portController; @override _IPAndPortFormField createState() => _IPAndPortFormField(); } class _IPAndPortFormField extends FormFieldState { - TextEditingController _ipController; - TextEditingController _portController; + TextEditingController? _ipController; + TextEditingController? _portController; - TextEditingController get _effectiveIPController => widget.ipController ?? _ipController; - TextEditingController get _effectivePortController => widget.portController ?? _portController; + TextEditingController get _effectiveIPController => widget.ipController ?? _ipController!; + TextEditingController get _effectivePortController => widget.portController ?? _portController!; @override - IPAndPortFormField get widget => super.widget; + IPAndPortFormField get widget => super.widget as IPAndPortFormField; @override void initState() { super.initState(); if (widget.ipController == null) { - _ipController = TextEditingController(text: widget.initialValue.ip); + _ipController = TextEditingController(text: widget.initialValue?.ip ?? ""); } else { - widget.ipController.addListener(_handleControllerChanged); + widget.ipController!.addListener(_handleControllerChanged); } if (widget.portController == null) { _portController = TextEditingController(text: widget.initialValue?.port?.toString() ?? ""); } else { - widget.portController.addListener(_handleControllerChanged); + widget.portController!.addListener(_handleControllerChanged); } } @@ -118,12 +118,12 @@ class _IPAndPortFormField extends FormFieldState { widget.ipController?.addListener(_handleControllerChanged); if (oldWidget.ipController != null && widget.ipController == null) { - _ipController = TextEditingController.fromValue(oldWidget.ipController.value); + _ipController = TextEditingController.fromValue(oldWidget.ipController!.value); } if (widget.ipController != null) { shouldUpdate = true; - update.ip = widget.ipController.text; + update.ip = widget.ipController!.text; if (oldWidget.ipController == null) _ipController = null; } } @@ -133,12 +133,12 @@ class _IPAndPortFormField extends FormFieldState { widget.portController?.addListener(_handleControllerChanged); if (oldWidget.portController != null && widget.portController == null) { - _portController = TextEditingController.fromValue(oldWidget.portController.value); + _portController = TextEditingController.fromValue(oldWidget.portController!.value); } if (widget.portController != null) { shouldUpdate = true; - update.port = int.parse(widget.portController.text); + update.port = int.parse(widget.portController!.text); if (oldWidget.portController == null) _portController = null; } } @@ -159,8 +159,8 @@ class _IPAndPortFormField extends FormFieldState { void reset() { super.reset(); setState(() { - _effectiveIPController.text = widget.initialValue.ip; - _effectivePortController.text = widget.initialValue.port.toString(); + _effectiveIPController.text = widget.initialValue?.ip ?? ""; + _effectivePortController.text = widget.initialValue?.port?.toString() ?? ""; }); } @@ -173,7 +173,11 @@ class _IPAndPortFormField extends FormFieldState { // example, the reset() method. In such cases, the FormField value will // already have been set. final effectivePort = int.parse(_effectivePortController.text); - if (_effectiveIPController.text != value.ip || effectivePort != value.port) { + if (value == null) { + return; + } + + if (_effectiveIPController.text != value!.ip || effectivePort != value!.port) { didChange(IPAndPort(ip: _effectiveIPController.text, port: effectivePort)); } } diff --git a/lib/components/IPField.dart b/lib/components/IPField.dart index 38bc527..0abadd3 100644 --- a/lib/components/IPField.dart +++ b/lib/components/IPField.dart @@ -8,16 +8,16 @@ class IPField extends StatelessWidget { final String help; final bool ipOnly; final bool autoFocus; - final FocusNode focusNode; - final FocusNode nextFocusNode; - final ValueChanged onChanged; + final FocusNode? focusNode; + final FocusNode? nextFocusNode; + final ValueChanged? onChanged; final EdgeInsetsGeometry textPadding; - final TextInputAction textInputAction; + final TextInputAction? textInputAction; final controller; final textAlign; const IPField( - {Key key, + {Key? key, this.ipOnly = false, this.help = "ip address", this.autoFocus = false, @@ -33,7 +33,7 @@ class IPField extends StatelessWidget { @override Widget build(BuildContext context) { var textStyle = CupertinoTheme.of(context).textTheme.textStyle; - final double ipWidth = ipOnly ? Utils.textSize("000000000000000", textStyle).width + 12 : null; + final double? ipWidth = ipOnly ? Utils.textSize("000000000000000", textStyle).width + 12 : null; return SizedBox( width: ipWidth, @@ -64,7 +64,7 @@ class IPTextInputFormatter extends TextInputFormatter { (String substring) { return whitelistedPattern .allMatches(substring) - .map((Match match) => match.group(0)) + .map((Match match) => match.group(0)!) .join() .replaceAll(RegExp(r','), '.'); }, @@ -79,7 +79,7 @@ TextEditingValue _selectionAwareTextManipulation( final int selectionStartIndex = value.selection.start; final int selectionEndIndex = value.selection.end; String manipulatedText; - TextSelection manipulatedSelection; + TextSelection? manipulatedSelection; if (selectionStartIndex < 0 || selectionEndIndex < 0) { manipulatedText = substringManipulation(value.text); } else { diff --git a/lib/components/IPFormField.dart b/lib/components/IPFormField.dart index 8156179..4811db9 100644 --- a/lib/components/IPFormField.dart +++ b/lib/components/IPFormField.dart @@ -9,15 +9,15 @@ import 'IPField.dart'; class IPFormField extends FormField { //TODO: validator, auto-validate, enabled? IPFormField({ - Key key, + Key? key, ipOnly = false, enableIPV6 = false, help = "ip address", autoFocus = false, focusNode, nextFocusNode, - ValueChanged onChanged, - FormFieldSetter onSaved, + ValueChanged? onChanged, + FormFieldSetter? onSaved, textPadding = const EdgeInsets.all(6.0), textInputAction, initialValue, @@ -41,7 +41,7 @@ class IPFormField extends FormField { return null; }, builder: (FormFieldState field) { - final _IPFormField state = field; + final _IPFormField state = field as _IPFormField; void onChangedHandler(String value) { if (onChanged != null) { @@ -64,7 +64,7 @@ class IPFormField extends FormField { textAlign: textAlign), field.hasError ? Text( - field.errorText, + field.errorText!, style: TextStyle(color: CupertinoColors.systemRed.resolveFrom(field.context), fontSize: 13), textAlign: textAlign, ) @@ -72,19 +72,19 @@ class IPFormField extends FormField { ]); }); - final TextEditingController controller; + final TextEditingController? controller; @override _IPFormField createState() => _IPFormField(); } class _IPFormField extends FormFieldState { - TextEditingController _controller; + TextEditingController? _controller; - TextEditingController get _effectiveController => widget.controller ?? _controller; + TextEditingController get _effectiveController => widget.controller ?? _controller!; @override - IPFormField get widget => super.widget; + IPFormField get widget => super.widget as IPFormField; @override void initState() { @@ -92,7 +92,7 @@ class _IPFormField extends FormFieldState { if (widget.controller == null) { _controller = TextEditingController(text: widget.initialValue); } else { - widget.controller.addListener(_handleControllerChanged); + widget.controller!.addListener(_handleControllerChanged); } } @@ -104,9 +104,9 @@ class _IPFormField extends FormFieldState { widget.controller?.addListener(_handleControllerChanged); if (oldWidget.controller != null && widget.controller == null) - _controller = TextEditingController.fromValue(oldWidget.controller.value); + _controller = TextEditingController.fromValue(oldWidget.controller!.value); if (widget.controller != null) { - setValue(widget.controller.text); + setValue(widget.controller!.text); if (oldWidget.controller == null) _controller = null; } } @@ -122,7 +122,7 @@ class _IPFormField extends FormFieldState { void reset() { super.reset(); setState(() { - _effectiveController.text = widget.initialValue; + _effectiveController.text = widget.initialValue ?? ""; }); } diff --git a/lib/components/PlatformTextFormField.dart b/lib/components/PlatformTextFormField.dart index 9129f0b..6ff9107 100644 --- a/lib/components/PlatformTextFormField.dart +++ b/lib/components/PlatformTextFormField.dart @@ -6,14 +6,14 @@ import 'package:mobile_nebula/components/SpecialTextField.dart'; class PlatformTextFormField extends FormField { //TODO: auto-validate, enabled? PlatformTextFormField( - {Key key, + {Key? key, widgetKey, this.controller, focusNode, nextFocusNode, - TextInputType keyboardType, + TextInputType? keyboardType, textInputAction, - List inputFormatters, + List? inputFormatters, textAlign, autofocus, maxLines = 1, @@ -25,10 +25,10 @@ class PlatformTextFormField extends FormField { expands, suffix, textAlignVertical, - String initialValue, - String placeholder, - FormFieldValidator validator, - ValueChanged onSaved}) + String? initialValue, + String? placeholder, + FormFieldValidator? validator, + ValueChanged? onSaved}) : super( key: key, initialValue: controller != null ? controller.text : (initialValue ?? ''), @@ -41,7 +41,7 @@ class PlatformTextFormField extends FormField { return null; }, builder: (FormFieldState field) { - final _PlatformTextFormFieldState state = field; + final _PlatformTextFormFieldState state = field as _PlatformTextFormFieldState; void onChangedHandler(String value) { if (onChanged != null) { @@ -73,7 +73,7 @@ class PlatformTextFormField extends FormField { suffix: suffix), field.hasError ? Text( - field.errorText, + field.errorText!, style: TextStyle(color: CupertinoColors.systemRed.resolveFrom(field.context), fontSize: 13), textAlign: textAlign, ) @@ -81,19 +81,19 @@ class PlatformTextFormField extends FormField { ]); }); - final TextEditingController controller; + final TextEditingController? controller; @override _PlatformTextFormFieldState createState() => _PlatformTextFormFieldState(); } class _PlatformTextFormFieldState extends FormFieldState { - TextEditingController _controller; + TextEditingController? _controller; - TextEditingController get _effectiveController => widget.controller ?? _controller; + TextEditingController get _effectiveController => widget.controller ?? _controller!; @override - PlatformTextFormField get widget => super.widget; + PlatformTextFormField get widget => super.widget as PlatformTextFormField; @override void initState() { @@ -101,7 +101,7 @@ class _PlatformTextFormFieldState extends FormFieldState { if (widget.controller == null) { _controller = TextEditingController(text: widget.initialValue); } else { - widget.controller.addListener(_handleControllerChanged); + widget.controller!.addListener(_handleControllerChanged); } } @@ -113,9 +113,9 @@ class _PlatformTextFormFieldState extends FormFieldState { widget.controller?.addListener(_handleControllerChanged); if (oldWidget.controller != null && widget.controller == null) - _controller = TextEditingController.fromValue(oldWidget.controller.value); + _controller = TextEditingController.fromValue(oldWidget.controller!.value); if (widget.controller != null) { - setValue(widget.controller.text); + setValue(widget.controller!.text); if (oldWidget.controller == null) _controller = null; } } @@ -131,7 +131,7 @@ class _PlatformTextFormFieldState extends FormFieldState { void reset() { super.reset(); setState(() { - _effectiveController.text = widget.initialValue; + _effectiveController.text = widget.initialValue ?? ""; }); } diff --git a/lib/components/SimplePage.dart b/lib/components/SimplePage.dart index 3ce8a5a..d21bde4 100644 --- a/lib/components/SimplePage.dart +++ b/lib/components/SimplePage.dart @@ -13,9 +13,9 @@ enum SimpleScrollable { class SimplePage extends StatelessWidget { const SimplePage( - {Key key, - this.title, - @required this.child, + {Key? key, + required this.title, + required this.child, this.leadingAction, this.trailingActions = const [], this.scrollable = SimpleScrollable.vertical, @@ -30,20 +30,20 @@ class SimplePage extends StatelessWidget { final String title; final Widget child; final SimpleScrollable scrollable; - final ScrollController scrollController; + final ScrollController? scrollController; /// Set this to true to force draw a scrollbar without a scroll view, this is helpful for pages with Reorder-able listviews /// This is set to true if you have any scrollable other than none final bool scrollbar; - final Widget bottomBar; + final Widget? bottomBar; /// If no leading action is provided then a default "Back" widget than pops the page will be provided - final Widget leadingAction; + final Widget? leadingAction; final List trailingActions; - final VoidCallback onRefresh; - final VoidCallback onLoading; - final RefreshController refreshController; + final VoidCallback? onRefresh; + final VoidCallback? onLoading; + final RefreshController? refreshController; @override Widget build(BuildContext context) { @@ -72,7 +72,7 @@ class SimplePage extends StatelessWidget { scrollController: scrollController, onRefresh: onRefresh, onLoading: onLoading, - controller: refreshController, + controller: refreshController!, child: realChild, enablePullUp: onLoading != null, enablePullDown: onRefresh != null, @@ -88,7 +88,7 @@ class SimplePage extends StatelessWidget { if (bottomBar != null) { realChild = Column(children: [ Expanded(child: realChild), - bottomBar, + bottomBar!, ]); } diff --git a/lib/components/SiteItem.dart b/lib/components/SiteItem.dart index cf24fb8..10550e4 100644 --- a/lib/components/SiteItem.dart +++ b/lib/components/SiteItem.dart @@ -4,7 +4,7 @@ import 'package:mobile_nebula/models/Site.dart'; import 'package:mobile_nebula/services/utils.dart'; class SiteItem extends StatelessWidget { - const SiteItem({Key key, this.site, this.onPressed}) : super(key: key); + const SiteItem({Key? key, required this.site, this.onPressed}) : super(key: key); final Site site; final onPressed; @@ -27,8 +27,8 @@ class SiteItem extends StatelessWidget { Widget _buildContent(BuildContext context) { final border = BorderSide(color: Utils.configSectionBorder(context)); var ip = "Error"; - if (site.certInfo != null && site.certInfo.cert.details.ips.length > 0) { - ip = site.certInfo.cert.details.ips[0]; + if (site.certInfo != null && site.certInfo!.cert.details.ips.length > 0) { + ip = site.certInfo!.cert.details.ips[0]; } return SpecialButton( diff --git a/lib/components/SpecialButton.dart b/lib/components/SpecialButton.dart index 381e9f4..7915418 100644 --- a/lib/components/SpecialButton.dart +++ b/lib/components/SpecialButton.dart @@ -5,15 +5,15 @@ import 'package:flutter/material.dart'; // This is a button that pushes the bare minimum onto you, it doesn't even respect button themes - unless you tell it to class SpecialButton extends StatefulWidget { - const SpecialButton({Key key, this.child, this.color, this.onPressed, this.useButtonTheme = false, this.decoration}) + const SpecialButton({Key? key, this.child, this.color, this.onPressed, this.useButtonTheme = false, this.decoration}) : super(key: key); - final Widget child; - final Color color; + final Widget? child; + final Color? color; final bool useButtonTheme; - final BoxDecoration decoration; + final BoxDecoration? decoration; - final Function onPressed; + final GestureTapCallback? onPressed; @override _SpecialButtonState createState() => _SpecialButtonState(); @@ -59,7 +59,7 @@ class _SpecialButtonState extends State with SingleTickerProvider child: Semantics( button: true, child: FadeTransition( - opacity: _opacityAnimation, + opacity: _opacityAnimation!, child: DefaultTextStyle(style: textStyle, child: Container(child: widget.child, color: widget.color)), ), ), @@ -71,8 +71,8 @@ class _SpecialButtonState extends State with SingleTickerProvider static const Duration kFadeInDuration = Duration(milliseconds: 100); final Tween _opacityTween = Tween(begin: 1.0); - AnimationController _animationController; - Animation _opacityAnimation; + AnimationController? _animationController; + Animation? _opacityAnimation; @override void initState() { @@ -82,7 +82,7 @@ class _SpecialButtonState extends State with SingleTickerProvider value: 0.0, vsync: this, ); - _opacityAnimation = _animationController.drive(CurveTween(curve: Curves.decelerate)).drive(_opacityTween); + _opacityAnimation = _animationController!.drive(CurveTween(curve: Curves.decelerate)).drive(_opacityTween); _setTween(); } @@ -98,8 +98,7 @@ class _SpecialButtonState extends State with SingleTickerProvider @override void dispose() { - _animationController.dispose(); - _animationController = null; + _animationController?.dispose(); super.dispose(); } @@ -127,14 +126,14 @@ class _SpecialButtonState extends State with SingleTickerProvider } void _animate() { - if (_animationController.isAnimating) { + if (_animationController == null || _animationController!.isAnimating) { return; } final bool wasHeldDown = _buttonHeldDown; final TickerFuture ticker = _buttonHeldDown - ? _animationController.animateTo(1.0, duration: kFadeOutDuration) - : _animationController.animateTo(0.0, duration: kFadeInDuration); + ? _animationController!.animateTo(1.0, duration: kFadeOutDuration) + : _animationController!.animateTo(0.0, duration: kFadeInDuration); ticker.then((void value) { if (mounted && wasHeldDown != _buttonHeldDown) { diff --git a/lib/components/SpecialTextField.dart b/lib/components/SpecialTextField.dart index af86a0e..1e09a6f 100644 --- a/lib/components/SpecialTextField.dart +++ b/lib/components/SpecialTextField.dart @@ -5,7 +5,7 @@ import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; /// A normal TextField or CupertinoTextField that looks the same on all platforms class SpecialTextField extends StatefulWidget { const SpecialTextField( - {Key key, + {Key? key, this.placeholder, this.suffix, this.controller, @@ -30,43 +30,44 @@ class SpecialTextField extends StatefulWidget { this.inputFormatters}) : super(key: key); - final String placeholder; - final TextEditingController controller; - final FocusNode focusNode; - final FocusNode nextFocusNode; - final bool autocorrect; - final int minLines; - final int maxLines; - final int maxLength; - final MaxLengthEnforcement maxLengthEnforcement; - final Widget suffix; - final TextStyle style; - final TextInputType keyboardType; - final Brightness keyboardAppearance; + final String? placeholder; + final TextEditingController? controller; + final FocusNode? focusNode; + final FocusNode? nextFocusNode; + final bool? autocorrect; + final int? minLines; + final int? maxLines; + final int? maxLength; + final MaxLengthEnforcement? maxLengthEnforcement; + final Widget? suffix; + final TextStyle? style; + final TextInputType? keyboardType; + final Brightness? keyboardAppearance; - final TextInputAction textInputAction; - final TextCapitalization textCapitalization; - final TextAlign textAlign; - final TextAlignVertical textAlignVertical; + final TextInputAction? textInputAction; + final TextCapitalization? textCapitalization; + final TextAlign? textAlign; + final TextAlignVertical? textAlignVertical; - final bool autofocus; - final ValueChanged onChanged; - final bool enabled; - final List inputFormatters; - final bool expands; + final bool? autofocus; + final ValueChanged? onChanged; + final bool? enabled; + final List? inputFormatters; + final bool? expands; @override _SpecialTextFieldState createState() => _SpecialTextFieldState(); } class _SpecialTextFieldState extends State { - List formatters; + List formatters = []; @override void initState() { - formatters = widget.inputFormatters; - if (formatters == null || formatters.length == 0) { + if (widget.inputFormatters == null || formatters.length == 0) { formatters = [FilteringTextInputFormatter.allow(RegExp(r'[^\t]'))]; + } else { + formatters = widget.inputFormatters!; } super.initState(); diff --git a/lib/components/config/ConfigButtonItem.dart b/lib/components/config/ConfigButtonItem.dart index 73e9d92..955f9d5 100644 --- a/lib/components/config/ConfigButtonItem.dart +++ b/lib/components/config/ConfigButtonItem.dart @@ -5,9 +5,9 @@ import 'package:mobile_nebula/services/utils.dart'; // A config item that detects tapping and calls back on a tap class ConfigButtonItem extends StatelessWidget { - const ConfigButtonItem({Key key, this.content, this.onPressed}) : super(key: key); + const ConfigButtonItem({Key? key, this.content, this.onPressed}) : super(key: key); - final Widget content; + final Widget? content; final onPressed; @override diff --git a/lib/components/config/ConfigCheckboxItem.dart b/lib/components/config/ConfigCheckboxItem.dart index 17b84a6..f013378 100644 --- a/lib/components/config/ConfigCheckboxItem.dart +++ b/lib/components/config/ConfigCheckboxItem.dart @@ -3,14 +3,15 @@ import 'package:mobile_nebula/components/SpecialButton.dart'; import 'package:mobile_nebula/services/utils.dart'; class ConfigCheckboxItem extends StatelessWidget { - const ConfigCheckboxItem({Key key, this.label, this.content, this.labelWidth = 100, this.onChanged, this.checked}) + const ConfigCheckboxItem( + {Key? key, this.label, this.content, this.labelWidth = 100, this.onChanged, this.checked = false}) : super(key: key); - final Widget label; - final Widget content; + final Widget? label; + final Widget? content; final double labelWidth; final bool checked; - final Function onChanged; + final Function? onChanged; @override Widget build(BuildContext context) { @@ -34,7 +35,7 @@ class ConfigCheckboxItem extends StatelessWidget { child: item, onPressed: () { if (onChanged != null) { - onChanged(); + onChanged!(); } }, ); diff --git a/lib/components/config/ConfigHeader.dart b/lib/components/config/ConfigHeader.dart index 7e572de..5e5389e 100644 --- a/lib/components/config/ConfigHeader.dart +++ b/lib/components/config/ConfigHeader.dart @@ -4,15 +4,15 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; TextStyle basicTextStyle(BuildContext context) => - Platform.isIOS ? CupertinoTheme.of(context).textTheme.textStyle : Theme.of(context).textTheme.subtitle1; + Platform.isIOS ? CupertinoTheme.of(context).textTheme.textStyle : Theme.of(context).textTheme.subtitle1!; const double _headerFontSize = 13.0; class ConfigHeader extends StatelessWidget { - const ConfigHeader({Key key, this.label, this.color}) : super(key: key); + const ConfigHeader({Key? key, required this.label, this.color}) : super(key: key); final String label; - final Color color; + final Color? color; @override Widget build(BuildContext context) { diff --git a/lib/components/config/ConfigItem.dart b/lib/components/config/ConfigItem.dart index f4fef54..7afec63 100644 --- a/lib/components/config/ConfigItem.dart +++ b/lib/components/config/ConfigItem.dart @@ -4,10 +4,14 @@ import 'package:mobile_nebula/services/utils.dart'; class ConfigItem extends StatelessWidget { const ConfigItem( - {Key key, this.label, this.content, this.labelWidth = 100, this.crossAxisAlignment = CrossAxisAlignment.center}) + {Key? key, + this.label, + required this.content, + this.labelWidth = 100, + this.crossAxisAlignment = CrossAxisAlignment.center}) : super(key: key); - final Widget label; + final Widget? label; final Widget content; final double labelWidth; final CrossAxisAlignment crossAxisAlignment; diff --git a/lib/components/config/ConfigPageItem.dart b/lib/components/config/ConfigPageItem.dart index 09a7c68..800fed5 100644 --- a/lib/components/config/ConfigPageItem.dart +++ b/lib/components/config/ConfigPageItem.dart @@ -7,7 +7,7 @@ import 'package:mobile_nebula/services/utils.dart'; class ConfigPageItem extends StatelessWidget { const ConfigPageItem( - {Key key, + {Key? key, this.label, this.content, this.labelWidth = 100, @@ -15,8 +15,8 @@ class ConfigPageItem extends StatelessWidget { this.crossAxisAlignment = CrossAxisAlignment.center}) : super(key: key); - final Widget label; - final Widget content; + final Widget? label; + final Widget? content; final double labelWidth; final CrossAxisAlignment crossAxisAlignment; final onPressed; @@ -28,8 +28,8 @@ class ConfigPageItem extends StatelessWidget { if (Platform.isAndroid) { final origTheme = Theme.of(context); theme = origTheme.copyWith( - textTheme: - origTheme.textTheme.copyWith(button: origTheme.textTheme.button.copyWith(fontWeight: FontWeight.normal))); + textTheme: origTheme.textTheme + .copyWith(button: origTheme.textTheme.button!.copyWith(fontWeight: FontWeight.normal))); return Theme(data: theme, child: _buildContent(context)); } else { final origTheme = CupertinoTheme.of(context); diff --git a/lib/components/config/ConfigSection.dart b/lib/components/config/ConfigSection.dart index 6568f7f..544b960 100644 --- a/lib/components/config/ConfigSection.dart +++ b/lib/components/config/ConfigSection.dart @@ -4,12 +4,13 @@ import 'package:mobile_nebula/services/utils.dart'; import 'ConfigHeader.dart'; class ConfigSection extends StatelessWidget { - const ConfigSection({Key key, this.label, this.children, this.borderColor, this.labelColor}) : super(key: key); + const ConfigSection({Key? key, this.label, required this.children, this.borderColor, this.labelColor}) + : super(key: key); final List children; - final String label; - final Color borderColor; - final Color labelColor; + final String? label; + final Color? borderColor; + final Color? labelColor; @override Widget build(BuildContext context) { @@ -32,7 +33,7 @@ class ConfigSection extends StatelessWidget { } return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - label != null ? ConfigHeader(label: label, color: labelColor) : Container(height: 20), + label != null ? ConfigHeader(label: label!, color: labelColor) : Container(height: 20), Container( decoration: BoxDecoration(border: Border(top: border, bottom: border), color: Utils.configItemBackground(context)), diff --git a/lib/components/config/ConfigTextItem.dart b/lib/components/config/ConfigTextItem.dart index b911723..3d0eaf0 100644 --- a/lib/components/config/ConfigTextItem.dart +++ b/lib/components/config/ConfigTextItem.dart @@ -5,10 +5,12 @@ import 'package:flutter/material.dart'; import 'package:mobile_nebula/components/SpecialTextField.dart'; class ConfigTextItem extends StatelessWidget { - const ConfigTextItem({Key key, this.placeholder, this.controller, this.style = const TextStyle(fontFamily: 'RobotoMono')}) : super(key: key); + const ConfigTextItem( + {Key? key, this.placeholder, this.controller, this.style = const TextStyle(fontFamily: 'RobotoMono')}) + : super(key: key); - final String placeholder; - final TextEditingController controller; + final String? placeholder; + final TextEditingController? controller; final TextStyle style; @override diff --git a/lib/models/CIDR.dart b/lib/models/CIDR.dart index 788805e..c5746a8 100644 --- a/lib/models/CIDR.dart +++ b/lib/models/CIDR.dart @@ -1,5 +1,5 @@ class CIDR { - CIDR({this.ip, this.bits}); + CIDR({this.ip = '', this.bits = 0}); String ip; int bits; @@ -13,13 +13,15 @@ class CIDR { return toString(); } - CIDR.fromString(String val) { + factory CIDR.fromString(String val) { final parts = val.split('/'); if (parts.length != 2) { throw 'Invalid CIDR string'; } - ip = parts[0]; - bits = int.parse(parts[1]); + return CIDR( + ip: parts[0], + bits: int.parse(parts[1]), + ); } } diff --git a/lib/models/Certificate.dart b/lib/models/Certificate.dart index 64bffc2..b48d984 100644 --- a/lib/models/Certificate.dart +++ b/lib/models/Certificate.dart @@ -1,7 +1,7 @@ class CertificateInfo { Certificate cert; - String rawCert; - CertificateValidity validity; + String? rawCert; + CertificateValidity? validity; CertificateInfo.debug({this.rawCert = ""}) : this.cert = Certificate.debug(), @@ -12,10 +12,10 @@ class CertificateInfo { rawCert = json['RawCert'], validity = CertificateValidity.fromJson(json['Validity']); - CertificateInfo({this.cert, this.rawCert, this.validity}); + CertificateInfo({required this.cert, this.rawCert, this.validity}); static List fromJsonList(List list) { - return list.map((v) => CertificateInfo.fromJson(v)); + return list.map((v) => CertificateInfo.fromJson(v)).toList(); } } @@ -59,8 +59,8 @@ class CertificateDetails { CertificateDetails.fromJson(Map json) : name = json['name'], - notBefore = DateTime.tryParse(json['notBefore']), - notAfter = DateTime.tryParse(json['notAfter']), + notBefore = DateTime.parse(json['notBefore']), + notAfter = DateTime.parse(json['notAfter']), publicKey = json['publicKey'], groups = List.from(json['groups']), ips = List.from(json['ips']), diff --git a/lib/models/HostInfo.dart b/lib/models/HostInfo.dart index 82204e5..d90851a 100644 --- a/lib/models/HostInfo.dart +++ b/lib/models/HostInfo.dart @@ -6,31 +6,48 @@ class HostInfo { int remoteIndex; List remoteAddresses; int cachedPackets; - Certificate cert; - UDPAddress currentRemote; + Certificate? cert; + UDPAddress? currentRemote; int messageCounter; - HostInfo.fromJson(Map json) { - vpnIp = json['vpnIp']; - localIndex = json['localIndex']; - remoteIndex = json['remoteIndex']; - cachedPackets = json['cachedPackets']; + HostInfo({ + required this.vpnIp, + required this.localIndex, + required this.remoteIndex, + required this.remoteAddresses, + required this.cachedPackets, + required this.messageCounter, + this.cert, + this.currentRemote, + }); + factory HostInfo.fromJson(Map json) { + UDPAddress? currentRemote; if (json['currentRemote'] != null) { currentRemote = UDPAddress.fromJson(json['currentRemote']); } + Certificate? cert; if (json['cert'] != null) { cert = Certificate.fromJson(json['cert']); } List addrs = json['remoteAddrs']; - remoteAddresses = []; - addrs?.forEach((val) { + List remoteAddresses = []; + addrs.forEach((val) { remoteAddresses.add(UDPAddress.fromJson(val)); }); - messageCounter = json['messageCounter']; + return HostInfo( + vpnIp: json['vpnIp'], + localIndex: json['localIndex'], + remoteIndex: json['remoteIndex'], + remoteAddresses: remoteAddresses, + cachedPackets: json['cachedPackets'], + messageCounter: json['messageCounter'], + cert: cert, + currentRemote: currentRemote, + ); } } diff --git a/lib/models/Hostmap.dart b/lib/models/Hostmap.dart index f30cdd6..ee899c4 100644 --- a/lib/models/Hostmap.dart +++ b/lib/models/Hostmap.dart @@ -5,5 +5,5 @@ class Hostmap { List destinations; bool lighthouse; - Hostmap({this.nebulaIp, this.destinations, this.lighthouse}); + Hostmap({required this.nebulaIp, required this.destinations, required this.lighthouse}); } diff --git a/lib/models/IPAndPort.dart b/lib/models/IPAndPort.dart index 76de2d5..82aaf76 100644 --- a/lib/models/IPAndPort.dart +++ b/lib/models/IPAndPort.dart @@ -1,12 +1,12 @@ class IPAndPort { - String ip; - int port; + String? ip; + int? port; IPAndPort({this.ip, this.port}); @override String toString() { - if (ip.contains(':')) { + if (ip != null && ip!.contains(':')) { return '[$ip]:$port'; } @@ -17,10 +17,13 @@ class IPAndPort { return toString(); } - IPAndPort.fromString(String val) { + factory IPAndPort.fromString(String val) { //TODO: Uri.parse is as close as I could get to parsing both ipv4 and v6 addresses with a port without bringing a whole mess of code into here final uri = Uri.parse("ugh://$val"); - this.ip = uri.host; - this.port = uri.port; + + return IPAndPort( + ip: uri.host, + port: uri.port, + ); } } diff --git a/lib/models/Site.dart b/lib/models/Site.dart index 3af3067..e630208 100644 --- a/lib/models/Site.dart +++ b/lib/models/Site.dart @@ -12,107 +12,75 @@ var uuid = Uuid(); class Site { static const platform = MethodChannel('net.defined.mobileNebula/NebulaVpnService'); - EventChannel _updates; + late EventChannel _updates; /// Signals that something about this site has changed. onError is called with an error string if there was an error StreamController _change = StreamController.broadcast(); // Identifiers - String name; - String id; + late String name; + late String id; // static_host_map - Map staticHostmap; - List unsafeRoutes; + late Map staticHostmap; + late List unsafeRoutes; // pki fields - List ca; - CertificateInfo certInfo; - String key; + late List ca; + String? key; + late CertificateInfo? certInfo; // lighthouse options - int lhDuration; // in seconds + late int lhDuration; // in seconds // listen settings - int port; - int mtu; + late int port; + late int mtu; - String cipher; - int sortKey; - bool connected; - String status; - String logFile; - String logVerbosity; + late String cipher; + late int sortKey; + late bool connected; + late String status; + late String logFile; + late String logVerbosity; // A list of errors encountered while loading the site - List errors; + late List errors; - Site( - {this.name, - id, - staticHostmap, - ca, - this.certInfo, - this.lhDuration = 0, - this.port = 0, - this.cipher = "aes", - this.sortKey, - this.mtu = 1300, - this.connected, - this.status, - this.logFile, - this.logVerbosity = 'info', - errors, - unsafeRoutes}) - : staticHostmap = staticHostmap ?? {}, - unsafeRoutes = unsafeRoutes ?? [], - errors = errors ?? [], - ca = ca ?? [], - id = id ?? uuid.v4(); - - Site.fromJson(Map json) { - name = json['name']; - id = json['id']; - - Map rawHostmap = json['staticHostmap']; - staticHostmap = {}; - rawHostmap.forEach((key, val) { - staticHostmap[key] = StaticHost.fromJson(val); - }); - - List rawUnsafeRoutes = json['unsafeRoutes']; - unsafeRoutes = []; - if (rawUnsafeRoutes != null) { - rawUnsafeRoutes.forEach((val) { - unsafeRoutes.add(UnsafeRoute.fromJson(val)); - }); - } - - List rawCA = json['ca']; - ca = []; - rawCA.forEach((val) { - ca.add(CertificateInfo.fromJson(val)); - }); - - if (json['cert'] != null) { - certInfo = CertificateInfo.fromJson(json['cert']); - } - - lhDuration = json['lhDuration']; - port = json['port']; - mtu = json['mtu']; - cipher = json['cipher']; - sortKey = json['sortKey']; - logFile = json['logFile']; - logVerbosity = json['logVerbosity']; - connected = json['connected'] ?? false; - status = json['status'] ?? ""; - - errors = []; - List rawErrors = json["errors"]; - rawErrors.forEach((error) { - errors.add(error); - }); + Site({ + String name = '', + String? id, + Map? staticHostmap, + List? ca, + CertificateInfo? certInfo, + int lhDuration = 0, + int port = 0, + String cipher = "aes", + int sortKey = 0, + int mtu = 1300, + bool connected = false, + String status = '', + String logFile = '', + String logVerbosity = 'info', + List? errors, + List? unsafeRoutes, + }) { + this.name = name; + this.id = id ?? uuid.v4(); + this.staticHostmap = staticHostmap ?? {}; + this.ca = ca ?? []; + this.certInfo = certInfo; + this.lhDuration = lhDuration; + this.port = port; + this.cipher = cipher; + this.sortKey = sortKey; + this.mtu = mtu; + this.connected = connected; + this.status = status; + this.logFile = logFile; + this.logVerbosity = logVerbosity; + this.errors = errors ?? []; + this.unsafeRoutes = unsafeRoutes ?? []; _updates = EventChannel('net.defined.nebula/$id'); _updates.receiveBroadcastStream().listen((d) { @@ -128,10 +96,60 @@ class Site { var error = err as PlatformException; this.status = error.details['status']; this.connected = error.details['connected']; - _change.addError(error.message); + _change.addError(error.message ?? 'An unexpected error occurred'); }); } + factory Site.fromJson(Map json) { + Map rawHostmap = json['staticHostmap']; + Map staticHostmap = {}; + rawHostmap.forEach((key, val) { + staticHostmap[key] = StaticHost.fromJson(val); + }); + + List rawUnsafeRoutes = json['unsafeRoutes']; + List unsafeRoutes = []; + rawUnsafeRoutes.forEach((val) { + unsafeRoutes.add(UnsafeRoute.fromJson(val)); + }); + + List rawCA = json['ca']; + List ca = []; + rawCA.forEach((val) { + ca.add(CertificateInfo.fromJson(val)); + }); + + CertificateInfo? certInfo; + if (json['cert'] != null) { + certInfo = CertificateInfo.fromJson(json['cert']); + } + + List rawErrors = json["errors"]; + List errors = []; + rawErrors.forEach((error) { + errors.add(error); + }); + + return Site( + name: json['name'], + id: json['id'], + staticHostmap: staticHostmap, + ca: ca, + certInfo: certInfo, + lhDuration: json['lhDuration'], + port: json['port'], + cipher: json['cipher'], + sortKey: json['sortKey'], + mtu: json['mtu'], + connected: json['connected'] ?? false, + status: json['status'] ?? "", + logFile: json['logFile'], + logVerbosity: json['logVerbosity'], + errors: errors, + unsafeRoutes: unsafeRoutes, + ); + } + Stream onChange() { return _change.stream; } @@ -142,10 +160,9 @@ class Site { 'id': id, 'staticHostmap': staticHostmap, 'unsafeRoutes': unsafeRoutes, - 'ca': ca?.map((cert) { - return cert.rawCert; - })?.join('\n') ?? - "", + 'ca': ca.map((cert) { + return cert.rawCert; + }).join('\n'), 'cert': certInfo?.rawCert, 'key': key, 'lhDuration': lhDuration, @@ -260,7 +277,7 @@ class Site { _change.close(); } - Future getHostInfo(String vpnIp, bool pending) async { + Future getHostInfo(String vpnIp, bool pending) async { try { var ret = await platform .invokeMethod("active.getHostInfo", {"id": id, "vpnIp": vpnIp, "pending": pending}); @@ -277,7 +294,7 @@ class Site { } } - Future setRemoteForTunnel(String vpnIp, String addr) async { + Future setRemoteForTunnel(String vpnIp, String addr) async { try { var ret = await platform .invokeMethod("active.setRemoteForTunnel", {"id": id, "vpnIp": vpnIp, "addr": addr}); diff --git a/lib/models/StaticHosts.dart b/lib/models/StaticHosts.dart index 1e30d89..1402f5c 100644 --- a/lib/models/StaticHosts.dart +++ b/lib/models/StaticHosts.dart @@ -4,11 +4,9 @@ class StaticHost { bool lighthouse; List destinations; - StaticHost({this.lighthouse, this.destinations}); - - StaticHost.fromJson(Map json) { - lighthouse = json['lighthouse']; + StaticHost({required this.lighthouse, required this.destinations}); + factory StaticHost.fromJson(Map json) { var list = json['destinations'] as List; var result = []; @@ -16,7 +14,10 @@ class StaticHost { result.add(IPAndPort.fromString(item)); }); - destinations = result; + return StaticHost( + lighthouse: json['lighthouse'], + destinations: result, + ); } Map toJson() { diff --git a/lib/models/UnsafeRoute.dart b/lib/models/UnsafeRoute.dart index 684fed8..0486290 100644 --- a/lib/models/UnsafeRoute.dart +++ b/lib/models/UnsafeRoute.dart @@ -1,12 +1,14 @@ class UnsafeRoute { - String route; - String via; + String? route; + String? via; UnsafeRoute({this.route, this.via}); - UnsafeRoute.fromJson(Map json) { - route = json['route']; - via = json['via']; + factory UnsafeRoute.fromJson(Map json) { + return UnsafeRoute( + route: json['route'], + via: json['via'], + ); } Map toJson() { diff --git a/lib/screens/AboutScreen.dart b/lib/screens/AboutScreen.dart index 45de7d0..31879cc 100644 --- a/lib/screens/AboutScreen.dart +++ b/lib/screens/AboutScreen.dart @@ -9,7 +9,7 @@ import 'package:mobile_nebula/services/utils.dart'; import 'package:package_info/package_info.dart'; class AboutScreen extends StatefulWidget { - const AboutScreen({Key key}) : super(key: key); + const AboutScreen({Key? key}) : super(key: key); @override _AboutScreenState createState() => _AboutScreenState(); @@ -17,7 +17,7 @@ class AboutScreen extends StatefulWidget { class _AboutScreenState extends State { bool ready = false; - PackageInfo packageInfo; + PackageInfo? packageInfo; @override void initState() { @@ -33,6 +33,7 @@ class _AboutScreenState extends State { @override Widget build(BuildContext context) { + // packageInfo is null until ready is true if (!ready) { return Center( child: PlatformCircularProgressIndicator(cupertino: (_, __) { @@ -48,13 +49,17 @@ class _AboutScreenState extends State { ConfigItem( label: Text('App version'), labelWidth: 150, - content: _buildText('${packageInfo.version}-${packageInfo.buildNumber} (sha: $gitSha)')), + content: _buildText('${packageInfo!.version}-${packageInfo!.buildNumber} (sha: $gitSha)')), ConfigItem( label: Text('Nebula version'), labelWidth: 150, content: _buildText('$nebulaVersion ($goVersion)')), ConfigItem( - label: Text('Flutter version'), labelWidth: 150, content: _buildText(flutterVersion['frameworkVersion'])), + label: Text('Flutter version'), + labelWidth: 150, + content: _buildText(flutterVersion['frameworkVersion'] ?? 'Unknown')), ConfigItem( - label: Text('Dart version'), labelWidth: 150, content: _buildText(flutterVersion['dartSdkVersion'])), + label: Text('Dart version'), + labelWidth: 150, + content: _buildText(flutterVersion['dartSdkVersion'] ?? 'Unknown')), ]), ConfigSection(children: [ //TODO: wire up these other pages diff --git a/lib/screens/HostInfoScreen.dart b/lib/screens/HostInfoScreen.dart index cc5a9c9..e8cd601 100644 --- a/lib/screens/HostInfoScreen.dart +++ b/lib/screens/HostInfoScreen.dart @@ -14,13 +14,19 @@ import 'package:mobile_nebula/services/utils.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; class HostInfoScreen extends StatefulWidget { - const HostInfoScreen({Key key, this.hostInfo, this.isLighthouse, this.pending, this.onChanged, this.site}) - : super(key: key); + const HostInfoScreen({ + Key? key, + required this.hostInfo, + required this.isLighthouse, + required this.pending, + this.onChanged, + required this.site, + }) : super(key: key); final bool isLighthouse; final bool pending; final HostInfo hostInfo; - final Function onChanged; + final Function? onChanged; final Site site; @override @@ -30,7 +36,7 @@ class HostInfoScreen extends StatefulWidget { //TODO: have a config option to refresh hostmaps on a cadence (applies to 3 screens so far) class _HostInfoScreenState extends State { - HostInfo hostInfo; + late HostInfo hostInfo; RefreshController refreshController = RefreshController(initialRefresh: false); @override @@ -64,9 +70,9 @@ class _HostInfoScreenState extends State { ? ConfigPageItem( label: Text('Certificate'), labelWidth: 150, - content: Text(hostInfo.cert.details.name), + content: Text(hostInfo.cert!.details.name), onPressed: () => Utils.openPage( - context, (context) => CertificateDetailsScreen(certInfo: CertificateInfo(cert: hostInfo.cert)))) + context, (context) => CertificateDetailsScreen(certInfo: CertificateInfo(cert: hostInfo.cert!)))) : Container(), ]); } @@ -116,7 +122,7 @@ class _HostInfoScreenState extends State { _setHostInfo(h); } } catch (err) { - Utils.popError(context, 'Error while changing the remote', err); + Utils.popError(context, 'Error while changing the remote', err.toString()); } }, )); @@ -156,11 +162,11 @@ class _HostInfoScreenState extends State { try { await widget.site.closeTunnel(hostInfo.vpnIp); if (widget.onChanged != null) { - widget.onChanged(); + widget.onChanged!(); } Navigator.pop(context); } catch (err) { - Utils.popError(context, 'Error while trying to close the tunnel', err); + Utils.popError(context, 'Error while trying to close the tunnel', err.toString()); } }, deleteLabel: 'Close')))); } @@ -174,7 +180,7 @@ class _HostInfoScreenState extends State { _setHostInfo(h); } catch (err) { - Utils.popError(context, 'Failed to refresh host info', err); + Utils.popError(context, 'Failed to refresh host info', err.toString()); } } diff --git a/lib/screens/MainScreen.dart b/lib/screens/MainScreen.dart index a93ad01..3e77819 100644 --- a/lib/screens/MainScreen.dart +++ b/lib/screens/MainScreen.dart @@ -23,7 +23,7 @@ import 'package:uuid/uuid.dart'; //TODO: add refresh class MainScreen extends StatefulWidget { - const MainScreen({Key key}) : super(key: key); + const MainScreen({Key? key}) : super(key: key); @override _MainScreenState createState() => _MainScreenState(); @@ -31,9 +31,9 @@ class MainScreen extends StatefulWidget { class _MainScreenState extends State { bool ready = false; - List sites; + List? sites; // A set of widgets to display in a column that represents an error blocking us from moving forward entirely - List error; + List? error; static const platform = MethodChannel('net.defined.mobileNebula/NebulaVpnService'); @@ -72,11 +72,14 @@ class _MainScreenState extends State { Widget _buildBody() { if (error != null) { - return Center(child: Padding(child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: error, - ), padding: EdgeInsets.symmetric(vertical: 0, horizontal: 10))); + return Center( + child: Padding( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: error!, + ), + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 10))); } if (!ready) { @@ -87,10 +90,6 @@ class _MainScreenState extends State { ); } - if (sites == null || sites.length == 0) { - return _buildNoSites(); - } - return _buildSites(); } @@ -112,8 +111,12 @@ class _MainScreenState extends State { } Widget _buildSites() { + if (sites == null || sites!.length == 0) { + return _buildNoSites(); + } + List items = []; - sites.forEach((site) { + sites!.forEach((site) { items.add(SiteItem( key: Key(site.id), site: site, @@ -134,17 +137,17 @@ class _MainScreenState extends State { } setState(() { - final Site moved = sites.removeAt(oldI); - sites.insert(newI, moved); + final Site moved = sites!.removeAt(oldI); + sites!.insert(newI, moved); }); for (var i = min(oldI, newI); i <= max(oldI, newI); i++) { - sites[i].sortKey = i; + sites![i].sortKey = i; try { - await sites[i].save(); + await sites![i].save(); } catch (err) { //TODO: display error at the end - print('ERR ${sites[i].name} - $err'); + print('ERR ${sites![i].name} - $err'); } } @@ -209,31 +212,27 @@ rmXnR1yvDZi1VPVmnNVY8NMsQpEpbbYlq7rul+ByQvg= if (Platform.isAndroid) { try { await platform.invokeMethod("android.requestPermissions"); - } on PlatformException catch (err) { if (err.code == "PERMISSIONS") { setState(() { error = [ - Text("Permissions Required", - style: TextStyle(fontWeight: FontWeight.bold)), + Text("Permissions Required", style: TextStyle(fontWeight: FontWeight.bold)), Text( - "VPN permissions are required for nebula to run, click the button below request and accept the appropriate permissions.", - textAlign: TextAlign.center - ), + "VPN permissions are required for nebula to run, click the button below request and accept the appropriate permissions.", + textAlign: TextAlign.center), ElevatedButton( - onPressed: () { - error = null; - _loadSites(); - }, - child: Text("Request Permissions") - ), + onPressed: () { + error = null; + _loadSites(); + }, + child: Text("Request Permissions")), ]; }); } else { setState(() { error = [ Text("Unknown Error", style: TextStyle(fontWeight: FontWeight.bold)), - Text(err.message, textAlign: TextAlign.center) + Text(err.message ?? 'An unknown error occurred', textAlign: TextAlign.center) ]; }); } @@ -241,7 +240,7 @@ rmXnR1yvDZi1VPVmnNVY8NMsQpEpbbYlq7rul+ByQvg= setState(() { error = [ Text("Unknown Error", style: TextStyle(fontWeight: FontWeight.bold)), - Text(err.message, textAlign: TextAlign.center) + Text(err.toString(), textAlign: TextAlign.center) ]; }); } @@ -264,12 +263,12 @@ rmXnR1yvDZi1VPVmnNVY8NMsQpEpbbYlq7rul+ByQvg= setState(() {}); }, onError: (err) { setState(() {}); - if (ModalRoute.of(context).isCurrent) { + if (ModalRoute.of(context)!.isCurrent) { Utils.popError(context, "${site.name} Error", err); } }); - sites.add(site); + sites!.add(site); } catch (err) { //TODO: handle error print("$err site config: $rawSite"); @@ -288,7 +287,7 @@ rmXnR1yvDZi1VPVmnNVY8NMsQpEpbbYlq7rul+ByQvg= "1 or more sites have errors and need your attention, problem sites have a red border."); } - sites.sort((a, b) { + sites!.sort((a, b) { return a.sortKey - b.sortKey; }); diff --git a/lib/screens/SiteDetailScreen.dart b/lib/screens/SiteDetailScreen.dart index e2f8d93..9ed698d 100644 --- a/lib/screens/SiteDetailScreen.dart +++ b/lib/screens/SiteDetailScreen.dart @@ -20,24 +20,24 @@ import 'package:pull_to_refresh/pull_to_refresh.dart'; //TODO: ios is now the problem with connecting screwing our ability to query the hostmap (its a race) class SiteDetailScreen extends StatefulWidget { - const SiteDetailScreen({Key key, this.site, this.onChanged}) : super(key: key); + const SiteDetailScreen({Key? key, required this.site, this.onChanged}) : super(key: key); final Site site; - final Function onChanged; + final Function? onChanged; @override _SiteDetailScreenState createState() => _SiteDetailScreenState(); } class _SiteDetailScreenState extends State { - Site site; - StreamSubscription onChange; + late Site site; + late StreamSubscription onChange; static const platform = MethodChannel('net.defined.mobileNebula/NebulaVpnService'); bool changed = false; - List activeHosts; - List pendingHosts; + List? activeHosts; + List? pendingHosts; RefreshController refreshController = RefreshController(initialRefresh: false); - bool lastState; + late bool lastState; @override void initState() { @@ -80,7 +80,7 @@ class _SiteDetailScreenState extends State { title: site.name, leadingAction: Utils.leadingBackWidget(context, onPressed: () { if (changed && widget.onChanged != null) { - widget.onChanged(); + widget.onChanged!(); } Navigator.pop(context); }), @@ -162,13 +162,13 @@ class _SiteDetailScreenState extends State { if (activeHosts == null) { active = SizedBox(height: 20, width: 20, child: PlatformCircularProgressIndicator()); } else { - active = Text(Utils.itemCountFormat(activeHosts.length, singleSuffix: "tunnel", multiSuffix: "tunnels")); + active = Text(Utils.itemCountFormat(activeHosts!.length, singleSuffix: "tunnel", multiSuffix: "tunnels")); } if (pendingHosts == null) { pending = SizedBox(height: 20, width: 20, child: PlatformCircularProgressIndicator()); } else { - pending = Text(Utils.itemCountFormat(pendingHosts.length, singleSuffix: "tunnel", multiSuffix: "tunnels")); + pending = Text(Utils.itemCountFormat(pendingHosts!.length, singleSuffix: "tunnel", multiSuffix: "tunnels")); } return ConfigSection( @@ -176,11 +176,13 @@ class _SiteDetailScreenState extends State { children: [ ConfigPageItem( onPressed: () { + if (activeHosts == null) return; + Utils.openPage( context, (context) => SiteTunnelsScreen( pending: false, - tunnels: activeHosts, + tunnels: activeHosts!, site: site, onChanged: (hosts) { setState(() { @@ -192,11 +194,13 @@ class _SiteDetailScreenState extends State { content: Container(alignment: Alignment.centerRight, child: active)), ConfigPageItem( onPressed: () { + if (pendingHosts == null) return; + Utils.openPage( context, (context) => SiteTunnelsScreen( pending: true, - tunnels: pendingHosts, + tunnels: pendingHosts!, site: site, onChanged: (hosts) { setState(() { @@ -250,7 +254,7 @@ class _SiteDetailScreenState extends State { pendingHosts = maps["pending"]; setState(() {}); } catch (err) { - Utils.popError(context, 'Error while fetching hostmaps', err); + Utils.popError(context, 'Error while fetching hostmaps', err.toString()); } } @@ -267,7 +271,7 @@ class _SiteDetailScreenState extends State { } if (widget.onChanged != null) { - widget.onChanged(); + widget.onChanged!(); } return true; } diff --git a/lib/screens/SiteLogsScreen.dart b/lib/screens/SiteLogsScreen.dart index 648de1a..e7b8944 100644 --- a/lib/screens/SiteLogsScreen.dart +++ b/lib/screens/SiteLogsScreen.dart @@ -11,7 +11,7 @@ import 'package:mobile_nebula/services/utils.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; class SiteLogsScreen extends StatefulWidget { - const SiteLogsScreen({Key key, this.site}) : super(key: key); + const SiteLogsScreen({Key? key, required this.site}) : super(key: key); final Site site; diff --git a/lib/screens/SiteTunnelsScreen.dart b/lib/screens/SiteTunnelsScreen.dart index 02cf1f7..c587e9c 100644 --- a/lib/screens/SiteTunnelsScreen.dart +++ b/lib/screens/SiteTunnelsScreen.dart @@ -10,26 +10,28 @@ import 'package:mobile_nebula/services/utils.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; class SiteTunnelsScreen extends StatefulWidget { - const SiteTunnelsScreen({Key key, this.site, this.tunnels, this.pending, this.onChanged}) : super(key: key); + const SiteTunnelsScreen( + {Key? key, required this.site, required this.tunnels, required this.pending, required this.onChanged}) + : super(key: key); final Site site; final List tunnels; final bool pending; - final Function(List) onChanged; + final Function(List)? onChanged; @override _SiteTunnelsScreenState createState() => _SiteTunnelsScreenState(); } class _SiteTunnelsScreenState extends State { - Site site; - List tunnels; + late Site site; + late List tunnels; RefreshController refreshController = RefreshController(initialRefresh: false); @override void initState() { site = widget.site; - tunnels = widget.tunnels ?? []; + tunnels = widget.tunnels; _sortTunnels(); super.initState(); } @@ -67,7 +69,7 @@ class _SiteTunnelsScreenState extends State { })), label: Row(children: [Padding(child: icon, padding: EdgeInsets.only(right: 10)), Text(hostInfo.vpnIp)]), labelWidth: ipWidth, - content: Container(alignment: Alignment.centerRight, child: Text(hostInfo.cert?.details?.name ?? "")), + content: Container(alignment: Alignment.centerRight, child: Text(hostInfo.cert?.details.name ?? "")), )); }); @@ -121,11 +123,11 @@ class _SiteTunnelsScreenState extends State { _sortTunnels(); if (widget.onChanged != null) { - widget.onChanged(tunnels); + widget.onChanged!(tunnels); } setState(() {}); } catch (err) { - Utils.popError(context, 'Error while fetching hostmap', err); + Utils.popError(context, 'Error while fetching hostmap', err.toString()); } } } diff --git a/lib/screens/siteConfig/AddCertificateScreen.dart b/lib/screens/siteConfig/AddCertificateScreen.dart index 0e028ea..38270a0 100644 --- a/lib/screens/siteConfig/AddCertificateScreen.dart +++ b/lib/screens/siteConfig/AddCertificateScreen.dart @@ -1,9 +1,9 @@ import 'dart:convert'; -import 'package:barcode_scan/barcode_scan.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; import 'package:mobile_nebula/components/SimplePage.dart'; import 'package:mobile_nebula/components/config/ConfigButtonItem.dart'; @@ -20,16 +20,24 @@ class CertificateResult { CertificateInfo certInfo; String key; - CertificateResult({this.certInfo, this.key}); + CertificateResult({required this.certInfo, required this.key}); } class AddCertificateScreen extends StatefulWidget { - const AddCertificateScreen({Key key, this.onSave, this.onReplace, this.pubKey, this.privKey}) : super(key: key); + const AddCertificateScreen({ + Key? key, + this.onSave, + this.onReplace, + required this.pubKey, + required this.privKey, + }) : super(key: key); - // onSave will pop a new CertificateDetailsScreen - final ValueChanged onSave; - // onReplace will return the CertificateResult, assuming the previous screen is a CertificateDetailsScreen - final ValueChanged onReplace; + // onSave will pop a new CertificateDetailsScreen. + // If onSave is null, onReplace must be set. + final ValueChanged? onSave; + // onReplace will return the CertificateResult, assuming the previous screen is a CertificateDetailsScreen. + // If onReplace is null, onSave must be set. + final ValueChanged? onReplace; final String pubKey; final String privKey; @@ -39,7 +47,7 @@ class AddCertificateScreen extends StatefulWidget { } class _AddCertificateScreenState extends State { - String pubKey; + late String pubKey; bool showKey = false; String inputType = 'paste'; @@ -98,9 +106,11 @@ class _AddCertificateScreenState extends State { child: CupertinoSlidingSegmentedControl( groupValue: inputType, onValueChanged: (v) { - setState(() { - inputType = v; - }); + if (v != null) { + setState(() { + inputType = v; + }); + } }, children: { 'paste': Text('Copy/Paste'), @@ -131,19 +141,16 @@ class _AddCertificateScreenState extends State { child: Text('Show/Import Private Key'), color: CupertinoColors.secondaryLabel.resolveFrom(context), onPressed: () => Utils.confirmDelete(context, 'Show/Import Private Key?', () { - setState(() { - showKey = true; - }); - }, deleteLabel: 'Yes')))); + setState(() { + showKey = true; + }); + }, deleteLabel: 'Yes')))); } return ConfigSection( label: 'Import a private key generated on another device', children: [ - ConfigTextItem( - controller: keyController, - style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14) - ), + ConfigTextItem(controller: keyController, style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14)), ], ); } @@ -196,13 +203,13 @@ class _AddCertificateScreenState extends State { ConfigButtonItem( content: Text('Scan a QR code'), onPressed: () async { - var options = ScanOptions( - restrictFormat: [BarcodeFormat.qr], - ); - - var result = await BarcodeScanner.scan(options: options); - if (result.rawContent != "") { - _addCertEntry(result.rawContent); + try { + var result = await FlutterBarcodeScanner.scanBarcode('#ff6666', 'Cancel', true, ScanMode.QR); + if (result != "") { + _addCertEntry(result); + } + } catch (err) { + return Utils.popError(context, 'Error scanning QR code', err.toString()); } }), ], @@ -225,36 +232,34 @@ class _AddCertificateScreenState extends State { if (tryCertInfo.cert.details.isCa) { return Utils.popError(context, 'Error loading certificate content', 'A certificate authority is not appropriate for a client certificate.'); - } else if (!tryCertInfo.validity.valid) { - return Utils.popError(context, 'Certificate was invalid', tryCertInfo.validity.reason); + } else if (!tryCertInfo.validity!.valid) { + return Utils.popError(context, 'Certificate was invalid', tryCertInfo.validity!.reason); } - var certMatch = await platform.invokeMethod( - "nebula.verifyCertAndKey", - {"cert": rawCert, "key": keyController.text} - ); + var certMatch = await platform + .invokeMethod("nebula.verifyCertAndKey", {"cert": rawCert, "key": keyController.text}); if (!certMatch) { // The method above will throw if there is a mismatch, this is just here in case we introduce a bug in the future return Utils.popError(context, 'Error loading certificate content', 'The provided certificates public key is not compatible with the private key.'); } - // If we are replacing we just return the results now if (widget.onReplace != null) { + // If we are replacing we just return the results now Navigator.pop(context); - widget.onReplace(CertificateResult(certInfo: tryCertInfo, key: keyController.text)); + widget.onReplace!(CertificateResult(certInfo: tryCertInfo, key: keyController.text)); return; + } else if (widget.onSave != null) { + // We have a cert, pop the details screen where they can hit save + Utils.openPage(context, (context) { + return CertificateDetailsScreen( + certInfo: tryCertInfo, + onSave: () { + Navigator.pop(context); + widget.onSave!(CertificateResult(certInfo: tryCertInfo, key: keyController.text)); + }); + }); } - - // We have a cert, pop the details screen where they can hit save - Utils.openPage(context, (context) { - return CertificateDetailsScreen( - certInfo: tryCertInfo, - onSave: () { - Navigator.pop(context); - widget.onSave(CertificateResult(certInfo: tryCertInfo, key: keyController.text)); - }); - }); } } on PlatformException catch (err) { return Utils.popError(context, 'Error loading certificate content', err.details ?? err.message); diff --git a/lib/screens/siteConfig/AdvancedScreen.dart b/lib/screens/siteConfig/AdvancedScreen.dart index 7bb07ab..7b301d8 100644 --- a/lib/screens/siteConfig/AdvancedScreen.dart +++ b/lib/screens/siteConfig/AdvancedScreen.dart @@ -28,10 +28,23 @@ class Advanced { String verbosity; List unsafeRoutes; int mtu; + + Advanced({ + required this.lhDuration, + required this.port, + required this.cipher, + required this.verbosity, + required this.unsafeRoutes, + required this.mtu, + }); } class AdvancedScreen extends StatefulWidget { - const AdvancedScreen({Key key, this.site, @required this.onSave}) : super(key: key); + const AdvancedScreen({ + Key? key, + required this.site, + required this.onSave, + }) : super(key: key); final Site site; final ValueChanged onSave; @@ -41,17 +54,19 @@ class AdvancedScreen extends StatefulWidget { } class _AdvancedScreenState extends State { - var settings = Advanced(); + late Advanced settings; var changed = false; @override void initState() { - settings.lhDuration = widget.site.lhDuration; - settings.port = widget.site.port; - settings.cipher = widget.site.cipher; - settings.verbosity = widget.site.logVerbosity; - settings.unsafeRoutes = widget.site.unsafeRoutes; - settings.mtu = widget.site.mtu; + settings = Advanced( + lhDuration: widget.site.lhDuration, + port: widget.site.port, + cipher: widget.site.cipher, + verbosity: widget.site.logVerbosity, + unsafeRoutes: widget.site.unsafeRoutes, + mtu: widget.site.mtu, + ); super.initState(); } @@ -80,7 +95,9 @@ class _AdvancedScreenState extends State { inputFormatters: [FilteringTextInputFormatter.digitsOnly], onSaved: (val) { setState(() { - settings.lhDuration = int.parse(val); + if (val != null) { + settings.lhDuration = int.parse(val!); + } }); }, )), @@ -96,7 +113,9 @@ class _AdvancedScreenState extends State { inputFormatters: [FilteringTextInputFormatter.digitsOnly], onSaved: (val) { setState(() { - settings.port = int.parse(val); + if (val != null) { + settings.port = int.parse(val!); + } }); }, )), @@ -111,7 +130,9 @@ class _AdvancedScreenState extends State { inputFormatters: [FilteringTextInputFormatter.digitsOnly], onSaved: (val) { setState(() { - settings.mtu = int.parse(val); + if (val != null) { + settings.mtu = int.parse(val!); + } }); }, )), @@ -177,7 +198,7 @@ class _AdvancedScreenState extends State { return RenderedConfigScreen(config: config, name: widget.site.name); }); } catch (err) { - Utils.popError(context, 'Failed to render the site config', err); + Utils.popError(context, 'Failed to render the site config', err.toString()); } }, ) diff --git a/lib/screens/siteConfig/CAListScreen.dart b/lib/screens/siteConfig/CAListScreen.dart index 75b32d4..41053aa 100644 --- a/lib/screens/siteConfig/CAListScreen.dart +++ b/lib/screens/siteConfig/CAListScreen.dart @@ -1,8 +1,8 @@ import 'dart:convert'; -import 'package:barcode_scan/barcode_scan.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart'; import 'package:mobile_nebula/components/FormPage.dart'; import 'package:mobile_nebula/components/config/ConfigButtonItem.dart'; import 'package:mobile_nebula/components/config/ConfigPageItem.dart'; @@ -17,10 +17,14 @@ import 'package:mobile_nebula/services/utils.dart'; //TODO: In addition you will want to think about re-generation while the site is still active (This means storing multiple keys in secure storage) class CAListScreen extends StatefulWidget { - const CAListScreen({Key key, this.cas, @required this.onSave}) : super(key: key); + const CAListScreen({ + Key? key, + required this.cas, + this.onSave, + }) : super(key: key); final List cas; - final ValueChanged> onSave; + final ValueChanged>? onSave; @override _CAListScreenState createState() => _CAListScreenState(); @@ -59,7 +63,7 @@ class _CAListScreenState extends State { onSave: () { if (widget.onSave != null) { Navigator.pop(context); - widget.onSave(cas.values.map((ca) { + widget.onSave!(cas.values.map((ca) { return ca; }).toList()); } @@ -90,8 +94,8 @@ class _CAListScreenState extends State { return items; } - _addCAEntry(String ca, ValueChanged callback) async { - String error; + _addCAEntry(String ca, ValueChanged callback) async { + String? error; //TODO: show an error popup try { @@ -118,9 +122,7 @@ class _CAListScreenState extends State { error = err.details ?? err.message; } - if (callback != null) { - callback(error); - } + callback(error); } List _addCA() { @@ -130,9 +132,11 @@ class _CAListScreenState extends State { child: CupertinoSlidingSegmentedControl( groupValue: inputType, onValueChanged: (v) { - setState(() { - inputType = v; - }); + if (v != null) { + setState(() { + inputType = v; + }); + } }, children: { 'paste': Text('Copy/Paste'), @@ -215,19 +219,19 @@ class _CAListScreenState extends State { ConfigButtonItem( content: Text('Scan a QR code'), onPressed: () async { - var options = ScanOptions( - restrictFormat: [BarcodeFormat.qr], - ); - - var result = await BarcodeScanner.scan(options: options); - if (result.rawContent != "") { - _addCAEntry(result.rawContent, (err) { - if (err != null) { - Utils.popError(context, 'Error loading CA content', err); - } else { - setState(() {}); - } - }); + try { + var result = await FlutterBarcodeScanner.scanBarcode('#ff6666', 'Cancel', true, ScanMode.QR); + if (result != "") { + _addCAEntry(result, (err) { + if (err != null) { + Utils.popError(context, 'Error loading CA content', err); + } else { + setState(() {}); + } + }); + } + } catch (err) { + return Utils.popError(context, 'Error scanning QR code', err.toString()); } }) ], diff --git a/lib/screens/siteConfig/CertificateDetailsScreen.dart b/lib/screens/siteConfig/CertificateDetailsScreen.dart index 979ca86..1d3402c 100644 --- a/lib/screens/siteConfig/CertificateDetailsScreen.dart +++ b/lib/screens/siteConfig/CertificateDetailsScreen.dart @@ -10,22 +10,30 @@ import 'package:mobile_nebula/services/utils.dart'; /// Displays the details of a CertificateInfo object. Respects incomplete objects (missing validity or rawCert) class CertificateDetailsScreen extends StatefulWidget { - const CertificateDetailsScreen({Key key, this.certInfo, this.onDelete, this.onSave, this.onReplace, this.pubKey, this.privKey}) - : super(key: key); + const CertificateDetailsScreen({ + Key? key, + required this.certInfo, + this.onDelete, + this.onSave, + this.onReplace, + this.pubKey, + this.privKey, + }) : super(key: key); final CertificateInfo certInfo; // onDelete is used to remove a CA cert - final Function onDelete; + final Function? onDelete; // onSave is used to install a new certificate - final Function onSave; + final Function? onSave; // onReplace is used to install a new certificate over top of the old one - final ValueChanged onReplace; + final ValueChanged? onReplace; - final String pubKey; - final String privKey; + // pubKey and privKey should be set if onReplace is not null. + final String? pubKey; + final String? privKey; @override _CertificateDetailsScreenState createState() => _CertificateDetailsScreenState(); @@ -33,8 +41,8 @@ class CertificateDetailsScreen extends StatefulWidget { class _CertificateDetailsScreenState extends State { bool changed = false; - CertificateResult certResult; - CertificateInfo certInfo; + CertificateResult? certResult; + late CertificateInfo certInfo; ScrollController controller = ScrollController(); @override @@ -58,10 +66,10 @@ class _CertificateDetailsScreenState extends State { onSave: () { if (widget.onSave != null) { Navigator.pop(context); - widget.onSave(); + widget.onSave!(); } else if (widget.onReplace != null) { Navigator.pop(context); - widget.onReplace(certResult); + widget.onReplace!(certResult!); } }, hideSave: widget.onSave == null && widget.onReplace == null, @@ -86,8 +94,8 @@ class _CertificateDetailsScreenState extends State { Widget _buildValid() { var valid = Text('yes'); - if (certInfo.validity != null && !certInfo.validity.valid) { - valid = Text(certInfo.validity.valid ? 'yes' : certInfo.validity.reason, + if (certInfo.validity != null && !certInfo.validity!.valid) { + valid = Text(certInfo.validity!.valid ? 'yes' : certInfo.validity!.reason, style: TextStyle(color: CupertinoColors.systemRed.resolveFrom(context))); } return ConfigSection( @@ -137,7 +145,7 @@ class _CertificateDetailsScreenState extends State { certInfo.rawCert != null ? ConfigItem( label: Text('PEM Format'), - content: SelectableText(certInfo.rawCert, style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14)), + content: SelectableText(certInfo.rawCert!, style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14)), crossAxisAlignment: CrossAxisAlignment.start) : Container(), ], @@ -145,7 +153,7 @@ class _CertificateDetailsScreenState extends State { } Widget _buildReplace() { - if (widget.onReplace == null) { + if (widget.onReplace == null || widget.pubKey == null || widget.privKey == null) { return Container(); } @@ -158,16 +166,19 @@ class _CertificateDetailsScreenState extends State { color: CupertinoColors.systemRed.resolveFrom(context), onPressed: () { Utils.openPage(context, (context) { - return AddCertificateScreen(onReplace: (result) { - setState(() { - changed = true; - certResult = result; - certInfo = certResult.certInfo; - }); - // Slam the page back to the top - controller.animateTo(0, - duration: const Duration(milliseconds: 10), curve: Curves.linearToEaseOut); - }, pubKey: widget.pubKey, privKey: widget.privKey, ); + return AddCertificateScreen( + onReplace: (result) { + setState(() { + changed = true; + certResult = result; + certInfo = result.certInfo; + }); + // Slam the page back to the top + controller.animateTo(0, + duration: const Duration(milliseconds: 10), curve: Curves.linearToEaseOut); + }, + pubKey: widget.pubKey!, + privKey: widget.privKey!); }); }))); } @@ -188,7 +199,7 @@ class _CertificateDetailsScreenState extends State { color: CupertinoColors.systemRed.resolveFrom(context), onPressed: () => Utils.confirmDelete(context, title, () async { Navigator.pop(context); - widget.onDelete(); + widget.onDelete!(); })))); } } diff --git a/lib/screens/siteConfig/CipherScreen.dart b/lib/screens/siteConfig/CipherScreen.dart index 5ac9b84..db8bfcd 100644 --- a/lib/screens/siteConfig/CipherScreen.dart +++ b/lib/screens/siteConfig/CipherScreen.dart @@ -6,7 +6,11 @@ import 'package:mobile_nebula/components/config/ConfigCheckboxItem.dart'; import 'package:mobile_nebula/components/config/ConfigSection.dart'; class CipherScreen extends StatefulWidget { - const CipherScreen({Key key, this.cipher, @required this.onSave}) : super(key: key); + const CipherScreen({ + Key? key, + required this.cipher, + required this.onSave, + }) : super(key: key); final String cipher; final ValueChanged onSave; @@ -16,7 +20,7 @@ class CipherScreen extends StatefulWidget { } class _CipherScreenState extends State { - String cipher; + late String cipher; bool changed = false; @override @@ -32,9 +36,7 @@ class _CipherScreenState extends State { changed: changed, onSave: () { Navigator.pop(context); - if (widget.onSave != null) { - widget.onSave(cipher); - } + widget.onSave(cipher); }, child: Column( children: [ diff --git a/lib/screens/siteConfig/LogVerbosityScreen.dart b/lib/screens/siteConfig/LogVerbosityScreen.dart index aed6374..a855183 100644 --- a/lib/screens/siteConfig/LogVerbosityScreen.dart +++ b/lib/screens/siteConfig/LogVerbosityScreen.dart @@ -6,7 +6,11 @@ import 'package:mobile_nebula/components/config/ConfigCheckboxItem.dart'; import 'package:mobile_nebula/components/config/ConfigSection.dart'; class LogVerbosityScreen extends StatefulWidget { - const LogVerbosityScreen({Key key, this.verbosity, @required this.onSave}) : super(key: key); + const LogVerbosityScreen({ + Key? key, + required this.verbosity, + required this.onSave, + }) : super(key: key); final String verbosity; final ValueChanged onSave; @@ -16,7 +20,7 @@ class LogVerbosityScreen extends StatefulWidget { } class _LogVerbosityScreenState extends State { - String verbosity; + late String verbosity; bool changed = false; @override @@ -32,9 +36,7 @@ class _LogVerbosityScreenState extends State { changed: changed, onSave: () { Navigator.pop(context); - if (widget.onSave != null) { - widget.onSave(verbosity); - } + widget.onSave(verbosity); }, child: Column( children: [ diff --git a/lib/screens/siteConfig/RenderedConfigScreen.dart b/lib/screens/siteConfig/RenderedConfigScreen.dart index 0abb06d..a6c9042 100644 --- a/lib/screens/siteConfig/RenderedConfigScreen.dart +++ b/lib/screens/siteConfig/RenderedConfigScreen.dart @@ -7,7 +7,11 @@ class RenderedConfigScreen extends StatelessWidget { final String config; final String name; - RenderedConfigScreen({Key key, this.config, this.name}) : super(key: key); + RenderedConfigScreen({ + Key? key, + required this.config, + required this.name, + }) : super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/screens/siteConfig/SiteConfigScreen.dart b/lib/screens/siteConfig/SiteConfigScreen.dart index ba47fce..9d6a6b7 100644 --- a/lib/screens/siteConfig/SiteConfigScreen.dart +++ b/lib/screens/siteConfig/SiteConfigScreen.dart @@ -22,9 +22,13 @@ import 'package:mobile_nebula/services/utils.dart'; //TODO: Enforce a name class SiteConfigScreen extends StatefulWidget { - const SiteConfigScreen({Key key, this.site, this.onSave}) : super(key: key); + const SiteConfigScreen({ + Key? key, + this.site, + required this.onSave, + }) : super(key: key); - final Site site; + final Site? site; // This is called after the target OS has saved the configuration final ValueChanged onSave; @@ -37,9 +41,9 @@ class _SiteConfigScreenState extends State { bool changed = false; bool newSite = false; bool debug = false; - Site site; - String pubKey; - String privKey; + late Site site; + String? pubKey; + String? privKey; static const platform = MethodChannel('net.defined.mobileNebula/NebulaVpnService'); final nameController = TextEditingController(); @@ -52,7 +56,7 @@ class _SiteConfigScreenState extends State { newSite = true; site = Site(); } else { - site = widget.site; + site = widget.site!; nameController.text = site.name; } @@ -61,7 +65,7 @@ class _SiteConfigScreenState extends State { @override Widget build(BuildContext context) { - if (pubKey == null) { + if (pubKey == null || privKey == null) { return Center( child: fpw.PlatformCircularProgressIndicator(cupertino: (_, __) { return fpw.CupertinoProgressIndicatorData(radius: 50); @@ -81,9 +85,7 @@ class _SiteConfigScreenState extends State { } Navigator.pop(context); - if (widget.onSave != null) { - widget.onSave(site); - } + widget.onSave(site); }, child: Column( children: [ @@ -126,17 +128,17 @@ class _SiteConfigScreenState extends State { } Widget _keys() { - final certError = site.certInfo == null || !site.certInfo.validity.valid; + final certError = site.certInfo == null || site.certInfo!.validity == null || !site.certInfo!.validity!.valid; var caError = site.ca.length == 0; if (!caError) { site.ca.forEach((ca) { - if (!ca.validity.valid) { + if (ca.validity == null || !ca.validity!.valid) { caError = true; } }); } - return ConfigSection( + return ConfigSection( label: "IDENTITY", children: [ ConfigPageItem( @@ -147,13 +149,13 @@ class _SiteConfigScreenState extends State { child: Icon(Icons.error, color: CupertinoColors.systemRed.resolveFrom(context), size: 20), padding: EdgeInsets.only(right: 5)) : Container(), - certError ? Text('Needs attention') : Text(site.certInfo.cert.details.name) + certError ? Text('Needs attention') : Text(site.certInfo?.cert.details.name ?? 'Unknown certificate') ]), onPressed: () { Utils.openPage(context, (context) { if (site.certInfo != null) { return CertificateDetailsScreen( - certInfo: site.certInfo, + certInfo: site.certInfo!, pubKey: pubKey, privKey: privKey, onReplace: (result) { @@ -165,13 +167,16 @@ class _SiteConfigScreenState extends State { }); } - return AddCertificateScreen(pubKey: pubKey, privKey: privKey, onSave: (result) { - setState(() { - changed = true; - site.certInfo = result.certInfo; - site.key = result.key; - }); - }); + return AddCertificateScreen( + pubKey: pubKey!, + privKey: privKey!, + onSave: (result) { + setState(() { + changed = true; + site.certInfo = result.certInfo; + site.key = result.key; + }); + }); }); }, ), diff --git a/lib/screens/siteConfig/StaticHostmapScreen.dart b/lib/screens/siteConfig/StaticHostmapScreen.dart index 43399db..e6b9a36 100644 --- a/lib/screens/siteConfig/StaticHostmapScreen.dart +++ b/lib/screens/siteConfig/StaticHostmapScreen.dart @@ -15,35 +15,42 @@ class _IPAndPort { final FocusNode focusNode; IPAndPort destination; - _IPAndPort({this.focusNode, this.destination}); + _IPAndPort({required this.focusNode, required this.destination}); } class StaticHostmapScreen extends StatefulWidget { - const StaticHostmapScreen( - {Key key, this.nebulaIp, this.destinations, this.lighthouse = false, this.onDelete, @required this.onSave}) - : super(key: key); + StaticHostmapScreen({ + Key? key, + this.nebulaIp = '', + destinations, + this.lighthouse = false, + this.onDelete, + required this.onSave, + }) : this.destinations = destinations ?? [], + super(key: key); final List destinations; final String nebulaIp; final bool lighthouse; final ValueChanged onSave; - final Function onDelete; + final Function? onDelete; @override _StaticHostmapScreenState createState() => _StaticHostmapScreenState(); } class _StaticHostmapScreenState extends State { - Map _destinations = {}; - String _nebulaIp; - bool _lighthouse; + late Map _destinations; + late String _nebulaIp; + late bool _lighthouse; bool changed = false; @override void initState() { _nebulaIp = widget.nebulaIp; _lighthouse = widget.lighthouse; - widget.destinations?.forEach((dest) { + _destinations = {}; + widget.destinations.forEach((dest) { _destinations[UniqueKey()] = _IPAndPort(focusNode: FocusNode(), destination: dest); }); @@ -75,7 +82,9 @@ class _StaticHostmapScreenState extends State { crossAxisAlignment: CrossAxisAlignment.end, textInputAction: TextInputAction.next, onSaved: (v) { - _nebulaIp = v; + if (v != null) { + _nebulaIp = v; + } })), ConfigItem( label: Text('Lighthouse'), @@ -107,7 +116,7 @@ class _StaticHostmapScreenState extends State { color: CupertinoColors.systemRed.resolveFrom(context), onPressed: () => Utils.confirmDelete(context, 'Delete host map?', () { Navigator.of(context).pop(); - widget.onDelete(); + widget.onDelete!(); }), ))) : Container() @@ -116,15 +125,13 @@ class _StaticHostmapScreenState extends State { _onSave() { Navigator.pop(context); - if (widget.onSave != null) { - var map = Hostmap(nebulaIp: _nebulaIp, destinations: [], lighthouse: _lighthouse); + var map = Hostmap(nebulaIp: _nebulaIp, destinations: [], lighthouse: _lighthouse); - _destinations.forEach((_, dest) { - map.destinations.add(dest.destination); - }); + _destinations.forEach((_, dest) { + map.destinations.add(dest.destination); + }); - widget.onSave(map); - } + widget.onSave(map); } List _buildHosts() { @@ -152,7 +159,9 @@ class _StaticHostmapScreenState extends State { noBorder: true, initialValue: dest.destination, onSaved: (v) { - dest.destination = v; + if (v != null) { + dest.destination = v; + } }, )), ]), diff --git a/lib/screens/siteConfig/StaticHostsScreen.dart b/lib/screens/siteConfig/StaticHostsScreen.dart index 76c7daf..6746948 100644 --- a/lib/screens/siteConfig/StaticHostsScreen.dart +++ b/lib/screens/siteConfig/StaticHostsScreen.dart @@ -18,12 +18,20 @@ class _Hostmap { List destinations; bool lighthouse; - _Hostmap({this.focusNode, this.nebulaIp, destinations, this.lighthouse}) - : destinations = destinations ?? []; + _Hostmap({ + required this.focusNode, + required this.nebulaIp, + required this.destinations, + required this.lighthouse, + }); } class StaticHostsScreen extends StatefulWidget { - const StaticHostsScreen({Key key, @required this.hostmap, @required this.onSave}) : super(key: key); + const StaticHostsScreen({ + Key? key, + required this.hostmap, + required this.onSave, + }) : super(key: key); final Map hostmap; final ValueChanged> onSave; @@ -38,7 +46,7 @@ class _StaticHostsScreenState extends State { @override void initState() { - widget.hostmap?.forEach((key, map) { + widget.hostmap.forEach((key, map) { _hostmap[UniqueKey()] = _Hostmap(focusNode: FocusNode(), nebulaIp: key, destinations: map.destinations, lighthouse: map.lighthouse); }); @@ -59,14 +67,12 @@ class _StaticHostsScreenState extends State { _onSave() { Navigator.pop(context); - if (widget.onSave != null) { - Map map = {}; - _hostmap.forEach((_, host) { - map[host.nebulaIp] = StaticHost(destinations: host.destinations, lighthouse: host.lighthouse); - }); + Map map = {}; + _hostmap.forEach((_, host) { + map[host.nebulaIp] = StaticHost(destinations: host.destinations, lighthouse: host.lighthouse); + }); - widget.onSave(map); - } + widget.onSave(map); } List _buildHosts() { diff --git a/lib/screens/siteConfig/UnsafeRouteScreen.dart b/lib/screens/siteConfig/UnsafeRouteScreen.dart index 82753a8..a7d3771 100644 --- a/lib/screens/siteConfig/UnsafeRouteScreen.dart +++ b/lib/screens/siteConfig/UnsafeRouteScreen.dart @@ -10,18 +10,23 @@ import 'package:mobile_nebula/models/UnsafeRoute.dart'; import 'package:mobile_nebula/services/utils.dart'; class UnsafeRouteScreen extends StatefulWidget { - const UnsafeRouteScreen({Key key, this.route, this.onDelete, @required this.onSave}) : super(key: key); + const UnsafeRouteScreen({ + Key? key, + required this.route, + required this.onSave, + this.onDelete, + }) : super(key: key); final UnsafeRoute route; final ValueChanged onSave; - final Function onDelete; + final Function? onDelete; @override _UnsafeRouteScreenState createState() => _UnsafeRouteScreenState(); } class _UnsafeRouteScreenState extends State { - UnsafeRoute route; + late UnsafeRoute route; bool changed = false; FocusNode routeFocus = FocusNode(); @@ -36,7 +41,7 @@ class _UnsafeRouteScreenState extends State { @override Widget build(BuildContext context) { - var routeCIDR = route?.route == null ? CIDR() : CIDR.fromString(route?.route); + var routeCIDR = route.route == null ? CIDR() : CIDR.fromString(route.route!); return FormPage( title: widget.onDelete == null ? 'New Unsafe Route' : 'Edit Unsafe Route', @@ -57,7 +62,7 @@ class _UnsafeRouteScreenState extends State { ConfigItem( label: Text('Via'), content: IPFormField( - initialValue: route?.via ?? "", + initialValue: route.via ?? '', ipOnly: true, help: 'nebula ip', textAlign: TextAlign.end, @@ -66,7 +71,9 @@ class _UnsafeRouteScreenState extends State { focusNode: viaFocus, nextFocusNode: mtuFocus, onSaved: (v) { - route.via = v; + if (v != null) { + route.via = v; + } })), //TODO: Android doesn't appear to support route based MTU, figure this out // ConfigItem( @@ -94,7 +101,7 @@ class _UnsafeRouteScreenState extends State { color: CupertinoColors.systemRed.resolveFrom(context), onPressed: () => Utils.confirmDelete(context, 'Delete unsafe route?', () { Navigator.of(context).pop(); - widget.onDelete(); + widget.onDelete!(); }), ))) : Container() @@ -103,8 +110,6 @@ class _UnsafeRouteScreenState extends State { _onSave() { Navigator.pop(context); - if (widget.onSave != null) { - widget.onSave(route); - } + widget.onSave(route); } } diff --git a/lib/screens/siteConfig/UnsafeRoutesScreen.dart b/lib/screens/siteConfig/UnsafeRoutesScreen.dart index 10a8094..85fe5a0 100644 --- a/lib/screens/siteConfig/UnsafeRoutesScreen.dart +++ b/lib/screens/siteConfig/UnsafeRoutesScreen.dart @@ -8,7 +8,11 @@ import 'package:mobile_nebula/screens/siteConfig/UnsafeRouteScreen.dart'; import 'package:mobile_nebula/services/utils.dart'; class UnsafeRoutesScreen extends StatefulWidget { - const UnsafeRoutesScreen({Key key, @required this.unsafeRoutes, @required this.onSave}) : super(key: key); + const UnsafeRoutesScreen({ + Key? key, + required this.unsafeRoutes, + required this.onSave, + }) : super(key: key); final List unsafeRoutes; final ValueChanged> onSave; @@ -18,11 +22,12 @@ class UnsafeRoutesScreen extends StatefulWidget { } class _UnsafeRoutesScreenState extends State { - Map unsafeRoutes = {}; + late Map unsafeRoutes; bool changed = false; @override void initState() { + unsafeRoutes = {}; widget.unsafeRoutes.forEach((route) { unsafeRoutes[UniqueKey()] = route; }); @@ -43,9 +48,7 @@ class _UnsafeRoutesScreenState extends State { _onSave() { Navigator.pop(context); - if (widget.onSave != null) { - widget.onSave(unsafeRoutes.values.toList()); - } + widget.onSave(unsafeRoutes.values.toList()); } List _buildRoutes() { @@ -53,7 +56,7 @@ class _UnsafeRoutesScreenState extends State { List items = []; unsafeRoutes.forEach((key, route) { items.add(ConfigPageItem( - label: Text(route.route), + label: Text(route.route ?? ''), labelWidth: ipWidth, content: Text('via ${route.via}', textAlign: TextAlign.end), onPressed: () { diff --git a/lib/services/settings.dart b/lib/services/settings.dart index e8a9d01..0f2bb39 100644 --- a/lib/services/settings.dart +++ b/lib/services/settings.dart @@ -7,7 +7,6 @@ import 'package:mobile_nebula/services/storage.dart'; class Settings { final _storage = Storage(); StreamController _change = StreamController.broadcast(); - var _ready = Completer(); var _settings = Map(); bool get useSystemColors { @@ -78,14 +77,11 @@ class Settings { } Settings._internal() { - _ready = Completer(); - _storage.readFile("config.json").then((rawConfig) { if (rawConfig != null) { _settings = jsonDecode(rawConfig); } - _ready.complete(); _change.add(null); }); } diff --git a/lib/services/share.dart b/lib/services/share.dart index d8a3623..ceddc76 100644 --- a/lib/services/share.dart +++ b/lib/services/share.dart @@ -12,13 +12,17 @@ class Share { /// - title: Title of message or subject if sending an email /// - text: The text to share /// - filename: The filename to use if sending over airdrop for example - static Future share({@required String title, @required String text, @required String filename}) async { - assert(title != null && title.isNotEmpty); - assert(text != null && text.isNotEmpty); - assert(filename != null && filename.isNotEmpty); + static Future share({ + required String title, + required String text, + required String filename, + }) async { + assert(title.isNotEmpty); + assert(text.isNotEmpty); + assert(filename.isNotEmpty); - if (title == null || title.isEmpty) { - throw FlutterError('Title cannot be null'); + if (title.isEmpty) { + throw FlutterError('Title cannot be empty'); } final bool success = await _channel.invokeMethod('share', { @@ -34,14 +38,18 @@ class Share { /// - title: Title of message or subject if sending an email /// - filePath: Path to the file to share /// - filename: An optional filename to override the existing file - static Future shareFile({@required String title, @required String filePath, String filename}) async { - assert(title != null && title.isNotEmpty); - assert(filePath != null && filePath.isNotEmpty); + static Future shareFile({ + required String title, + required String filePath, + String? filename, + }) async { + assert(title.isNotEmpty); + assert(filePath.isNotEmpty); - if (title == null || title.isEmpty) { - throw FlutterError('Title cannot be null'); - } else if (filePath == null || filePath.isEmpty) { - throw FlutterError('FilePath cannot be null'); + if (title.isEmpty) { + throw FlutterError('Title cannot be empty'); + } else if (filePath.isEmpty) { + throw FlutterError('FilePath cannot be empty'); } final bool success = await _channel.invokeMethod('shareFile', { diff --git a/lib/services/storage.dart b/lib/services/storage.dart index 7abbb33..af8dcc6 100644 --- a/lib/services/storage.dart +++ b/lib/services/storage.dart @@ -34,7 +34,7 @@ class Storage { return directory.path; } - Future readFile(String path) async { + Future readFile(String path) async { try { final parent = await localPath; final file = File(p.join(parent, path)); diff --git a/lib/services/utils.dart b/lib/services/utils.dart index 8922c9c..fbc261f 100644 --- a/lib/services/utils.dart +++ b/lib/services/utils.dart @@ -52,7 +52,7 @@ class Utils { /// Builds a simple leading widget that pops the current screen. /// Provide your own onPressed to override that behavior, just remember you have to pop - static Widget leadingBackWidget(BuildContext context, {label = 'Back', Function onPressed}) { + static Widget leadingBackWidget(BuildContext context, {label = 'Back', Function? onPressed}) { if (Platform.isIOS) { return CupertinoButton( child: Row(children: [Icon(context.platformIcons.back), Text(label)]), @@ -83,11 +83,11 @@ class Utils { static Widget trailingSaveWidget(BuildContext context, Function onPressed) { return CupertinoButton( - child: Text('Save', style: TextStyle( - fontWeight: FontWeight.bold, - //TODO: For some reason on android if inherit is the default of true the text color here turns to the background color - inherit: Platform.isIOS ? true : false - )), + child: Text('Save', + style: TextStyle( + fontWeight: FontWeight.bold, + //TODO: For some reason on android if inherit is the default of true the text color here turns to the background color + inherit: Platform.isIOS ? true : false)), padding: Platform.isAndroid ? null : EdgeInsets.zero, onPressed: () => onPressed()); } @@ -122,7 +122,7 @@ class Utils { }); } - static popError(BuildContext context, String title, String error, {StackTrace stack}) { + static popError(BuildContext context, String title, String error, {StackTrace? stack}) { if (stack != null) { error += '\n${stack.toString()}'; } @@ -170,14 +170,14 @@ class Utils { return int.parse(parts[3]) | int.parse(parts[2]) << 8 | int.parse(parts[1]) << 16 | int.parse(parts[0]) << 24; } - static Future pickFile(BuildContext context) async { + static Future pickFile(BuildContext context) async { await FilePicker.platform.clearTemporaryFiles(); final result = await FilePicker.platform.pickFiles(allowMultiple: false); if (result == null) { return null; } - final file = File(result.files.first.path); + final file = File(result!.files.first.path!); return file.readAsString(); } } diff --git a/lib/validators/ipValidator.dart b/lib/validators/ipValidator.dart index 69a9226..50959d0 100644 --- a/lib/validators/ipValidator.dart +++ b/lib/validators/ipValidator.dart @@ -1,6 +1,10 @@ import 'dart:io'; -bool ipValidator(String str, bool enableIPV6) { +bool ipValidator(String? str, bool enableIPV6) { + if (str == null) { + return false; + } + final ia = InternetAddress.tryParse(str); if (ia == null) { return false; diff --git a/nebula/go.mod b/nebula/go.mod index 584993c..25d252d 100644 --- a/nebula/go.mod +++ b/nebula/go.mod @@ -6,7 +6,7 @@ go 1.18 require ( github.com/sirupsen/logrus v1.8.1 - github.com/slackhq/nebula v1.6.0 + github.com/slackhq/nebula v1.6.1-0.20220919174748-4c0ae3df5ef7 golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 gopkg.in/yaml.v2 v2.4.0 ) @@ -33,7 +33,6 @@ require ( github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect github.com/vishvananda/netlink v1.1.0 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect - golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/net v0.0.0-20220403103023-749bd193bc2b // indirect golang.org/x/sys v0.0.0-20220406155245-289d7a0edf71 // indirect diff --git a/nebula/go.sum b/nebula/go.sum index 4ceba64..878aba1 100644 --- a/nebula/go.sum +++ b/nebula/go.sum @@ -210,8 +210,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/slackhq/nebula v1.6.0 h1:1M2txSJq5Jef/A68Kw6SwdLS0PMtjhx4X509ZBHtG54= -github.com/slackhq/nebula v1.6.0/go.mod h1:UmkqnXe4O53QwToSl/gG7sM4BroQwAB7dd4hUaT6MlI= +github.com/slackhq/nebula v1.6.1-0.20220919174748-4c0ae3df5ef7 h1:u06zUX/HdLxZnyewES34xIjRELl5xDXCLseiiBn6Dyo= +github.com/slackhq/nebula v1.6.1-0.20220919174748-4c0ae3df5ef7/go.mod h1:UmkqnXe4O53QwToSl/gG7sM4BroQwAB7dd4hUaT6MlI= github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8= github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -231,7 +231,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -249,7 +248,6 @@ golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= @@ -270,10 +268,7 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk= -golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -316,7 +311,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220403103023-749bd193bc2b h1:vI32FkLJNAWtGD4BwkThwEy6XS7ZLLMHkSkYfF8M0W0= @@ -383,7 +377,6 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220406155245-289d7a0edf71 h1:PRD0hj6tTuUnCFD08vkvjkYFbQg/9lV8KIxe1y4/cvU= @@ -444,7 +437,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pubspec.lock b/pubspec.lock index add365e..cc6fee2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.2" - barcode_scan: - dependency: "direct main" - description: - name: barcode_scan - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1" + version: "2.9.0" boolean_selector: dependency: transitive description: @@ -28,28 +21,21 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" + version: "1.2.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0" + version: "1.16.0" crypto: dependency: transitive description: @@ -70,14 +56,14 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" ffi: dependency: transitive description: name: ffi url: "https://pub.dartlang.org" source: hosted - version: "1.1.2" + version: "2.0.1" file: dependency: transitive description: @@ -91,33 +77,33 @@ packages: name: file_picker url: "https://pub.dartlang.org" source: hosted - version: "3.0.4" - fixnum: - dependency: transitive - description: - name: fixnum - url: "https://pub.dartlang.org" - source: hosted - version: "0.10.11" + version: "5.0.1" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_barcode_scanner: + dependency: "direct main" + description: + name: flutter_barcode_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" flutter_platform_widgets: dependency: "direct main" description: name: flutter_platform_widgets url: "https://pub.dartlang.org" source: hosted - version: "1.12.1" + version: "2.0.0" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.0.7" flutter_test: dependency: "direct dev" description: flutter @@ -134,21 +120,28 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3" + version: "0.6.4" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.11" + version: "0.12.12" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0" package_info: dependency: "direct main" description: @@ -162,14 +155,14 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.2" path_provider: dependency: "direct main" description: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" + version: "2.0.11" path_provider_android: dependency: transitive description: @@ -190,7 +183,7 @@ packages: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.7" path_provider_macos: dependency: transitive description: @@ -211,7 +204,7 @@ packages: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.1.3" platform: dependency: transitive description: @@ -233,13 +226,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.2.4" - protobuf: - dependency: transitive - description: - name: protobuf - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.4" pull_to_refresh: dependency: "direct main" description: @@ -258,7 +244,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.9.0" stack_trace: dependency: transitive description: @@ -279,21 +265,21 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.3" + version: "0.4.12" typed_data: dependency: transitive description: @@ -370,14 +356,14 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" win32: dependency: transitive description: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.3.3" + version: "2.7.0" xdg_directories: dependency: transitive description: @@ -386,5 +372,5 @@ packages: source: hosted version: "0.2.0" sdks: - dart: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + dart: ">=2.18.1 <3.0.0" + flutter: ">=3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index edb47b7..6a57861 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,10 +11,10 @@ description: Mobile Nebula Client # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.1.0+52 +version: 0.1.0+54 environment: - sdk: ">=2.1.0 <3.0.0" + sdk: '>=2.18.1 <3.0.0' dependencies: flutter: @@ -23,14 +23,14 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 - flutter_platform_widgets: ^1.2.0 - path_provider: ^2.0.1 - file_picker: ^3.0.2+2 - barcode_scan: ^3.0.1 + flutter_platform_widgets: ^2.0.0 + path_provider: ^2.0.11 + file_picker: ^5.0.1 uuid: ^3.0.4 package_info: ^2.0.0 url_launcher: ^6.0.6 pull_to_refresh: ^2.0.0 + flutter_barcode_scanner: ^2.0.0 dev_dependencies: flutter_test: