From b7236befd1246cf1ee24c19e98a0da7ba5dedad5 Mon Sep 17 00:00:00 2001 From: Christien Rioux Date: Mon, 24 Jul 2023 09:31:51 -0400 Subject: [PATCH] hud --- assets/i18n/en.json | 9 +-- lib/app.dart | 2 + lib/components/new_account_form.dart | 71 ----------------- lib/pages/new_account.dart | 113 +++++++++++++++++++++------ lib/tools/desktop_control.dart | 15 ++++ lib/tools/widget_helpers.dart | 18 +++++ pubspec.lock | 32 ++++++++ pubspec.yaml | 4 + 8 files changed, 164 insertions(+), 100 deletions(-) delete mode 100644 lib/components/new_account_form.dart diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 92947ba..4b46ec9 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -6,13 +6,10 @@ "settings_tooltip": "Settings" }, "new_account_page": { - "title": "Create a new account", + "titlebar": "Create a new account", "header": "Account Profile", - "import": "Import an existing account" - }, - "new_account_form": { - "name": "Name", - "title": "Title (optional)", + "form_name": "Name", + "form_title": "Title (optional)", "create": "Create", "instructions": "This information will be shared with the people you invite to connect with you on VeilidChat." }, diff --git a/lib/app.dart b/lib/app.dart index 67e9529..17f5c6a 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:animated_theme_switcher/animated_theme_switcher.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'router/router.dart'; import 'package:flutter_translate/flutter_translate.dart'; @@ -32,6 +33,7 @@ class VeilidChatApp extends ConsumerWidget { localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, + FormBuilderLocalizations.delegate, localizationDelegate ], supportedLocales: localizationDelegate.supportedLocales, diff --git a/lib/components/new_account_form.dart b/lib/components/new_account_form.dart deleted file mode 100644 index 6618a1d..0000000 --- a/lib/components/new_account_form.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:awesome_extensions/awesome_extensions.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_translate/flutter_translate.dart'; - -import '../components/default_app_bar.dart'; -import '../tools/desktop_control.dart'; - -class NewAccountForm extends ConsumerStatefulWidget { - const NewAccountForm({super.key}); - - @override - NewAccountFormState createState() { - return NewAccountFormState(); - } -} - -class NewAccountFormState extends ConsumerState { - final _formKey = GlobalKey(); - - @override - Widget build(BuildContext context) { - return Form( - key: _formKey, - child: Column( - children: [ - TextFormField( - key: const ValueKey("name"), - autofocus: true, - decoration: - InputDecoration(hintText: translate("new_account_form.name")), - maxLength: 64, - // The validator receives the text that the user has entered. - validator: (value) { - if (value == null || value.isEmpty) { - return 'Name is required'; - } - return null; - }, - ), - TextFormField( - key: const ValueKey("title"), - maxLength: 64, - decoration: - InputDecoration(hintText: translate("new_account_form.title")), - ), - Row(children: [ - const Spacer(), - Text(translate("new_account_form.instructions")) - .toCenter() - .flexible(flex: 4), - const Spacer(), - ]).paddingSymmetric(vertical: 24), - ElevatedButton( - onPressed: () { - // Validate returns true if the form is valid, or false otherwise. - if (_formKey.currentState!.validate()) { - // If the form is valid, display a snackbar. In the real world, - // you'd often call a server or save the information in a database. - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Processing Data')), - ); - } - }, - child: Text(translate('new_account_form.create')), - ).paddingSymmetric(vertical: 16).toCenter(), - ], - ), - ); - } -} diff --git a/lib/pages/new_account.dart b/lib/pages/new_account.dart index a67e582..f5a5dc5 100644 --- a/lib/pages/new_account.dart +++ b/lib/pages/new_account.dart @@ -1,37 +1,104 @@ +import 'dart:io'; + import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_translate/flutter_translate.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import '../components/default_app_bar.dart'; -import '../components/new_account_form.dart'; +import '../providers/local_accounts.dart'; import '../tools/tools.dart'; -class NewAccountPage extends ConsumerWidget { +class NewAccountPage extends ConsumerStatefulWidget { const NewAccountPage({super.key}); static const path = '/new_account'; @override - Widget build(BuildContext context, WidgetRef ref) { - enableTitleBar(true); - - return Scaffold( - resizeToAvoidBottomInset: false, - appBar: DefaultAppBar(context, - title: Text(translate("new_account_page.title"))), - body: Container( - padding: const EdgeInsets.all(24), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text(translate("new_account_page.header")) - .textStyle(context.headlineSmall) - .paddingSymmetric(vertical: 16), - const NewAccountForm().flexible(), - Text(translate("new_account_page.import")) - ], - ), - )); + NewAccountPageState createState() { + return NewAccountPageState(); + } +} + +class NewAccountPageState extends ConsumerState { + final _formKey = GlobalKey(); + late bool isInAsyncCall = false; + + Widget _newAccountForm(BuildContext context, + {required Future Function(GlobalKey) onSubmit}) { + return FormBuilder( + key: _formKey, + child: ListView( + children: [ + Text(translate("new_account_page.header")) + .textStyle(context.headlineSmall) + .paddingSymmetric(vertical: 16), + FormBuilderTextField( + autofocus: true, + name: 'name', + decoration: InputDecoration( + hintText: translate("new_account_page.form_name")), + maxLength: 64, + // The validator receives the text that the user has entered. + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required(), + ]), + ), + FormBuilderTextField( + name: 'title', + maxLength: 64, + decoration: InputDecoration( + hintText: translate("new_account_page.form_title")), + ), + Row(children: [ + const Spacer(), + Text(translate("new_account_page.instructions")) + .toCenter() + .flexible(flex: 6), + const Spacer(), + ]).paddingSymmetric(vertical: 4), + ElevatedButton( + onPressed: () async { + if (_formKey.currentState?.saveAndValidate() ?? false) { + setState(() { + isInAsyncCall = true; + }); + try { + await onSubmit(_formKey); + } finally { + setState(() { + isInAsyncCall = false; + }); + } + } + }, + child: Text(translate('new_account_page.create')), + ).paddingSymmetric(vertical: 4).alignAtCenterRight(), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + enableTitleBar(true); + portraitOnly(); + + final localAccounts = ref.watch(localAccountsProvider); + + return Scaffold( + // resizeToAvoidBottomInset: false, + appBar: DefaultAppBar(context, + title: Text(translate("new_account_page.titlebar"))), + body: _newAccountForm( + context, + onSubmit: (formKey) async { + debugPrint(_formKey.currentState?.value.toString()); + FocusScope.of(context).unfocus(); + await Future.delayed(Duration(seconds: 5)); + }, + ).paddingSymmetric(horizontal: 24, vertical: 8), + ).withModalHUD(context, isInAsyncCall); } } diff --git a/lib/tools/desktop_control.dart b/lib/tools/desktop_control.dart index 101f233..485d5f9 100644 --- a/lib/tools/desktop_control.dart +++ b/lib/tools/desktop_control.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:window_manager/window_manager.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; Future setupDesktopWindow() async { if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { @@ -30,3 +31,17 @@ void enableTitleBar(bool enabled) { } } } + +void portraitOnly() { + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + ]); +} + +void landscapeOnly() { + SystemChrome.setPreferredOrientations([ + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight, + ]); +} diff --git a/lib/tools/widget_helpers.dart b/lib/tools/widget_helpers.dart index f9424c9..bd32011 100644 --- a/lib/tools/widget_helpers.dart +++ b/lib/tools/widget_helpers.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:blurry_modal_progress_hud/blurry_modal_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; extension BorderExt on Widget { Container debugBorder() { @@ -7,3 +9,19 @@ extension BorderExt on Widget { child: this); } } + +extension ModalProgressExt on Widget { + BlurryModalProgressHUD withModalHUD(BuildContext context, bool isLoading) { + return BlurryModalProgressHUD( + inAsyncCall: isLoading, + blurEffectIntensity: 4, + progressIndicator: SpinKitFoldingCube( + color: Theme.of(context).highlightColor, + size: 90.0, + ), + dismissible: false, + opacity: 0.3, + color: Theme.of(context).shadowColor, + child: this); + } +} diff --git a/pubspec.lock b/pubspec.lock index 7c05011..b476f80 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -81,6 +81,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + blurry_modal_progress_hud: + dependency: "direct main" + description: + name: blurry_modal_progress_hud + sha256: "01027921b499fc65c97682b4b9d1c4dd2fe7cfb30d5be5fb3f6518d729dfd1e3" + url: "https://pub.dev" + source: hosted + version: "1.1.0" boolean_selector: dependency: transitive description: @@ -382,6 +390,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.3.1" + flutter_form_builder: + dependency: "direct main" + description: + name: flutter_form_builder + sha256: e828c11156d75fc668da1d8e7ddc2311ea8368f023fd28a7c7c126c8b903c5d6 + url: "https://pub.dev" + source: hosted + version: "9.1.0" flutter_hooks: dependency: "direct main" description: @@ -419,6 +435,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.6" + flutter_spinkit: + dependency: "direct main" + description: + name: flutter_spinkit + sha256: b39c753e909d4796906c5696a14daf33639a76e017136c8d82bf3e620ce5bb8e + url: "https://pub.dev" + source: hosted + version: "5.2.0" flutter_svg: dependency: "direct main" description: @@ -445,6 +469,14 @@ packages: description: flutter source: sdk version: "0.0.0" + form_builder_validators: + dependency: "direct main" + description: + name: form_builder_validators + sha256: e04998b1597d76a51da7f009ed3b2f12d4173f13e146e8744fd2453e8595a2c9 + url: "https://pub.dev" + source: hosted + version: "9.0.0" freezed: dependency: "direct dev" description: diff --git a/pubspec.yaml b/pubspec.yaml index d0e5b52..010aabe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,6 +45,10 @@ dependencies: circular_profile_avatar: ^2.0.5 badges: ^3.1.1 awesome_extensions: ^2.0.9 + flutter_form_builder: ^9.1.0 + form_builder_validators: ^9.0.0 + blurry_modal_progress_hud: ^1.1.0 + flutter_spinkit: ^5.2.0 dev_dependencies: flutter_test: