mirror of
https://github.com/DefinedNet/mobile_nebula.git
synced 2025-01-18 11:17:06 +00:00
Add DangerButton component (#219)
This pulls out a component that we can use for "dangerous" operations like deleting, and styles it in one place. It also starts to move us slowly towards Material 3, with the rounded corners on these buttons in Android. Android: |Before Light|Before Dark|After Light|After Dark| |---|---|---|---| |<img width="425" alt="Android Studio 2025-01-15 14 16 36" src="https://github.com/user-attachments/assets/4823e551-6a40-48dd-9bc1-3004699b90ea" />|<img width="417" alt="Android Studio 2025-01-15 14 16 47" src="https://github.com/user-attachments/assets/df5461fd-586e-47bb-99b9-0212e63f0454" />|<img width="413" alt="Android Studio 2025-01-15 14 15 59" src="https://github.com/user-attachments/assets/d88a6225-b71a-4886-8387-e35811a3a6ec" />|<img width="418" alt="Android Studio 2025-01-15 14 16 15" src="https://github.com/user-attachments/assets/d4f23b1c-7003-4a00-b865-4a123d8fe3e9" />| iOS: |Before Light|Before Dark|After Light|After Dark| |---|---|---|---| |<img width="437" alt="Simulator 2025-01-15 15 56 26" src="https://github.com/user-attachments/assets/87c4eed3-6d07-4858-8ad8-d8c011538154" />|<img width="445" alt="Simulator 2025-01-15 15 56 36" src="https://github.com/user-attachments/assets/9dc5b174-7bc7-48ec-a3c0-61633168c31a" />|<img width="439" alt="Simulator 2025-01-15 16 05 23" src="https://github.com/user-attachments/assets/31dc9ab6-8a3c-49c7-892d-627f16e2a8cd" />|<img width="444" alt="Simulator 2025-01-15 16 05 37" src="https://github.com/user-attachments/assets/979280d6-e1f4-4d57-a86a-10bb4def729a" />|
This commit is contained in:
parent
46f029a91d
commit
991837676a
7 changed files with 64 additions and 30 deletions
28
lib/components/DangerButton.dart
Normal file
28
lib/components/DangerButton.dart
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DangerButton extends StatelessWidget {
|
||||||
|
const DangerButton({Key? key, required this.child, this.onPressed}) : super(key: key);
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
final GestureTapCallback? onPressed;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
return FilledButton(
|
||||||
|
onPressed: onPressed,
|
||||||
|
child: child,
|
||||||
|
style: FilledButton.styleFrom(backgroundColor: Theme.of(context).colorScheme.error));
|
||||||
|
} else {
|
||||||
|
// Workaround for https://github.com/flutter/flutter/issues/161590
|
||||||
|
final themeData = CupertinoTheme.of(context);
|
||||||
|
return CupertinoTheme(
|
||||||
|
data: themeData.copyWith(primaryColor: CupertinoColors.white),
|
||||||
|
child: CupertinoButton(
|
||||||
|
child: child, onPressed: onPressed, color: CupertinoColors.systemRed.resolveFrom(context)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart' show CupertinoThemeData, DefaultCupertinoLocalizations;
|
import 'package:flutter/cupertino.dart' show CupertinoThemeData, DefaultCupertinoLocalizations, CupertinoColors;
|
||||||
import 'package:flutter/material.dart'
|
import 'package:flutter/material.dart'
|
||||||
show BottomSheetThemeData, Colors, DefaultMaterialLocalizations, ThemeData, ThemeMode;
|
show BottomSheetThemeData, ColorScheme, Colors, DefaultMaterialLocalizations, ThemeData, ThemeMode;
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
@ -87,8 +87,11 @@ class _AppState extends State<App> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ThemeData lightTheme = ThemeData(
|
final ThemeData lightTheme = ThemeData(
|
||||||
useMaterial3: false,
|
useMaterial3: false,
|
||||||
brightness: Brightness.light,
|
colorScheme: ColorScheme.fromSwatch(
|
||||||
primarySwatch: Colors.blueGrey,
|
brightness: Brightness.light,
|
||||||
|
primarySwatch: Colors.blueGrey,
|
||||||
|
errorColor: CupertinoColors.systemRed.resolveFrom(context),
|
||||||
|
),
|
||||||
primaryColor: Colors.blueGrey[900],
|
primaryColor: Colors.blueGrey[900],
|
||||||
fontFamily: 'PublicSans',
|
fontFamily: 'PublicSans',
|
||||||
//scaffoldBackgroundColor: Colors.grey[100],
|
//scaffoldBackgroundColor: Colors.grey[100],
|
||||||
|
@ -100,8 +103,11 @@ class _AppState extends State<App> {
|
||||||
|
|
||||||
final ThemeData darkTheme = ThemeData(
|
final ThemeData darkTheme = ThemeData(
|
||||||
useMaterial3: false,
|
useMaterial3: false,
|
||||||
brightness: Brightness.dark,
|
colorScheme: ColorScheme.fromSwatch(
|
||||||
primarySwatch: Colors.grey,
|
brightness: Brightness.dark,
|
||||||
|
primarySwatch: Colors.grey,
|
||||||
|
errorColor: CupertinoColors.systemRed.resolveFrom(context),
|
||||||
|
),
|
||||||
primaryColor: Colors.grey[900],
|
primaryColor: Colors.grey[900],
|
||||||
fontFamily: 'PublicSans',
|
fontFamily: 'PublicSans',
|
||||||
scaffoldBackgroundColor: Colors.grey[800],
|
scaffoldBackgroundColor: Colors.grey[800],
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||||
|
import 'package:mobile_nebula/components/DangerButton.dart';
|
||||||
import 'package:mobile_nebula/components/SimplePage.dart';
|
import 'package:mobile_nebula/components/SimplePage.dart';
|
||||||
import 'package:mobile_nebula/components/config/ConfigCheckboxItem.dart';
|
import 'package:mobile_nebula/components/config/ConfigCheckboxItem.dart';
|
||||||
import 'package:mobile_nebula/components/config/ConfigItem.dart';
|
import 'package:mobile_nebula/components/config/ConfigItem.dart';
|
||||||
|
@ -161,9 +162,8 @@ class _HostInfoScreenState extends State<HostInfoScreen> {
|
||||||
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: PlatformElevatedButton(
|
child: DangerButton(
|
||||||
child: Text('Close Tunnel'),
|
child: Text('Close Tunnel'),
|
||||||
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);
|
||||||
|
|
|
@ -17,6 +17,8 @@ import 'package:mobile_nebula/screens/siteConfig/SiteConfigScreen.dart';
|
||||||
import 'package:mobile_nebula/services/utils.dart';
|
import 'package:mobile_nebula/services/utils.dart';
|
||||||
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||||
|
|
||||||
|
import '../components/DangerButton.dart';
|
||||||
|
|
||||||
//TODO: If the site isn't active, don't respond to reloads on hostmaps
|
//TODO: If the site isn't active, don't respond to reloads on hostmaps
|
||||||
//TODO: ios is now the problem with connecting screwing our ability to query the hostmap (its a race)
|
//TODO: ios is now the problem with connecting screwing our ability to query the hostmap (its a race)
|
||||||
|
|
||||||
|
@ -256,14 +258,14 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
|
||||||
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: PlatformElevatedButton(
|
child: DangerButton(
|
||||||
child: Text('Delete'),
|
child: Text('Delete'),
|
||||||
color: CupertinoColors.systemRed.resolveFrom(context),
|
onPressed: () => Utils.confirmDelete(context, 'Delete Site?', () async {
|
||||||
onPressed: () => Utils.confirmDelete(context, 'Delete Site?', () async {
|
if (await _deleteSite()) {
|
||||||
if (await _deleteSite()) {
|
Navigator.of(context).pop();
|
||||||
Navigator.of(context).pop();
|
}
|
||||||
}
|
}),
|
||||||
}))));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
_listHostmap() async {
|
_listHostmap() async {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||||
|
import 'package:mobile_nebula/components/DangerButton.dart';
|
||||||
import 'package:mobile_nebula/components/FormPage.dart';
|
import 'package:mobile_nebula/components/FormPage.dart';
|
||||||
import 'package:mobile_nebula/components/config/ConfigItem.dart';
|
import 'package:mobile_nebula/components/config/ConfigItem.dart';
|
||||||
import 'package:mobile_nebula/components/config/ConfigSection.dart';
|
import 'package:mobile_nebula/components/config/ConfigSection.dart';
|
||||||
|
@ -164,9 +165,8 @@ class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
|
||||||
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: PlatformElevatedButton(
|
child: DangerButton(
|
||||||
child: Text('Replace certificate'),
|
child: Text('Replace certificate'),
|
||||||
color: CupertinoColors.systemRed.resolveFrom(context),
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Utils.openPage(context, (context) {
|
Utils.openPage(context, (context) {
|
||||||
return AddCertificateScreen(
|
return AddCertificateScreen(
|
||||||
|
@ -199,9 +199,8 @@ class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
|
||||||
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: PlatformElevatedButton(
|
child: DangerButton(
|
||||||
child: Text('Delete'),
|
child: Text('Delete'),
|
||||||
color: CupertinoColors.systemRed.resolveFrom(context),
|
|
||||||
onPressed: () => Utils.confirmDelete(context, title, () async {
|
onPressed: () => Utils.confirmDelete(context, title, () async {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
widget.onDelete!();
|
widget.onDelete!();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||||
|
import 'package:mobile_nebula/components/DangerButton.dart';
|
||||||
import 'package:mobile_nebula/components/FormPage.dart';
|
import 'package:mobile_nebula/components/FormPage.dart';
|
||||||
import 'package:mobile_nebula/components/IPAndPortFormField.dart';
|
import 'package:mobile_nebula/components/IPAndPortFormField.dart';
|
||||||
import 'package:mobile_nebula/components/IPFormField.dart';
|
import 'package:mobile_nebula/components/IPFormField.dart';
|
||||||
|
@ -119,14 +120,12 @@ class _StaticHostmapScreenState extends State<StaticHostmapScreen> {
|
||||||
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: PlatformElevatedButton(
|
child: DangerButton(
|
||||||
child: Text('Delete'),
|
child: Text('Delete'),
|
||||||
color: CupertinoColors.systemRed.resolveFrom(context),
|
onPressed: () => Utils.confirmDelete(context, 'Delete host map?', () {
|
||||||
onPressed: () => Utils.confirmDelete(context, 'Delete host map?', () {
|
Navigator.of(context).pop();
|
||||||
Navigator.of(context).pop();
|
widget.onDelete!();
|
||||||
widget.onDelete!();
|
}))))
|
||||||
}),
|
|
||||||
)))
|
|
||||||
: Container()
|
: Container()
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||||
import 'package:mobile_nebula/components/CIDRFormField.dart';
|
import 'package:mobile_nebula/components/CIDRFormField.dart';
|
||||||
|
import 'package:mobile_nebula/components/DangerButton.dart';
|
||||||
import 'package:mobile_nebula/components/FormPage.dart';
|
import 'package:mobile_nebula/components/FormPage.dart';
|
||||||
import 'package:mobile_nebula/components/IPFormField.dart';
|
import 'package:mobile_nebula/components/IPFormField.dart';
|
||||||
import 'package:mobile_nebula/components/config/ConfigItem.dart';
|
import 'package:mobile_nebula/components/config/ConfigItem.dart';
|
||||||
|
@ -96,9 +97,8 @@ class _UnsafeRouteScreenState extends State<UnsafeRouteScreen> {
|
||||||
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: PlatformElevatedButton(
|
child: DangerButton(
|
||||||
child: Text('Delete'),
|
child: Text('Delete'),
|
||||||
color: CupertinoColors.systemRed.resolveFrom(context),
|
|
||||||
onPressed: () => Utils.confirmDelete(context, 'Delete unsafe route?', () {
|
onPressed: () => Utils.confirmDelete(context, 'Delete unsafe route?', () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
widget.onDelete!();
|
widget.onDelete!();
|
||||||
|
|
Loading…
Reference in a new issue