2020-07-27 20:43:58 +00:00
|
|
|
import 'dart:io';
|
|
|
|
|
2021-04-23 17:33:28 +00:00
|
|
|
import 'package:file_picker/file_picker.dart';
|
2020-07-27 20:43:58 +00:00
|
|
|
import 'package:flutter/cupertino.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
|
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
|
|
|
|
|
|
class Utils {
|
|
|
|
/// Minimum size (width or height) of a interactive component
|
|
|
|
static const double minInteractiveSize = 44;
|
|
|
|
|
|
|
|
/// The background color for a page, this is the furthest back color
|
|
|
|
static Color pageBackground(BuildContext context) {
|
|
|
|
return CupertinoColors.systemGroupedBackground.resolveFrom(context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The background color for a config item
|
|
|
|
static Color configItemBackground(BuildContext context) {
|
|
|
|
return CupertinoColors.secondarySystemGroupedBackground.resolveFrom(context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The top and bottom border color of a config section
|
|
|
|
static Color configSectionBorder(BuildContext context) {
|
|
|
|
return CupertinoColors.secondarySystemFill.resolveFrom(context);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Size textSize(String text, TextStyle style) {
|
|
|
|
final TextPainter textPainter =
|
|
|
|
TextPainter(text: TextSpan(text: text, style: style), maxLines: 1, textDirection: TextDirection.ltr)
|
|
|
|
..layout(minWidth: 0, maxWidth: double.infinity);
|
|
|
|
return textPainter.size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static openPage(BuildContext context, WidgetBuilder pageToDisplayBuilder) {
|
|
|
|
Navigator.push(
|
|
|
|
context,
|
|
|
|
platformPageRoute(
|
|
|
|
context: context,
|
|
|
|
builder: pageToDisplayBuilder,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
static String itemCountFormat(int items, {singleSuffix = "item", multiSuffix = "items"}) {
|
|
|
|
if (items == 1) {
|
|
|
|
return items.toString() + " " + singleSuffix;
|
|
|
|
}
|
|
|
|
|
|
|
|
return items.toString() + " " + multiSuffix;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Builds a simple leading widget that pops the current screen.
|
|
|
|
/// Provide your own onPressed to override that behavior, just remember you have to pop
|
2022-09-21 20:27:35 +00:00
|
|
|
static Widget leadingBackWidget(BuildContext context, {label = 'Back', Function? onPressed}) {
|
2021-06-08 19:41:15 +00:00
|
|
|
if (Platform.isIOS) {
|
|
|
|
return CupertinoButton(
|
|
|
|
child: Row(children: <Widget>[Icon(context.platformIcons.back), Text(label)]),
|
2020-07-27 20:43:58 +00:00
|
|
|
padding: EdgeInsets.zero,
|
|
|
|
onPressed: () {
|
|
|
|
if (onPressed == null) {
|
|
|
|
Navigator.pop(context);
|
|
|
|
} else {
|
|
|
|
onPressed();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-06-08 19:41:15 +00:00
|
|
|
return IconButton(
|
2020-07-27 20:43:58 +00:00
|
|
|
padding: EdgeInsets.zero,
|
2021-06-08 19:41:15 +00:00
|
|
|
icon: Icon(context.platformIcons.back),
|
|
|
|
tooltip: label,
|
2020-07-27 20:43:58 +00:00
|
|
|
onPressed: () {
|
|
|
|
if (onPressed == null) {
|
|
|
|
Navigator.pop(context);
|
|
|
|
} else {
|
|
|
|
onPressed();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Widget trailingSaveWidget(BuildContext context, Function onPressed) {
|
|
|
|
return CupertinoButton(
|
2022-09-21 20:27:35 +00:00
|
|
|
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)),
|
2020-07-27 20:43:58 +00:00
|
|
|
padding: Platform.isAndroid ? null : EdgeInsets.zero,
|
|
|
|
onPressed: () => onPressed());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Simple cross platform delete confirmation dialog - can also be used to confirm throwing away a change by swapping the deleteLabel
|
|
|
|
static confirmDelete(BuildContext context, String title, Function onConfirm,
|
|
|
|
{String deleteLabel = 'Delete', String cancelLabel = 'Cancel'}) {
|
|
|
|
showDialog<void>(
|
|
|
|
context: context,
|
|
|
|
barrierDismissible: false,
|
|
|
|
builder: (context) {
|
|
|
|
return PlatformAlertDialog(
|
|
|
|
title: Text(title),
|
|
|
|
actions: <Widget>[
|
|
|
|
PlatformDialogAction(
|
|
|
|
child: Text(deleteLabel,
|
|
|
|
style:
|
|
|
|
TextStyle(fontWeight: FontWeight.bold, color: CupertinoColors.systemRed.resolveFrom(context))),
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.pop(context);
|
|
|
|
onConfirm();
|
|
|
|
},
|
|
|
|
),
|
|
|
|
PlatformDialogAction(
|
|
|
|
child: Text(cancelLabel),
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
},
|
|
|
|
)
|
|
|
|
],
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-09-21 20:27:35 +00:00
|
|
|
static popError(BuildContext context, String title, String error, {StackTrace? stack}) {
|
2021-06-08 19:41:15 +00:00
|
|
|
if (stack != null) {
|
|
|
|
error += '\n${stack.toString()}';
|
|
|
|
}
|
|
|
|
|
2020-07-27 20:43:58 +00:00
|
|
|
showDialog(
|
|
|
|
context: context,
|
|
|
|
barrierDismissible: false,
|
|
|
|
builder: (context) {
|
|
|
|
if (Platform.isAndroid) {
|
|
|
|
return AlertDialog(title: Text(title), content: Text(error), actions: <Widget>[
|
2021-04-23 17:33:28 +00:00
|
|
|
TextButton(
|
2020-07-27 20:43:58 +00:00
|
|
|
child: Text('Ok'),
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
},
|
|
|
|
)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return CupertinoAlertDialog(
|
|
|
|
title: Text(title),
|
|
|
|
content: Text(error),
|
|
|
|
actions: <Widget>[
|
|
|
|
CupertinoDialogAction(
|
|
|
|
child: Text('Ok'),
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
},
|
|
|
|
)
|
|
|
|
],
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static launchUrl(String url, BuildContext context) async {
|
|
|
|
if (await canLaunch(url)) {
|
|
|
|
await launch(url);
|
|
|
|
} else {
|
|
|
|
Utils.popError(context, 'Error', 'Could not launch web view');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ip2int(String ip) {
|
|
|
|
final parts = ip.split('.');
|
|
|
|
return int.parse(parts[3]) | int.parse(parts[2]) << 8 | int.parse(parts[1]) << 16 | int.parse(parts[0]) << 24;
|
|
|
|
}
|
2021-04-23 17:33:28 +00:00
|
|
|
|
2022-09-21 20:27:35 +00:00
|
|
|
static Future<String?> pickFile(BuildContext context) async {
|
2021-04-23 17:33:28 +00:00
|
|
|
await FilePicker.platform.clearTemporaryFiles();
|
|
|
|
final result = await FilePicker.platform.pickFiles(allowMultiple: false);
|
|
|
|
if (result == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2022-11-17 21:43:16 +00:00
|
|
|
final file = File(result.files.first.path!);
|
2021-04-23 17:33:28 +00:00
|
|
|
return file.readAsString();
|
|
|
|
}
|
2020-07-27 20:43:58 +00:00
|
|
|
}
|