@@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
55import 'package:flutter_spinkit/flutter_spinkit.dart' ;
66import 'package:flutter_translate/flutter_translate.dart' ;
77import 'package:nextcloud_cookbook_flutter/src/blocs/recipe/recipe_bloc.dart' ;
8+ import 'package:nextcloud_cookbook_flutter/src/util/url_validator.dart' ;
89
910class RecipeImportForm extends StatefulWidget {
1011 final String importUrl;
@@ -17,6 +18,7 @@ class RecipeImportForm extends StatefulWidget {
1718
1819class _RecipeImportFormState extends State <RecipeImportForm > {
1920 late TextEditingController _importUrlController;
21+ final _formKey = GlobalKey <FormState >();
2022
2123 @override
2224 void initState () {
@@ -42,25 +44,61 @@ class _RecipeImportFormState extends State<RecipeImportForm> {
4244 return BlocBuilder <RecipeBloc , RecipeState >(
4345 builder: (BuildContext context, RecipeState state) {
4446 final enabled = state.status != RecipeStatus .updateInProgress;
47+
48+ void onSubmit () {
49+ if (_formKey.currentState! .validate () && enabled) {
50+ _formKey.currentState! .save ();
51+ }
52+ }
53+
54+ String ? validate (String ? value) {
55+ if (value == null || value.isEmpty) {
56+ return translate (
57+ 'login.server_url.validator.pattern' ,
58+ );
59+ }
60+
61+ if (! URLUtils .isValid (value)) {
62+ return translate (
63+ 'login.server_url.validator.pattern' ,
64+ );
65+ }
66+
67+ return null ;
68+ }
69+
70+ Future <void > pasteClipboard () async {
71+ final clipboard = await Clipboard .getData ('text/plain' );
72+ final text = clipboard? .text;
73+ if (text != null ) {
74+ _importUrlController.text = text;
75+ }
76+
77+ _formKey.currentState! .validate ();
78+ }
79+
4580 return SingleChildScrollView (
4681 child: Padding (
4782 padding: const EdgeInsets .all (10.0 ),
4883 child: Form (
84+ key: _formKey,
4985 child: Column (
5086 children: [
51- TextField (
87+ TextFormField (
5288 enabled: enabled,
5389 controller: _importUrlController,
90+ validator: validate,
91+ onSaved: (value) {
92+ BlocProvider .of <RecipeBloc >(context).add (
93+ RecipeImported (value! ),
94+ );
95+ },
96+ onEditingComplete: onSubmit,
5497 decoration: InputDecoration (
5598 hintText: translate ("recipe_import.field" ),
5699 suffixIcon: IconButton (
57100 tooltip: translate ("recipe_import.clipboard" ),
58- onPressed: () async {
59- final clipboard =
60- await Clipboard .getData ('text/plain' );
61- final text = clipboard? .text;
62- if (text != null ) _importUrlController.text = text;
63- },
101+ onPressed: pasteClipboard,
64102 icon: const Icon (Icons .content_copy_outlined),
65103 ),
66104 ),
@@ -69,13 +107,7 @@ class _RecipeImportFormState extends State<RecipeImportForm> {
69107 Center (
70108 child: enabled
71109 ? OutlinedButton .icon (
72- onPressed: () {
73- if (enabled) {
74- BlocProvider .of <RecipeBloc >(context).add (
75- RecipeImported (_importUrlController.text),
76- );
77- }
78- },
110+ onPressed: onSubmit,
79111 icon: const Icon (Icons .cloud_download_outlined),
80112 label: Text (translate ("recipe_import.button" )),
81113 style: OutlinedButton .styleFrom (
0 commit comments