mobile_nebula/lib/services/utils.dart
Caleb Jasik 2b844d27dd
Add Flutter lint (#253)
* Enable `flutter_lints` linting

* Fix unmarked deps, we aren't on web so we don't need a URL strategy

* ` dart fix --apply --code=use_super_parameters`

* `dart fix --apply --code=use_key_in_widget_constructors`

* `dart fix --apply --code=use_function_type_syntax_for_parameters`

* Ignore code-generated `lib/services/theme.dart` file

* `dart fix --apply --code=unnecessary_this`

* `dart fix --apply --code=unnecessary_null_in_if_null_operators`

* `dart fix --apply --code=unnecessary_new`

* `dart fix --apply --code=sort_child_properties_last`

* `dart fix --apply --code=sized_box_for_whitespace`

* `dart fix --apply --code=prefer_typing_uninitialized_variables`

* `dart fix --apply --code=prefer_is_empty`

* `dart fix --apply --code=prefer_interpolation_to_compose_strings`

* `dart fix --apply --code=prefer_final_fields`

* `dart fix --apply --code=prefer_const_constructors_in_immutables`

* `dart fix --apply --code=prefer_collection_literals`

* `dart fix --apply --code=no_leading_underscores_for_local_identifiers`

* `dart fix --apply --code=curly_braces_in_flow_control_structures`

* `dart fix --apply --code=avoid_function_literals_in_foreach_calls`

* `dart fix --apply --code=annotate_overrides`

* Add CI for dart linting

* `dart format lib/`

* Re-enable the `usePathUrlStrategy` call, with proper deps

https://docs.flutter.dev/ui/navigation/url-strategies#configuring-the-url-strategy
2025-03-04 11:29:23 -06:00

203 lines
6.2 KiB
Dart

import 'dart:io';
import 'package:file_picker/file_picker.dart';
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_string.dart';
import 'package:google_fonts/google_fonts.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, {String singleSuffix = "item", String multiSuffix = "items"}) {
if (items == 1) {
return "$items $singleSuffix";
}
return "$items $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
static Widget leadingBackWidget(BuildContext context, {label = 'Back', Function? onPressed}) {
if (Platform.isIOS) {
return CupertinoNavigationBarBackButton(
previousPageTitle: label,
onPressed: () {
if (onPressed == null) {
Navigator.pop(context);
} else {
onPressed();
}
},
);
}
return IconButton(
padding: EdgeInsets.zero,
icon: Icon(context.platformIcons.back),
tooltip: label,
onPressed: () {
if (onPressed == null) {
Navigator.pop(context);
} else {
onPressed();
}
},
);
}
static Widget trailingSaveWidget(BuildContext context, Function onPressed) {
return PlatformTextButton(
padding: Platform.isAndroid ? null : EdgeInsets.zero,
onPressed: () => onPressed(),
child: Text('Save'),
);
}
/// 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();
},
),
],
);
},
);
}
static popError(BuildContext context, String title, String error, {StackTrace? stack}) {
if (stack != null) {
error += '\n${stack.toString()}';
}
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
if (Platform.isAndroid) {
return AlertDialog(
title: Text(title),
content: Text(error),
actions: <Widget>[
TextButton(
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 canLaunchUrlString(url)) {
await launchUrlString(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;
}
static Future<String?> 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!);
return file.readAsString();
}
static TextTheme createTextTheme(BuildContext context, String bodyFontString, String displayFontString) {
TextTheme baseTextTheme = Theme.of(context).textTheme;
TextTheme bodyTextTheme = GoogleFonts.getTextTheme(bodyFontString, baseTextTheme);
TextTheme displayTextTheme = GoogleFonts.getTextTheme(displayFontString, baseTextTheme);
TextTheme textTheme = displayTextTheme.copyWith(
bodyLarge: bodyTextTheme.bodyLarge,
bodyMedium: bodyTextTheme.bodyMedium,
bodySmall: bodyTextTheme.bodySmall,
labelLarge: bodyTextTheme.labelLarge,
labelMedium: bodyTextTheme.labelMedium,
labelSmall: bodyTextTheme.labelSmall,
);
return textTheme;
}
}