Compare commits

...

11 commits

24 changed files with 79 additions and 61 deletions

View file

@ -1,12 +1,11 @@
name: Flutter format name: Flutter check
on: on:
push: push:
branches: branches:
- main - main
pull_request: pull_request:
paths: paths:
- '.github/workflows/flutterfmt.yml' - '.github/workflows/fluttercheck.yml'
- '.github/workflows/flutterfmt.sh'
- '**.dart' - '**.dart'
jobs: jobs:
flutterfmt: flutterfmt:
@ -25,3 +24,19 @@ jobs:
- name: Check formating - name: Check formating
run: dart format -l120 lib/ --set-exit-if-changed --suppress-analytics --output none run: dart format -l120 lib/ --set-exit-if-changed --suppress-analytics --output none
flutterlint:
name: Run flutter lint
runs-on: ubuntu-latest
steps:
- name: Install flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.29.0'
- name: Check out code
uses: actions/checkout@v4
with:
show-progress: false
- name: Check linting
run: dart fix --dry-run

View file

@ -111,10 +111,7 @@ class _IPAndPortFormField extends FormFieldState<IPAndPort> {
@override @override
void didUpdateWidget(IPAndPortFormField oldWidget) { void didUpdateWidget(IPAndPortFormField oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
var update = IPAndPort( var update = IPAndPort(ip: widget.ipController?.text, port: int.tryParse(widget.portController?.text ?? ""));
ip: widget.ipController?.text,
port: int.tryParse(widget.portController?.text ?? ""),
);
bool shouldUpdate = false; bool shouldUpdate = false;
if (widget.ipController != oldWidget.ipController) { if (widget.ipController != oldWidget.ipController) {

View file

@ -106,8 +106,9 @@ class _IPFormField extends FormFieldState<String> {
oldWidget.controller?.removeListener(_handleControllerChanged); oldWidget.controller?.removeListener(_handleControllerChanged);
widget.controller?.addListener(_handleControllerChanged); widget.controller?.addListener(_handleControllerChanged);
if (oldWidget.controller != null && widget.controller == null) if (oldWidget.controller != null && widget.controller == null) {
_controller = TextEditingController.fromValue(oldWidget.controller!.value); _controller = TextEditingController.fromValue(oldWidget.controller!.value);
}
if (widget.controller != null) { if (widget.controller != null) {
setValue(widget.controller!.text); setValue(widget.controller!.text);
if (oldWidget.controller == null) _controller = null; if (oldWidget.controller == null) _controller = null;

View file

@ -115,8 +115,9 @@ class _PlatformTextFormFieldState extends FormFieldState<String> {
oldWidget.controller?.removeListener(_handleControllerChanged); oldWidget.controller?.removeListener(_handleControllerChanged);
widget.controller?.addListener(_handleControllerChanged); widget.controller?.addListener(_handleControllerChanged);
if (oldWidget.controller != null && widget.controller == null) if (oldWidget.controller != null && widget.controller == null) {
_controller = TextEditingController.fromValue(oldWidget.controller!.value); _controller = TextEditingController.fromValue(oldWidget.controller!.value);
}
if (widget.controller != null) { if (widget.controller != null) {
setValue(widget.controller!.text); setValue(widget.controller!.text);
if (oldWidget.controller == null) _controller = null; if (oldWidget.controller == null) _controller = null;

View file

@ -15,18 +15,18 @@ class ConfigSection extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final border = BorderSide(color: borderColor ?? Utils.configSectionBorder(context)); final border = BorderSide(color: borderColor ?? Utils.configSectionBorder(context));
List<Widget> _children = []; List<Widget> mappedChildren = [];
final len = children.length; final len = children.length;
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
_children.add(children[i]); mappedChildren.add(children[i]);
if (i < len - 1) { if (i < len - 1) {
double pad = 15; double pad = 15;
if (children[i + 1].runtimeType.toString() == 'ConfigButtonItem') { if (children[i + 1].runtimeType.toString() == 'ConfigButtonItem') {
pad = 0; pad = 0;
} }
_children.add( mappedChildren.add(
Padding( Padding(
padding: EdgeInsets.only(left: pad), padding: EdgeInsets.only(left: pad),
child: Divider(height: 1, color: Utils.configSectionBorder(context)), child: Divider(height: 1, color: Utils.configSectionBorder(context)),
@ -44,7 +44,7 @@ class ConfigSection extends StatelessWidget {
border: Border(top: border, bottom: border), border: Border(top: border, bottom: border),
color: Utils.configItemBackground(context), color: Utils.configItemBackground(context),
), ),
child: Column(children: _children), child: Column(children: mappedChildren),
), ),
], ],
); );

View file

@ -12,8 +12,10 @@ import 'package:mobile_nebula/services/settings.dart';
import 'package:mobile_nebula/services/theme.dart'; import 'package:mobile_nebula/services/theme.dart';
import 'package:mobile_nebula/services/utils.dart'; import 'package:mobile_nebula/services/utils.dart';
import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:flutter_web_plugins/url_strategy.dart';
Future<void> main() async { Future<void> main() async {
usePathUrlStrategy();
var settings = Settings(); var settings = Settings();
if (settings.trackErrors) { if (settings.trackErrors) {
await SentryFlutter.init((options) { await SentryFlutter.init((options) {
@ -106,6 +108,7 @@ class _AppState extends State<App> {
}, },
cupertino: (_, __) => CupertinoAppData(theme: CupertinoThemeData(brightness: brightness)), cupertino: (_, __) => CupertinoAppData(theme: CupertinoThemeData(brightness: brightness)),
onGenerateRoute: (settings) { onGenerateRoute: (settings) {
print(settings);
if (settings.name == '/') { if (settings.name == '/') {
return platformPageRoute(context: context, builder: (context) => MainScreen(dnEnrolled)); return platformPageRoute(context: context, builder: (context) => MainScreen(dnEnrolled));
} }

View file

@ -3,9 +3,7 @@ class CertificateInfo {
String? rawCert; String? rawCert;
CertificateValidity? validity; CertificateValidity? validity;
CertificateInfo.debug({this.rawCert = ""}) CertificateInfo.debug({this.rawCert = ""}) : cert = Certificate.debug(), validity = CertificateValidity.debug();
: cert = Certificate.debug(),
validity = CertificateValidity.debug();
CertificateInfo.fromJson(Map<String, dynamic> json) CertificateInfo.fromJson(Map<String, dynamic> json)
: cert = Certificate.fromJson(json['Cert']), : cert = Certificate.fromJson(json['Cert']),

View file

@ -15,7 +15,7 @@ class Site {
late 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 /// Signals that something about this site has changed. onError is called with an error string if there was an error
StreamController _change = StreamController.broadcast(); final StreamController _change = StreamController.broadcast();
// Identifiers // Identifiers
late String name; late String name;
@ -169,15 +169,15 @@ class Site {
List<dynamic> rawUnsafeRoutes = json['unsafeRoutes']; List<dynamic> rawUnsafeRoutes = json['unsafeRoutes'];
List<UnsafeRoute> unsafeRoutes = []; List<UnsafeRoute> unsafeRoutes = [];
rawUnsafeRoutes.forEach((val) { for (var val in rawUnsafeRoutes) {
unsafeRoutes.add(UnsafeRoute.fromJson(val)); unsafeRoutes.add(UnsafeRoute.fromJson(val));
}); }
List<dynamic> rawCA = json['ca']; List<dynamic> rawCA = json['ca'];
List<CertificateInfo> ca = []; List<CertificateInfo> ca = [];
rawCA.forEach((val) { for (var val in rawCA) {
ca.add(CertificateInfo.fromJson(val)); ca.add(CertificateInfo.fromJson(val));
}); }
CertificateInfo? certInfo; CertificateInfo? certInfo;
if (json['cert'] != null) { if (json['cert'] != null) {
@ -186,9 +186,9 @@ class Site {
List<dynamic> rawErrors = json["errors"]; List<dynamic> rawErrors = json["errors"];
List<String> errors = []; List<String> errors = [];
rawErrors.forEach((error) { for (var error in rawErrors) {
errors.add(error); errors.add(error);
}); }
return { return {
"name": json["name"], "name": json["name"],
@ -295,9 +295,9 @@ class Site {
List<dynamic> f = jsonDecode(ret); List<dynamic> f = jsonDecode(ret);
List<HostInfo> hosts = []; List<HostInfo> hosts = [];
f.forEach((v) { for (var v in f) {
hosts.add(HostInfo.fromJson(v)); hosts.add(HostInfo.fromJson(v));
}); }
return hosts; return hosts;
} on PlatformException catch (err) { } on PlatformException catch (err) {
@ -317,9 +317,9 @@ class Site {
List<dynamic> f = jsonDecode(ret); List<dynamic> f = jsonDecode(ret);
List<HostInfo> hosts = []; List<HostInfo> hosts = [];
f.forEach((v) { for (var v in f) {
hosts.add(HostInfo.fromJson(v)); hosts.add(HostInfo.fromJson(v));
}); }
return hosts; return hosts;
} on PlatformException catch (err) { } on PlatformException catch (err) {

View file

@ -10,9 +10,9 @@ class StaticHost {
var list = json['destinations'] as List<dynamic>; var list = json['destinations'] as List<dynamic>;
var result = <IPAndPort>[]; var result = <IPAndPort>[];
list.forEach((item) { for (var item in list) {
result.add(IPAndPort.fromString(item)); result.add(IPAndPort.fromString(item));
}); }
return StaticHost(lighthouse: json['lighthouse'], destinations: result); return StaticHost(lighthouse: json['lighthouse'], destinations: result);
} }

View file

@ -49,6 +49,7 @@ class _EnrollmentScreenState extends State<EnrollmentScreen> {
static const platform = MethodChannel('net.defined.mobileNebula/NebulaVpnService'); static const platform = MethodChannel('net.defined.mobileNebula/NebulaVpnService');
@override
void initState() { void initState() {
code = widget.code; code = widget.code;
super.initState(); super.initState();
@ -172,7 +173,7 @@ class _EnrollmentScreenState extends State<EnrollmentScreen> {
} }
Widget _codeEntry() { Widget _codeEntry() {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); final GlobalKey<FormState> formKey = GlobalKey<FormState>();
String? validator(String? value) { String? validator(String? value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
@ -182,7 +183,7 @@ class _EnrollmentScreenState extends State<EnrollmentScreen> {
} }
Future<void> onSubmit() async { Future<void> onSubmit() async {
final bool isValid = _formKey.currentState?.validate() ?? false; final bool isValid = formKey.currentState?.validate() ?? false;
if (!isValid) { if (!isValid) {
return; return;
} }
@ -205,7 +206,7 @@ class _EnrollmentScreenState extends State<EnrollmentScreen> {
), ),
); );
final form = Form(key: _formKey, child: Platform.isAndroid ? input : ConfigSection(children: [input])); final form = Form(key: formKey, child: Platform.isAndroid ? input : ConfigSection(children: [input]));
return Column( return Column(
children: [ children: [

View file

@ -124,7 +124,7 @@ class _HostInfoScreenState extends State<HostInfoScreen> {
final double ipWidth = final double ipWidth =
Utils.textSize("000.000.000.000:000000", CupertinoTheme.of(context).textTheme.textStyle).width; Utils.textSize("000.000.000.000:000000", CupertinoTheme.of(context).textTheme.textStyle).width;
hostInfo.remoteAddresses.forEach((remoteObj) { for (var remoteObj in hostInfo.remoteAddresses) {
String remote = remoteObj.toString(); String remote = remoteObj.toString();
items.add( items.add(
ConfigCheckboxItem( ConfigCheckboxItem(
@ -148,7 +148,7 @@ class _HostInfoScreenState extends State<HostInfoScreen> {
}, },
), ),
); );
}); }
return ConfigSection(label: items.isNotEmpty ? 'Tap to change the active address' : null, children: items); return ConfigSection(label: items.isNotEmpty ? 'Tap to change the active address' : null, children: items);
} }
@ -159,7 +159,7 @@ class _HostInfoScreenState extends State<HostInfoScreen> {
final double ipWidth = final double ipWidth =
Utils.textSize("000.000.000.000:000000", CupertinoTheme.of(context).textTheme.textStyle).width; Utils.textSize("000.000.000.000:000000", CupertinoTheme.of(context).textTheme.textStyle).width;
hostInfo.remoteAddresses.forEach((remoteObj) { for (var remoteObj in hostInfo.remoteAddresses) {
String remote = remoteObj.toString(); String remote = remoteObj.toString();
items.add( items.add(
ConfigCheckboxItem( ConfigCheckboxItem(
@ -169,7 +169,7 @@ class _HostInfoScreenState extends State<HostInfoScreen> {
checked: currentRemote == remote, checked: currentRemote == remote,
), ),
); );
}); }
return ConfigSection(label: items.isNotEmpty ? 'REMOTES' : null, children: items); return ConfigSection(label: items.isNotEmpty ? 'REMOTES' : null, children: items);
} }

View file

@ -207,7 +207,7 @@ class _MainScreenState extends State<MainScreen> {
} }
List<Widget> items = []; List<Widget> items = [];
sites!.forEach((site) { for (var site in sites!) {
items.add( items.add(
SiteItem( SiteItem(
key: Key(site.id), key: Key(site.id),
@ -223,7 +223,7 @@ class _MainScreenState extends State<MainScreen> {
}, },
), ),
); );
}); }
Widget child = ReorderableListView( Widget child = ReorderableListView(
shrinkWrap: true, shrinkWrap: true,

View file

@ -117,14 +117,14 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
} }
List<Widget> items = []; List<Widget> items = [];
site.errors.forEach((error) { for (var error in site.errors) {
items.add( items.add(
ConfigItem( ConfigItem(
labelWidth: 0, labelWidth: 0,
content: Padding(padding: EdgeInsets.symmetric(vertical: 10), child: SelectableText(error)), content: Padding(padding: EdgeInsets.symmetric(vertical: 10), child: SelectableText(error)),
), ),
); );
}); }
return ConfigSection( return ConfigSection(
label: 'ERRORS', label: 'ERRORS',

View file

@ -85,7 +85,7 @@ class _AdvancedScreenState extends State<AdvancedScreen> {
//TODO: Auto select on focus? //TODO: Auto select on focus?
content: content:
widget.site.managed widget.site.managed
? Text(settings.lhDuration.toString() + " seconds", textAlign: TextAlign.right) ? Text("${settings.lhDuration} seconds", textAlign: TextAlign.right)
: PlatformTextFormField( : PlatformTextFormField(
initialValue: settings.lhDuration.toString(), initialValue: settings.lhDuration.toString(),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,

View file

@ -39,9 +39,9 @@ class _CAListScreenState extends State<CAListScreen> {
@override @override
void initState() { void initState() {
widget.cas.forEach((ca) { for (var ca in widget.cas) {
cas[ca.cert.fingerprint] = ca; cas[ca.cert.fingerprint] = ca;
}); }
super.initState(); super.initState();
} }
@ -115,14 +115,14 @@ class _CAListScreenState extends State<CAListScreen> {
var ignored = 0; var ignored = 0;
List<dynamic> certs = jsonDecode(rawCerts); List<dynamic> certs = jsonDecode(rawCerts);
certs.forEach((rawCert) { for (var rawCert in certs) {
final info = CertificateInfo.fromJson(rawCert); final info = CertificateInfo.fromJson(rawCert);
if (!info.cert.details.isCa) { if (!info.cert.details.isCa) {
ignored++; ignored++;
return; continue;
} }
cas[info.cert.fingerprint] = info; cas[info.cert.fingerprint] = info;
}); }
if (ignored > 0) { if (ignored > 0) {
error = 'One or more certificates were ignored because they were not certificate authorities.'; error = 'One or more certificates were ignored because they were not certificate authorities.';

View file

@ -7,7 +7,7 @@ class RenderedConfigScreen extends StatelessWidget {
final String config; final String config;
final String name; final String name;
RenderedConfigScreen({super.key, required this.config, required this.name}); const RenderedConfigScreen({super.key, required this.config, required this.name});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View file

@ -163,11 +163,11 @@ class _SiteConfigScreenState extends State<SiteConfigScreen> {
if (!site.managed) { if (!site.managed) {
caError = site.ca.isEmpty; caError = site.ca.isEmpty;
if (!caError) { if (!caError) {
site.ca.forEach((ca) { for (var ca in site.ca) {
if (ca.validity == null || !ca.validity!.valid) { if (ca.validity == null || !ca.validity!.valid) {
caError = true; caError = true;
} }
}); }
} }
} }

View file

@ -50,9 +50,9 @@ class _StaticHostmapScreenState extends State<StaticHostmapScreen> {
_nebulaIp = widget.nebulaIp; _nebulaIp = widget.nebulaIp;
_lighthouse = widget.lighthouse; _lighthouse = widget.lighthouse;
_destinations = {}; _destinations = {};
widget.destinations.forEach((dest) { for (var dest in widget.destinations) {
_destinations[UniqueKey()] = _IPAndPort(focusNode: FocusNode(), destination: dest); _destinations[UniqueKey()] = _IPAndPort(focusNode: FocusNode(), destination: dest);
}); }
if (_destinations.isEmpty) { if (_destinations.isEmpty) {
_addDestination(); _addDestination();

View file

@ -32,7 +32,7 @@ class StaticHostsScreen extends StatefulWidget {
} }
class _StaticHostsScreenState extends State<StaticHostsScreen> { class _StaticHostsScreenState extends State<StaticHostsScreen> {
Map<Key, _Hostmap> _hostmap = {}; final Map<Key, _Hostmap> _hostmap = {};
bool changed = false; bool changed = false;
@override @override
@ -90,7 +90,7 @@ class _StaticHostsScreenState extends State<StaticHostsScreen> {
], ],
), ),
labelWidth: ipWidth, labelWidth: ipWidth,
content: Text(host.destinations.length.toString() + ' items', textAlign: TextAlign.end), content: Text('${host.destinations.length} items', textAlign: TextAlign.end),
onPressed: () { onPressed: () {
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
return StaticHostmapScreen( return StaticHostmapScreen(

View file

@ -24,9 +24,9 @@ class _UnsafeRoutesScreenState extends State<UnsafeRoutesScreen> {
@override @override
void initState() { void initState() {
unsafeRoutes = {}; unsafeRoutes = {};
widget.unsafeRoutes.forEach((route) { for (var route in widget.unsafeRoutes) {
unsafeRoutes[UniqueKey()] = route; unsafeRoutes[UniqueKey()] = route;
}); }
super.initState(); super.initState();
} }

View file

@ -10,8 +10,8 @@ bool DEFAULT_TRACK_ERRORS = true;
class Settings { class Settings {
final _storage = Storage(); final _storage = Storage();
StreamController _change = StreamController.broadcast(); final StreamController _change = StreamController.broadcast();
var _settings = Map<String, dynamic>(); var _settings = <String, dynamic>{};
bool get useSystemColors { bool get useSystemColors {
return _getBool('systemDarkMode', true); return _getBool('systemDarkMode', true);

View file

@ -39,12 +39,12 @@ class Utils {
Navigator.push(context, platformPageRoute(context: context, builder: pageToDisplayBuilder)); Navigator.push(context, platformPageRoute(context: context, builder: pageToDisplayBuilder));
} }
static String itemCountFormat(int items, {singleSuffix = "item", multiSuffix = "items"}) { static String itemCountFormat(int items, {String singleSuffix = "item", String multiSuffix = "items"}) {
if (items == 1) { if (items == 1) {
return items.toString() + " " + singleSuffix; return "$items $singleSuffix";
} }
return items.toString() + " " + multiSuffix; return "$items $multiSuffix";
} }
/// Builds a simple leading widget that pops the current screen. /// Builds a simple leading widget that pops the current screen.

View file

@ -188,7 +188,7 @@ packages:
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_web_plugins: flutter_web_plugins:
dependency: transitive dependency: "direct main"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"

View file

@ -19,6 +19,8 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_web_plugins:
sdk: flutter
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.