3
0
Fork 0

Ran flutter format -l 120 --suppress-analytics lib (#38)

This commit is contained in:
Nate Brown 2021-05-03 16:58:04 -05:00 committed by GitHub
parent 3a37802f4d
commit 9934f226e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 329 additions and 317 deletions

View File

@ -56,42 +56,42 @@ class _CIDRFieldState extends State<CIDRField> {
return Container( return Container(
child: Row(children: <Widget>[ child: Row(children: <Widget>[
Expanded( Expanded(
child: Padding( child: Padding(
padding: EdgeInsets.fromLTRB(6, 6, 2, 6), padding: EdgeInsets.fromLTRB(6, 6, 2, 6),
child: IPField( child: IPField(
help: widget.ipHelp, help: widget.ipHelp,
ipOnly: true, ipOnly: true,
textPadding: EdgeInsets.all(0), textPadding: EdgeInsets.all(0),
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
textAlign: TextAlign.end, textAlign: TextAlign.end,
focusNode: widget.focusNode, focusNode: widget.focusNode,
nextFocusNode: bitsFocus, nextFocusNode: bitsFocus,
onChanged: (val) {
cidr.ip = val;
widget.onChanged(cidr);
},
controller: widget.ipController,
))),
Text("/"),
Container(
width: Utils.textSize("bits", textStyle).width + 12,
padding: EdgeInsets.fromLTRB(2, 6, 6, 6),
child: SpecialTextField(
keyboardType: TextInputType.number,
focusNode: bitsFocus,
nextFocusNode: widget.nextFocusNode,
controller: widget.bitsController,
onChanged: (val) { onChanged: (val) {
cidr.bits = int.tryParse(val ?? ""); cidr.ip = val;
widget.onChanged(cidr); widget.onChanged(cidr);
}, },
maxLength: 2, controller: widget.ipController,
inputFormatters: [FilteringTextInputFormatter.digitsOnly], ))),
textInputAction: widget.textInputAction ?? TextInputAction.done, Text("/"),
placeholder: 'bits', Container(
)) width: Utils.textSize("bits", textStyle).width + 12,
])); padding: EdgeInsets.fromLTRB(2, 6, 6, 6),
child: SpecialTextField(
keyboardType: TextInputType.number,
focusNode: bitsFocus,
nextFocusNode: widget.nextFocusNode,
controller: widget.bitsController,
onChanged: (val) {
cidr.bits = int.tryParse(val ?? "");
widget.onChanged(cidr);
},
maxLength: 2,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
textInputAction: widget.textInputAction ?? TextInputAction.done,
placeholder: 'bits',
))
]));
} }
@override @override

View File

@ -8,7 +8,13 @@ import 'package:mobile_nebula/services/utils.dart';
/// SimplePage with a form and built in validation and confirmation to discard changes if any are made /// SimplePage with a form and built in validation and confirmation to discard changes if any are made
class FormPage extends StatefulWidget { class FormPage extends StatefulWidget {
const FormPage( const FormPage(
{Key key, this.title, @required this.child, @required this.onSave, @required this.changed, this.hideSave = false, this.scrollController}) {Key key,
this.title,
@required this.child,
@required this.onSave,
@required this.changed,
this.hideSave = false,
this.scrollController})
: super(key: key); : super(key: key);
final String title; final String title;

View File

@ -48,9 +48,7 @@ class IPField extends StatelessWidget {
onChanged: onChanged, onChanged: onChanged,
maxLength: ipOnly ? 15 : null, maxLength: ipOnly ? 15 : null,
maxLengthEnforced: ipOnly ? true : false, maxLengthEnforced: ipOnly ? true : false,
inputFormatters: ipOnly inputFormatters: ipOnly ? [IPTextInputFormatter()] : [FilteringTextInputFormatter.allow(RegExp(r'[^\s]+'))],
? [IPTextInputFormatter()]
: [FilteringTextInputFormatter.allow(RegExp(r'[^\s]+'))],
textInputAction: this.textInputAction, textInputAction: this.textInputAction,
placeholder: help, placeholder: help,
)); ));
@ -68,16 +66,17 @@ class IPTextInputFormatter extends TextInputFormatter {
return whitelistedPattern return whitelistedPattern
.allMatches(substring) .allMatches(substring)
.map<String>((Match match) => match.group(0)) .map<String>((Match match) => match.group(0))
.join().replaceAll(RegExp(r','), '.'); .join()
.replaceAll(RegExp(r','), '.');
}, },
); );
} }
} }
TextEditingValue _selectionAwareTextManipulation( TextEditingValue _selectionAwareTextManipulation(
TextEditingValue value, TextEditingValue value,
String substringManipulation(String substring), String substringManipulation(String substring),
) { ) {
final int selectionStartIndex = value.selection.start; final int selectionStartIndex = value.selection.start;
final int selectionEndIndex = value.selection.end; final int selectionEndIndex = value.selection.end;
String manipulatedText; String manipulatedText;
@ -85,15 +84,9 @@ TextEditingValue _selectionAwareTextManipulation(
if (selectionStartIndex < 0 || selectionEndIndex < 0) { if (selectionStartIndex < 0 || selectionEndIndex < 0) {
manipulatedText = substringManipulation(value.text); manipulatedText = substringManipulation(value.text);
} else { } else {
final String beforeSelection = substringManipulation( final String beforeSelection = substringManipulation(value.text.substring(0, selectionStartIndex));
value.text.substring(0, selectionStartIndex) final String inSelection = substringManipulation(value.text.substring(selectionStartIndex, selectionEndIndex));
); final String afterSelection = substringManipulation(value.text.substring(selectionEndIndex));
final String inSelection = substringManipulation(
value.text.substring(selectionStartIndex, selectionEndIndex)
);
final String afterSelection = substringManipulation(
value.text.substring(selectionEndIndex)
);
manipulatedText = beforeSelection + inSelection + afterSelection; manipulatedText = beforeSelection + inSelection + afterSelection;
if (value.selection.baseOffset > value.selection.extentOffset) { if (value.selection.baseOffset > value.selection.extentOffset) {
manipulatedSelection = value.selection.copyWith( manipulatedSelection = value.selection.copyWith(
@ -110,8 +103,6 @@ TextEditingValue _selectionAwareTextManipulation(
return TextEditingValue( return TextEditingValue(
text: manipulatedText, text: manipulatedText,
selection: manipulatedSelection ?? const TextSelection.collapsed(offset: -1), selection: manipulatedSelection ?? const TextSelection.collapsed(offset: -1),
composing: manipulatedText == value.text composing: manipulatedText == value.text ? value.composing : TextRange.empty,
? value.composing
: TextRange.empty,
); );
} }

View File

@ -45,14 +45,16 @@ class SimplePage extends StatelessWidget {
final VoidCallback onLoading; final VoidCallback onLoading;
final RefreshController refreshController; final RefreshController refreshController;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget realChild = child; Widget realChild = child;
var addScrollbar = this.scrollbar; var addScrollbar = this.scrollbar;
if (scrollable == SimpleScrollable.vertical || scrollable == SimpleScrollable.both) { if (scrollable == SimpleScrollable.vertical || scrollable == SimpleScrollable.both) {
realChild = SingleChildScrollView(scrollDirection: Axis.vertical, child: realChild, controller: refreshController == null ? scrollController : null); realChild = SingleChildScrollView(
scrollDirection: Axis.vertical,
child: realChild,
controller: refreshController == null ? scrollController : null);
addScrollbar = true; addScrollbar = true;
} }
@ -67,15 +69,15 @@ class SimplePage extends StatelessWidget {
footerTriggerDistance: -100, footerTriggerDistance: -100,
maxUnderScrollExtent: 100, maxUnderScrollExtent: 100,
child: SmartRefresher( child: SmartRefresher(
scrollController: scrollController, scrollController: scrollController,
onRefresh: onRefresh, onRefresh: onRefresh,
onLoading: onLoading, onLoading: onLoading,
controller: refreshController, controller: refreshController,
child: realChild, child: realChild,
enablePullUp: onLoading != null, enablePullUp: onLoading != null,
enablePullDown: onRefresh != null, enablePullDown: onRefresh != null,
footer: ClassicFooter(loadStyle: LoadStyle.ShowWhenLoading), footer: ClassicFooter(loadStyle: LoadStyle.ShowWhenLoading),
)); ));
addScrollbar = true; addScrollbar = true;
} }

View File

@ -5,7 +5,8 @@ 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 // 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 { class SpecialButton extends StatefulWidget {
const SpecialButton({Key key, this.child, this.color, this.onPressed, this.useButtonTheme = false, this.decoration}) : super(key: key); const SpecialButton({Key key, this.child, this.color, this.onPressed, this.useButtonTheme = false, this.decoration})
: super(key: key);
final Widget child; final Widget child;
final Color color; final Color color;
@ -33,12 +34,12 @@ class _SpecialButtonState extends State<SpecialButton> with SingleTickerProvider
return Material( return Material(
textStyle: textStyle, textStyle: textStyle,
child: Ink( child: Ink(
decoration: widget.decoration, decoration: widget.decoration,
color: widget.color, color: widget.color,
child: InkWell( child: InkWell(
child: widget.child, child: widget.child,
onTap: widget.onPressed, onTap: widget.onPressed,
))); )));
} }
Widget _buildGeneric() { Widget _buildGeneric() {
@ -48,22 +49,21 @@ class _SpecialButtonState extends State<SpecialButton> with SingleTickerProvider
} }
return Container( return Container(
decoration: widget.decoration, decoration: widget.decoration,
child: GestureDetector( child: GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onTapDown: _handleTapDown, onTapDown: _handleTapDown,
onTapUp: _handleTapUp, onTapUp: _handleTapUp,
onTapCancel: _handleTapCancel, onTapCancel: _handleTapCancel,
onTap: widget.onPressed, onTap: widget.onPressed,
child: Semantics( child: Semantics(
button: true, button: true,
child: FadeTransition( child: FadeTransition(
opacity: _opacityAnimation, opacity: _opacityAnimation,
child: DefaultTextStyle(style: textStyle, child: Container(child: widget.child, color: widget.color)), child: DefaultTextStyle(style: textStyle, child: Container(child: widget.child, color: widget.color)),
),
), ),
), ));
)
);
} }
// Eyeballed values. Feel free to tweak. // Eyeballed values. Feel free to tweak.

View File

@ -23,15 +23,15 @@ import 'package:flutter/gestures.dart';
const int iOSHorizontalOffset = -2; const int iOSHorizontalOffset = -2;
class _TextSpanEditingController extends TextEditingController { class _TextSpanEditingController extends TextEditingController {
_TextSpanEditingController({@required TextSpan textSpan}): _TextSpanEditingController({@required TextSpan textSpan})
assert(textSpan != null), : assert(textSpan != null),
_textSpan = textSpan, _textSpan = textSpan,
super(text: textSpan.toPlainText()); super(text: textSpan.toPlainText());
final TextSpan _textSpan; final TextSpan _textSpan;
@override @override
TextSpan buildTextSpan({TextStyle style ,bool withComposing}) { TextSpan buildTextSpan({TextStyle style, bool withComposing}) {
// This does not care about composing. // This does not care about composing.
return TextSpan( return TextSpan(
style: style, style: style,
@ -49,7 +49,7 @@ class _TextSpanEditingController extends TextEditingController {
class _SpecialSelectableTextSelectionGestureDetectorBuilder extends TextSelectionGestureDetectorBuilder { class _SpecialSelectableTextSelectionGestureDetectorBuilder extends TextSelectionGestureDetectorBuilder {
_SpecialSelectableTextSelectionGestureDetectorBuilder({ _SpecialSelectableTextSelectionGestureDetectorBuilder({
@required _SpecialSelectableTextState state, @required _SpecialSelectableTextState state,
}) : _state = state, }) : _state = state,
super(delegate: state); super(delegate: state);
final _SpecialSelectableTextState _state; final _SpecialSelectableTextState _state;
@ -109,8 +109,7 @@ class _SpecialSelectableTextSelectionGestureDetectorBuilder extends TextSelectio
break; break;
} }
} }
if (_state.widget.onTap != null) if (_state.widget.onTap != null) _state.widget.onTap();
_state.widget.onTap();
} }
@override @override
@ -202,40 +201,40 @@ class SpecialSelectableText extends StatefulWidget {
/// must not be null. If specified, the [maxLines] argument must be greater /// must not be null. If specified, the [maxLines] argument must be greater
/// than zero. /// than zero.
const SpecialSelectableText( const SpecialSelectableText(
this.data, { this.data, {
Key key, Key key,
this.focusNode, this.focusNode,
this.style, this.style,
this.strutStyle, this.strutStyle,
this.textAlign, this.textAlign,
this.textDirection, this.textDirection,
this.textScaleFactor, this.textScaleFactor,
this.showCursor = false, this.showCursor = false,
this.autofocus = false, this.autofocus = false,
ToolbarOptions toolbarOptions, ToolbarOptions toolbarOptions,
this.minLines, this.minLines,
this.maxLines, this.maxLines,
this.cursorWidth = 2.0, this.cursorWidth = 2.0,
this.cursorRadius, this.cursorRadius,
this.cursorColor, this.cursorColor,
this.dragStartBehavior = DragStartBehavior.start, this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection = true, this.enableInteractiveSelection = true,
this.onTap, this.onTap,
this.scrollPhysics, this.scrollPhysics,
this.textHeightBehavior, this.textHeightBehavior,
this.textWidthBasis, this.textWidthBasis,
}) : assert(showCursor != null), }) : assert(showCursor != null),
assert(autofocus != null), assert(autofocus != null),
assert(dragStartBehavior != null), assert(dragStartBehavior != null),
assert(maxLines == null || maxLines > 0), assert(maxLines == null || maxLines > 0),
assert(minLines == null || minLines > 0), assert(minLines == null || minLines > 0),
assert( assert(
(maxLines == null) || (minLines == null) || (maxLines >= minLines), (maxLines == null) || (minLines == null) || (maxLines >= minLines),
'minLines can\'t be greater than maxLines', 'minLines can\'t be greater than maxLines',
), ),
assert( assert(
data != null, data != null,
'A non-null String must be provided to a SpecialSelectableText widget.', 'A non-null String must be provided to a SpecialSelectableText widget.',
), ),
textSpan = null, textSpan = null,
toolbarOptions = toolbarOptions ?? toolbarOptions = toolbarOptions ??
@ -252,40 +251,40 @@ class SpecialSelectableText extends StatefulWidget {
/// ///
/// The [autofocus] and [dragStartBehavior] arguments must not be null. /// The [autofocus] and [dragStartBehavior] arguments must not be null.
const SpecialSelectableText.rich( const SpecialSelectableText.rich(
this.textSpan, { this.textSpan, {
Key key, Key key,
this.focusNode, this.focusNode,
this.style, this.style,
this.strutStyle, this.strutStyle,
this.textAlign, this.textAlign,
this.textDirection, this.textDirection,
this.textScaleFactor, this.textScaleFactor,
this.showCursor = false, this.showCursor = false,
this.autofocus = false, this.autofocus = false,
ToolbarOptions toolbarOptions, ToolbarOptions toolbarOptions,
this.minLines, this.minLines,
this.maxLines, this.maxLines,
this.cursorWidth = 2.0, this.cursorWidth = 2.0,
this.cursorRadius, this.cursorRadius,
this.cursorColor, this.cursorColor,
this.dragStartBehavior = DragStartBehavior.start, this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection = true, this.enableInteractiveSelection = true,
this.onTap, this.onTap,
this.scrollPhysics, this.scrollPhysics,
this.textHeightBehavior, this.textHeightBehavior,
this.textWidthBasis, this.textWidthBasis,
}) : assert(showCursor != null), }) : assert(showCursor != null),
assert(autofocus != null), assert(autofocus != null),
assert(dragStartBehavior != null), assert(dragStartBehavior != null),
assert(maxLines == null || maxLines > 0), assert(maxLines == null || maxLines > 0),
assert(minLines == null || minLines > 0), assert(minLines == null || minLines > 0),
assert( assert(
(maxLines == null) || (minLines == null) || (maxLines >= minLines), (maxLines == null) || (minLines == null) || (maxLines >= minLines),
'minLines can\'t be greater than maxLines', 'minLines can\'t be greater than maxLines',
), ),
assert( assert(
textSpan != null, textSpan != null,
'A non-null TextSpan must be provided to a SpecialSelectableText.rich widget.', 'A non-null TextSpan must be provided to a SpecialSelectableText.rich widget.',
), ),
data = null, data = null,
toolbarOptions = toolbarOptions ?? toolbarOptions = toolbarOptions ??
@ -434,13 +433,17 @@ class SpecialSelectableText extends StatefulWidget {
properties.add(DoubleProperty('cursorWidth', cursorWidth, defaultValue: 2.0)); properties.add(DoubleProperty('cursorWidth', cursorWidth, defaultValue: 2.0));
properties.add(DiagnosticsProperty<Radius>('cursorRadius', cursorRadius, defaultValue: null)); properties.add(DiagnosticsProperty<Radius>('cursorRadius', cursorRadius, defaultValue: null));
properties.add(DiagnosticsProperty<Color>('cursorColor', cursorColor, defaultValue: null)); properties.add(DiagnosticsProperty<Color>('cursorColor', cursorColor, defaultValue: null));
properties.add(FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled')); properties.add(
FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled'));
properties.add(DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null)); properties.add(DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null));
properties.add(DiagnosticsProperty<TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null)); properties
.add(DiagnosticsProperty<TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null));
} }
} }
class _SpecialSelectableTextState extends State<SpecialSelectableText> with AutomaticKeepAliveClientMixin implements TextSelectionGestureDetectorBuilderDelegate { class _SpecialSelectableTextState extends State<SpecialSelectableText>
with AutomaticKeepAliveClientMixin
implements TextSelectionGestureDetectorBuilderDelegate {
EditableTextState get _editableText => editableTextKey.currentState; EditableTextState get _editableText => editableTextKey.currentState;
_TextSpanEditingController _controller; _TextSpanEditingController _controller;
@ -468,18 +471,14 @@ class _SpecialSelectableTextState extends State<SpecialSelectableText> with Auto
void initState() { void initState() {
super.initState(); super.initState();
_selectionGestureDetectorBuilder = _SpecialSelectableTextSelectionGestureDetectorBuilder(state: this); _selectionGestureDetectorBuilder = _SpecialSelectableTextSelectionGestureDetectorBuilder(state: this);
_controller = _TextSpanEditingController( _controller = _TextSpanEditingController(textSpan: widget.textSpan ?? TextSpan(text: widget.data));
textSpan: widget.textSpan ?? TextSpan(text: widget.data)
);
} }
@override @override
void didUpdateWidget(SpecialSelectableText oldWidget) { void didUpdateWidget(SpecialSelectableText oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (widget.data != oldWidget.data || widget.textSpan != oldWidget.textSpan) { if (widget.data != oldWidget.data || widget.textSpan != oldWidget.textSpan) {
_controller = _TextSpanEditingController( _controller = _TextSpanEditingController(textSpan: widget.textSpan ?? TextSpan(text: widget.data));
textSpan: widget.textSpan ?? TextSpan(text: widget.data)
);
} }
if (_effectiveFocusNode.hasFocus && _controller.selection.isCollapsed) { if (_effectiveFocusNode.hasFocus && _controller.selection.isCollapsed) {
_showSelectionHandles = false; _showSelectionHandles = false;
@ -525,20 +524,15 @@ class _SpecialSelectableTextState extends State<SpecialSelectableText> with Auto
bool _shouldShowSelectionHandles(SelectionChangedCause cause) { bool _shouldShowSelectionHandles(SelectionChangedCause cause) {
// When the text field is activated by something that doesn't trigger the // When the text field is activated by something that doesn't trigger the
// selection overlay, we shouldn't show the handles either. // selection overlay, we shouldn't show the handles either.
if (!_selectionGestureDetectorBuilder.shouldShowSelectionToolbar) if (!_selectionGestureDetectorBuilder.shouldShowSelectionToolbar) return false;
return false;
if (_controller.selection.isCollapsed) if (_controller.selection.isCollapsed) return false;
return false;
if (cause == SelectionChangedCause.keyboard) if (cause == SelectionChangedCause.keyboard) return false;
return false;
if (cause == SelectionChangedCause.longPress) if (cause == SelectionChangedCause.longPress) return true;
return true;
if (_controller.text.isNotEmpty) if (_controller.text.isNotEmpty) return true;
return true;
return false; return false;
} }
@ -555,9 +549,10 @@ class _SpecialSelectableTextState extends State<SpecialSelectableText> with Auto
assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasMediaQuery(context));
assert(debugCheckHasDirectionality(context)); assert(debugCheckHasDirectionality(context));
assert( assert(
!(widget.style != null && widget.style.inherit == false && !(widget.style != null &&
(widget.style.fontSize == null || widget.style.textBaseline == null)), widget.style.inherit == false &&
'inherit false style must supply fontSize and textBaseline', (widget.style.fontSize == null || widget.style.textBaseline == null)),
'inherit false style must supply fontSize and textBaseline',
); );
final ThemeData themeData = Theme.of(context); final ThemeData themeData = Theme.of(context);
@ -596,8 +591,7 @@ class _SpecialSelectableTextState extends State<SpecialSelectableText> with Auto
final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
TextStyle effectiveTextStyle = widget.style; TextStyle effectiveTextStyle = widget.style;
if (widget.style == null || widget.style.inherit) if (widget.style == null || widget.style.inherit) effectiveTextStyle = defaultTextStyle.style.merge(widget.style);
effectiveTextStyle = defaultTextStyle.style.merge(widget.style);
if (MediaQuery.boldTextOverride(context)) if (MediaQuery.boldTextOverride(context))
effectiveTextStyle = effectiveTextStyle.merge(const TextStyle(fontWeight: FontWeight.bold)); effectiveTextStyle = effectiveTextStyle.merge(const TextStyle(fontWeight: FontWeight.bold));
final Widget child = RepaintBoundary( final Widget child = RepaintBoundary(
@ -648,13 +642,12 @@ class _SpecialSelectableTextState extends State<SpecialSelectableText> with Auto
_effectiveFocusNode.requestFocus(); _effectiveFocusNode.requestFocus();
}, },
child: _selectionGestureDetectorBuilder.buildGestureDetector( child: _selectionGestureDetectorBuilder.buildGestureDetector(
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
child: RawKeyboardListener( child: RawKeyboardListener(
focusNode: _keyFocusNode, focusNode: _keyFocusNode,
onKey: _onKey, onKey: _onKey,
child: child, child: child,
) )),
),
); );
} }

View File

@ -216,7 +216,6 @@ class Site {
}); });
return hosts; return hosts;
} on PlatformException catch (err) { } on PlatformException catch (err) {
//TODO: fix this message //TODO: fix this message
throw err.details ?? err.message ?? err.toString(); throw err.details ?? err.message ?? err.toString();
@ -239,7 +238,6 @@ class Site {
}); });
return hosts; return hosts;
} on PlatformException catch (err) { } on PlatformException catch (err) {
throw err.details ?? err.message ?? err.toString(); throw err.details ?? err.message ?? err.toString();
} catch (err) { } catch (err) {
@ -251,7 +249,6 @@ class Site {
try { try {
var res = await Future.wait([this.listHostmap(), this.listPendingHostmap()]); var res = await Future.wait([this.listHostmap(), this.listPendingHostmap()]);
return {"active": res[0], "pending": res[1]}; return {"active": res[0], "pending": res[1]};
} on PlatformException catch (err) { } on PlatformException catch (err) {
throw err.details ?? err.message ?? err.toString(); throw err.details ?? err.message ?? err.toString();
} catch (err) { } catch (err) {
@ -265,14 +262,14 @@ class Site {
Future<HostInfo> getHostInfo(String vpnIp, bool pending) async { Future<HostInfo> getHostInfo(String vpnIp, bool pending) async {
try { try {
var ret = await platform.invokeMethod("active.getHostInfo", <String, dynamic>{"id": id, "vpnIp": vpnIp, "pending": pending}); var ret = await platform
.invokeMethod("active.getHostInfo", <String, dynamic>{"id": id, "vpnIp": vpnIp, "pending": pending});
final h = jsonDecode(ret); final h = jsonDecode(ret);
if (h == null) { if (h == null) {
return null; return null;
} }
return HostInfo.fromJson(h); return HostInfo.fromJson(h);
} on PlatformException catch (err) { } on PlatformException catch (err) {
throw err.details ?? err.message ?? err.toString(); throw err.details ?? err.message ?? err.toString();
} catch (err) { } catch (err) {
@ -282,14 +279,14 @@ class Site {
Future<HostInfo> setRemoteForTunnel(String vpnIp, String addr) async { Future<HostInfo> setRemoteForTunnel(String vpnIp, String addr) async {
try { try {
var ret = await platform.invokeMethod("active.setRemoteForTunnel", <String, dynamic>{"id": id, "vpnIp": vpnIp, "addr": addr}); var ret = await platform
.invokeMethod("active.setRemoteForTunnel", <String, dynamic>{"id": id, "vpnIp": vpnIp, "addr": addr});
final h = jsonDecode(ret); final h = jsonDecode(ret);
if (h == null) { if (h == null) {
return null; return null;
} }
return HostInfo.fromJson(h); return HostInfo.fromJson(h);
} on PlatformException catch (err) { } on PlatformException catch (err) {
throw err.details ?? err.message ?? err.toString(); throw err.details ?? err.message ?? err.toString();
} catch (err) { } catch (err) {
@ -300,7 +297,6 @@ class Site {
Future<bool> closeTunnel(String vpnIp) async { Future<bool> closeTunnel(String vpnIp) async {
try { try {
return await platform.invokeMethod("active.closeTunnel", <String, dynamic>{"id": id, "vpnIp": vpnIp}); return await platform.invokeMethod("active.closeTunnel", <String, dynamic>{"id": id, "vpnIp": vpnIp});
} on PlatformException catch (err) { } on PlatformException catch (err) {
throw err.details ?? err.message ?? err.toString(); throw err.details ?? err.message ?? err.toString();
} catch (err) { } catch (err) {

View File

@ -48,18 +48,32 @@ class _AboutScreenState extends State<AboutScreen> {
title: 'About', title: 'About',
child: Column(children: [ child: Column(children: [
ConfigSection(children: <Widget>[ ConfigSection(children: <Widget>[
ConfigItem(label: Text('App version'), labelWidth: 150, content: _buildText('${packageInfo.version}-${packageInfo.buildNumber} (sha: $gitSha)')), ConfigItem(
ConfigItem(label: Text('Nebula version'), labelWidth: 150, content: _buildText('$nebulaVersion ($goVersion)')), label: Text('App version'),
ConfigItem(label: Text('Flutter version'), labelWidth: 150, content: _buildText(flutterVersion['frameworkVersion'])), labelWidth: 150,
ConfigItem(label: Text('Dart version'), labelWidth: 150, content: _buildText(flutterVersion['dartSdkVersion'])), 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'])),
ConfigItem(
label: Text('Dart version'), labelWidth: 150, content: _buildText(flutterVersion['dartSdkVersion'])),
]), ]),
ConfigSection(children: <Widget>[ ConfigSection(children: <Widget>[
//TODO: wire up these other pages //TODO: wire up these other pages
// ConfigPageItem(label: Text('Changelog'), labelWidth: 300, onPressed: () => Utils.launchUrl('https://defined.net/mobile/changelog', context)), // ConfigPageItem(label: Text('Changelog'), labelWidth: 300, onPressed: () => Utils.launchUrl('https://defined.net/mobile/changelog', context)),
ConfigPageItem(label: Text('Privacy policy'), labelWidth: 300, onPressed: () => Utils.launchUrl('https://defined.net/privacy-policy', context)), ConfigPageItem(
label: Text('Privacy policy'),
labelWidth: 300,
onPressed: () => Utils.launchUrl('https://defined.net/privacy-policy', context)),
// ConfigPageItem(label: Text('Licenses'), labelWidth: 300, onPressed: () => Utils.launchUrl('https://defined.net/mobile/license', context)), // ConfigPageItem(label: Text('Licenses'), labelWidth: 300, onPressed: () => Utils.launchUrl('https://defined.net/mobile/license', context)),
]), ]),
Padding(padding: EdgeInsets.only(top: 20), child: Text('Copyright © 2020 Defined Networking, Inc', textAlign: TextAlign.center,)), Padding(
padding: EdgeInsets.only(top: 20),
child: Text(
'Copyright © 2020 Defined Networking, Inc',
textAlign: TextAlign.center,
)),
]), ]),
); );
} }

View File

@ -161,16 +161,16 @@ class _HostInfoScreenState extends State<HostInfoScreen> {
child: Text('Close Tunnel'), child: Text('Close Tunnel'),
color: CupertinoColors.systemRed.resolveFrom(context), color: CupertinoColors.systemRed.resolveFrom(context),
onPressed: () => Utils.confirmDelete(context, 'Close Tunnel?', () async { onPressed: () => Utils.confirmDelete(context, 'Close Tunnel?', () async {
try { try {
await widget.site.closeTunnel(hostInfo.vpnIp); await widget.site.closeTunnel(hostInfo.vpnIp);
if (widget.onChanged != null) { if (widget.onChanged != null) {
widget.onChanged(); widget.onChanged();
} }
Navigator.pop(context); Navigator.pop(context);
} catch (err) { } catch (err) {
Utils.popError(context, 'Error while trying to close the tunnel', err); Utils.popError(context, 'Error while trying to close the tunnel', err);
} }
}, deleteLabel: 'Close')))); }, deleteLabel: 'Close'))));
} }
_getHostInfo() async { _getHostInfo() async {

View File

@ -87,18 +87,19 @@ class _MainScreenState extends State<MainScreen> {
Widget _buildNoSites() { Widget _buildNoSites() {
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Center(child: Column( child: Center(
children: <Widget>[ child: Column(
Padding( children: <Widget>[
padding: const EdgeInsets.fromLTRB(0, 8.0, 0, 8.0), Padding(
child: Text('Welcome to Nebula!', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)), padding: const EdgeInsets.fromLTRB(0, 8.0, 0, 8.0),
child: Text('Welcome to Nebula!', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)),
),
Text('You don\'t have any site configurations installed yet. Hit the plus button above to get started.',
textAlign: TextAlign.center),
],
), ),
Text('You don\'t have any site configurations installed yet. Hit the plus button above to get started.', ));
textAlign: TextAlign.center),
],
),
));
} }
Widget _buildSites() { Widget _buildSites() {
@ -146,10 +147,7 @@ class _MainScreenState extends State<MainScreen> {
} }
// The theme here is to remove the hardcoded canvas border reordering forces on us // The theme here is to remove the hardcoded canvas border reordering forces on us
return Theme( return Theme(data: Theme.of(context).copyWith(canvasColor: Colors.transparent), child: child);
data: Theme.of(context).copyWith(canvasColor: Colors.transparent),
child: child
);
} }
Widget _debugSave() { Widget _debugSave() {
@ -173,15 +171,16 @@ mUOcsdFcCZiXrj7ryQIG1+WfqA46w71A/lV4nAc=
-----END NEBULA CERTIFICATE-----'''; -----END NEBULA CERTIFICATE-----''';
var s = Site( var s = Site(
name: "DEBUG TEST", name: "DEBUG TEST",
id: uuid.v4(), id: uuid.v4(),
staticHostmap: { staticHostmap: {
"10.1.0.1": StaticHost(lighthouse: true, destinations: [IPAndPort(ip: '10.1.1.53', port: 4242), IPAndPort(ip: '1::1', port: 4242)]) "10.1.0.1": StaticHost(
}, lighthouse: true,
ca: [CertificateInfo.debug(rawCert: ca)], destinations: [IPAndPort(ip: '10.1.1.53', port: 4242), IPAndPort(ip: '1::1', port: 4242)])
certInfo: CertificateInfo.debug(rawCert: cert), },
unsafeRoutes: [UnsafeRoute(route: '10.3.3.3/32', via: '10.1.0.1')] ca: [CertificateInfo.debug(rawCert: ca)],
); certInfo: CertificateInfo.debug(rawCert: cert),
unsafeRoutes: [UnsafeRoute(route: '10.3.3.3/32', via: '10.1.0.1')]);
s.key = '''-----BEGIN NEBULA X25519 PRIVATE KEY----- s.key = '''-----BEGIN NEBULA X25519 PRIVATE KEY-----
rmXnR1yvDZi1VPVmnNVY8NMsQpEpbbYlq7rul+ByQvg= rmXnR1yvDZi1VPVmnNVY8NMsQpEpbbYlq7rul+ByQvg=
@ -239,7 +238,8 @@ rmXnR1yvDZi1VPVmnNVY8NMsQpEpbbYlq7rul+ByQvg=
} }
if (hasErrors) { if (hasErrors) {
Utils.popError(context, "Site Error(s)", "1 or more sites have errors and need your attention, problem sites have a red border."); Utils.popError(context, "Site Error(s)",
"1 or more sites have errors and need your attention, problem sites have a red border.");
} }
sites.sort((a, b) { sites.sort((a, b) {

View File

@ -62,7 +62,6 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
} }
} }
setState(() {}); setState(() {});
}, onError: (err) { }, onError: (err) {
setState(() {}); setState(() {});
Utils.popError(context, "Error", err); Utils.popError(context, "Error", err);
@ -111,7 +110,8 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
List<Widget> items = []; List<Widget> items = [];
site.errors.forEach((error) { site.errors.forEach((error) {
items.add(ConfigItem( items.add(ConfigItem(
labelWidth: 0, content: Padding(padding: EdgeInsets.symmetric(vertical: 10), child: SpecialSelectableText(error)))); labelWidth: 0,
content: Padding(padding: EdgeInsets.symmetric(vertical: 10), child: SpecialSelectableText(error))));
}); });
return ConfigSection( return ConfigSection(

View File

@ -54,7 +54,10 @@ class _SiteLogsScreenState extends State<SiteLogsScreen> {
refreshController.loadComplete(); refreshController.loadComplete();
}, },
refreshController: refreshController, refreshController: refreshController,
child: Container(padding: EdgeInsets.all(5), constraints: logBoxConstraints(context), child: SpecialSelectableText(logs.trim(), style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14))), child: Container(
padding: EdgeInsets.all(5),
constraints: logBoxConstraints(context),
child: SpecialSelectableText(logs.trim(), style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14))),
bottomBar: _buildBottomBar(), bottomBar: _buildBottomBar(),
); );
} }
@ -78,7 +81,10 @@ class _SiteLogsScreenState extends State<SiteLogsScreen> {
padding: padding, padding: padding,
icon: Icon(context.platformIcons.share, size: 30), icon: Icon(context.platformIcons.share, size: 30),
onPressed: () { onPressed: () {
Share.shareFile(title: '${widget.site.name} logs', filePath: widget.site.logFile, filename: '${widget.site.name}.log'); Share.shareFile(
title: '${widget.site.name} logs',
filePath: widget.site.logFile,
filename: '${widget.site.name}.log');
}, },
)), )),
Expanded( Expanded(
@ -94,7 +100,8 @@ class _SiteLogsScreenState extends State<SiteLogsScreen> {
padding: padding, padding: padding,
icon: Icon(context.platformIcons.downArrow, size: 30), icon: Icon(context.platformIcons.downArrow, size: 30),
onPressed: () async { onPressed: () async {
controller.animateTo(controller.position.maxScrollExtent, duration: const Duration(milliseconds: 500), curve: Curves.linearToEaseOut); controller.animateTo(controller.position.maxScrollExtent,
duration: const Duration(milliseconds: 500), curve: Curves.linearToEaseOut);
}, },
)), )),
])); ]));

View File

@ -72,9 +72,7 @@ class _AddCertificateScreenState extends State<AddCertificateScreen> {
items.addAll(_buildShare()); items.addAll(_buildShare());
items.addAll(_buildLoadCert()); items.addAll(_buildLoadCert());
return SimplePage( return SimplePage(title: 'Certificate', child: Column(children: items));
title: 'Certificate',
child: Column(children: items));
} }
_generateKeys() async { _generateKeys() async {
@ -215,8 +213,8 @@ class _AddCertificateScreenState extends State<AddCertificateScreen> {
if (certs.length > 0) { if (certs.length > 0) {
var tryCertInfo = CertificateInfo.fromJson(certs.first); var tryCertInfo = CertificateInfo.fromJson(certs.first);
if (tryCertInfo.cert.details.isCa) { if (tryCertInfo.cert.details.isCa) {
return Utils.popError(context, 'Error loading certificate content', 'A certificate authority is not appropriate for a client certificate.'); return Utils.popError(context, 'Error loading certificate content',
'A certificate authority is not appropriate for a client certificate.');
} else if (!tryCertInfo.validity.valid) { } else if (!tryCertInfo.validity.valid) {
return Utils.popError(context, 'Certificate was invalid', tryCertInfo.validity.reason); return Utils.popError(context, 'Certificate was invalid', tryCertInfo.validity.reason);
} }
@ -232,10 +230,12 @@ class _AddCertificateScreenState extends State<AddCertificateScreen> {
// We have a cert, pop the details screen where they can hit save // We have a cert, pop the details screen where they can hit save
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
return CertificateDetailsScreen(certInfo: tryCertInfo, onSave: () { return CertificateDetailsScreen(
Navigator.pop(context); certInfo: tryCertInfo,
widget.onSave(CertificateResult(certInfo: tryCertInfo, key: privKey)); onSave: () {
}); Navigator.pop(context);
widget.onSave(CertificateResult(certInfo: tryCertInfo, key: privKey));
});
}); });
} }
} on PlatformException catch (err) { } on PlatformException catch (err) {

View File

@ -148,19 +148,21 @@ class _AdvancedScreenState extends State<AdvancedScreen> {
}); });
}), }),
ConfigPageItem( ConfigPageItem(
label: Text('Unsafe routes'), label: Text('Unsafe routes'),
labelWidth: 150, labelWidth: 150,
content: Text(Utils.itemCountFormat(settings.unsafeRoutes.length), textAlign: TextAlign.end), content: Text(Utils.itemCountFormat(settings.unsafeRoutes.length), textAlign: TextAlign.end),
onPressed: () { onPressed: () {
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
return UnsafeRoutesScreen(unsafeRoutes: settings.unsafeRoutes, onSave: (routes) { return UnsafeRoutesScreen(
setState(() { unsafeRoutes: settings.unsafeRoutes,
settings.unsafeRoutes = routes; onSave: (routes) {
changed = true; setState(() {
settings.unsafeRoutes = routes;
changed = true;
});
}); });
}); });
}); },
},
) )
], ],
), ),

View File

@ -201,7 +201,6 @@ class _CAListScreenState extends State<CAListScreen> {
setState(() {}); setState(() {});
} }
}); });
} catch (err) { } catch (err) {
return Utils.popError(context, 'Failed to load CA file', err.toString()); return Utils.popError(context, 'Failed to load CA file', err.toString());
} }

View File

@ -12,7 +12,8 @@ import 'package:mobile_nebula/services/utils.dart';
/// Displays the details of a CertificateInfo object. Respects incomplete objects (missing validity or rawCert) /// Displays the details of a CertificateInfo object. Respects incomplete objects (missing validity or rawCert)
class CertificateDetailsScreen extends StatefulWidget { class CertificateDetailsScreen extends StatefulWidget {
const CertificateDetailsScreen({Key key, this.certInfo, this.onDelete, this.onSave, this.onReplace}) : super(key: key); const CertificateDetailsScreen({Key key, this.certInfo, this.onDelete, this.onSave, this.onReplace})
: super(key: key);
final CertificateInfo certInfo; final CertificateInfo certInfo;
@ -78,8 +79,7 @@ class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
return ConfigSection(children: <Widget>[ return ConfigSection(children: <Widget>[
ConfigItem(label: Text('Name'), content: SpecialSelectableText(certInfo.cert.details.name)), ConfigItem(label: Text('Name'), content: SpecialSelectableText(certInfo.cert.details.name)),
ConfigItem( ConfigItem(
label: Text('Type'), label: Text('Type'), content: Text(certInfo.cert.details.isCa ? 'CA certificate' : 'Client certificate')),
content: Text(certInfo.cert.details.isCa ? 'CA certificate' : 'Client certificate')),
]); ]);
} }
@ -106,18 +106,17 @@ class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
Widget _buildFilters() { Widget _buildFilters() {
List<Widget> items = []; List<Widget> items = [];
if (certInfo.cert.details.groups.length > 0) { if (certInfo.cert.details.groups.length > 0) {
items.add(ConfigItem( items.add(
label: Text('Groups'), content: SpecialSelectableText(certInfo.cert.details.groups.join(', ')))); ConfigItem(label: Text('Groups'), content: SpecialSelectableText(certInfo.cert.details.groups.join(', '))));
} }
if (certInfo.cert.details.ips.length > 0) { if (certInfo.cert.details.ips.length > 0) {
items items.add(ConfigItem(label: Text('IPs'), content: SpecialSelectableText(certInfo.cert.details.ips.join(', '))));
.add(ConfigItem(label: Text('IPs'), content: SpecialSelectableText(certInfo.cert.details.ips.join(', '))));
} }
if (certInfo.cert.details.subnets.length > 0) { if (certInfo.cert.details.subnets.length > 0) {
items.add(ConfigItem( items.add(
label: Text('Subnets'), content: SpecialSelectableText(certInfo.cert.details.subnets.join(', ')))); ConfigItem(label: Text('Subnets'), content: SpecialSelectableText(certInfo.cert.details.subnets.join(', '))));
} }
return items.length > 0 return items.length > 0
@ -141,8 +140,8 @@ class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
certInfo.rawCert != null certInfo.rawCert != null
? ConfigItem( ? ConfigItem(
label: Text('PEM Format'), label: Text('PEM Format'),
content: SpecialSelectableText(certInfo.rawCert, content:
style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14)), SpecialSelectableText(certInfo.rawCert, style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14)),
crossAxisAlignment: CrossAxisAlignment.start) crossAxisAlignment: CrossAxisAlignment.start)
: Container(), : Container(),
], ],
@ -155,26 +154,26 @@ class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
} }
return Padding( return Padding(
padding: EdgeInsets.only(top: 50, bottom: 10, left: 10, right: 10), padding: EdgeInsets.only(top: 50, bottom: 10, left: 10, right: 10),
child: SizedBox( child: SizedBox(
width: double.infinity, width: double.infinity,
child: PlatformButton( child: PlatformButton(
child: Text('Replace certificate'), child: Text('Replace certificate'),
color: CupertinoColors.systemRed.resolveFrom(context), color: CupertinoColors.systemRed.resolveFrom(context),
onPressed: () { onPressed: () {
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
return AddCertificateScreen( return AddCertificateScreen(onReplace: (result) {
onReplace: (result) { setState(() {
setState(() { changed = true;
changed = true; certResult = result;
certResult = result; certInfo = certResult.certInfo;
certInfo = certResult.certInfo; });
// Slam the page back to the top
controller.animateTo(0,
duration: const Duration(milliseconds: 10), curve: Curves.linearToEaseOut);
}); });
// Slam the page back to the top
controller.animateTo(0, duration: const Duration(milliseconds: 10), curve: Curves.linearToEaseOut);
}); });
}); })));
})));
} }
Widget _buildDelete() { Widget _buildDelete() {

View File

@ -140,25 +140,23 @@ class _SiteConfigScreenState extends State<SiteConfigScreen> {
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
if (site.certInfo != null) { if (site.certInfo != null) {
return CertificateDetailsScreen( return CertificateDetailsScreen(
certInfo: site.certInfo, certInfo: site.certInfo,
onReplace: (result) { onReplace: (result) {
setState(() { setState(() {
changed = true; changed = true;
site.certInfo = result.certInfo; site.certInfo = result.certInfo;
site.key = result.key; site.key = result.key;
});
}); });
}
);
} }
return AddCertificateScreen( return AddCertificateScreen(onSave: (result) {
onSave: (result) { setState(() {
setState(() { changed = true;
changed = true; site.certInfo = result.certInfo;
site.certInfo = result.certInfo; site.key = result.key;
site.key = result.key; });
}); });
});
}); });
}, },
), ),

View File

@ -83,12 +83,14 @@ class _UnsafeRoutesScreenState extends State<UnsafeRoutesScreen> {
content: Text('Add a new route'), content: Text('Add a new route'),
onPressed: () { onPressed: () {
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
return UnsafeRouteScreen(route: UnsafeRoute(), onSave: (route) { return UnsafeRouteScreen(
setState(() { route: UnsafeRoute(),
changed = true; onSave: (route) {
unsafeRoutes[UniqueKey()] = route; setState(() {
}); changed = true;
}); unsafeRoutes[UniqueKey()] = route;
});
});
}); });
}, },
)); ));

View File

@ -44,8 +44,7 @@ class Share {
throw FlutterError('FilePath cannot be null'); throw FlutterError('FilePath cannot be null');
} }
final bool success = final bool success = await _channel.invokeMethod('shareFile', <String, dynamic>{
await _channel.invokeMethod('shareFile', <String, dynamic>{
'title': title, 'title': title,
'filePath': filePath, 'filePath': filePath,
'filename': filename, 'filename': filename,

View File

@ -7,15 +7,19 @@ bool ipValidator(String str, bool enableIPV6) {
} }
switch (ia.type) { switch (ia.type) {
case InternetAddressType.IPv6: { case InternetAddressType.IPv6:
if (enableIPV6) { {
if (enableIPV6) {
return true;
}
}
break;
case InternetAddressType.IPv4:
{
return true; return true;
} }
} break;
break;
case InternetAddressType.IPv4: { return true; }
break;
} }
return false; return false;