2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-04-28 13:36:50 +00:00

Refactor modal form into a new stateful widget

This commit is contained in:
Oliver 2021-07-22 17:49:43 +10:00
parent 885df45138
commit 64aed4b31a
3 changed files with 126 additions and 88 deletions

View File

@ -1,3 +1,4 @@
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:inventree/api.dart'; import 'package:inventree/api.dart';
import 'package:inventree/widget/dialogs.dart'; import 'package:inventree/widget/dialogs.dart';
import 'package:inventree/widget/fields.dart'; import 'package:inventree/widget/fields.dart';
@ -139,7 +140,7 @@ Map<String, dynamic> extractFields(APIResponse response) {
* @param method is the HTTP method to use to send the form data to the server (e.g. POST / PATCH) * @param method is the HTTP method to use to send the form data to the server (e.g. POST / PATCH)
*/ */
Future<void> launchApiForm(String title, String url, Map<String, dynamic> fields, {Map<String, dynamic> modelData = const {}, String method = "PATCH", Function? onSuccess, Function? onCancel}) async { Future<void> launchApiForm(BuildContext context, String title, String url, Map<String, dynamic> fields, {Map<String, dynamic> modelData = const {}, String method = "PATCH", Function? onSuccess, Function? onCancel}) async {
var options = await InvenTreeAPI().options(url); var options = await InvenTreeAPI().options(url);
@ -195,136 +196,169 @@ Future<void> launchApiForm(String title, String url, Map<String, dynamic> fields
formFields.add(APIFormField(fieldName, remoteField)); formFields.add(APIFormField(fieldName, remoteField));
} }
List<Widget> buildWidgets() { // Now, launch a new widget!
Navigator.push(
context,
MaterialPageRoute(builder: (context) => APIFormWidget(title, url, formFields))
);
}
class APIFormWidget extends StatefulWidget {
//! Form title to display
final String title;
//! API URL
final String url;
final List<APIFormField> fields;
APIFormWidget(this.title, this.url, this.fields, {Key? key}) : super(key: key);
@override
_APIFormWidgetState createState() => _APIFormWidgetState(title, url, fields);
}
class _APIFormWidgetState extends State<APIFormWidget> {
final _formKey = new GlobalKey<FormState>();
String title;
String url;
List<APIFormField> fields;
_APIFormWidgetState(this.title, this.url, this.fields) : super();
List<Widget> _buildForm() {
List<Widget> widgets = []; List<Widget> widgets = [];
for (var ff in formFields) { for (var field in fields) {
if (ff.hidden) { if (field.hidden) {
continue; continue;
} }
widgets.add(ff.constructField()); widgets.add(field.constructField());
if (ff.hasErrors()) { if (field.hasErrors()) {
for (String error in ff.errorMessages()) { for (String error in field.errorMessages()) {
widgets.add( widgets.add(
ListTile( ListTile(
title: Text( title: Text(
error, error,
style: TextStyle(color: Color.fromRGBO(250, 50, 50, 1)) style: TextStyle(color: Color.fromRGBO(250, 50, 50, 1)),
),
) )
)
); );
} }
} }
} }
// TODO: Add a "Save" button
// widgets.add(Spacer());
/*
widgets.add(
TextButton(
child: Text(
L10().save
),
onPressed: null,
)
);
*/
return widgets; return widgets;
} }
Future<void> _save(BuildContext context) async {
List<Widget> _widgets = buildWidgets();
void sendRequest(BuildContext context) async {
// Package up the form data // Package up the form data
Map<String, String> formData = {}; Map<String, String> _data = {};
for (var field in formFields) { for (var field in fields) {
formData[field.name] = field.value.toString(); _data[field.name] = field.value.toString();
} }
var response = await InvenTreeAPI().patch( // TODO: Handle "POST" forms too!!
final response = await InvenTreeAPI().patch(
url, url,
body: formData, body: _data,
); );
if (!response.isValid()) { if (!response.isValid()) {
// TODO - Display an error message here... // TODO: Display an error message!
return; return;
} }
switch (response.statusCode) { switch (response.statusCode) {
case 200: case 200:
case 201: case 201:
// Form was validated by the server // Form was successfully validated by the server
// Hide this form
Navigator.pop(context); Navigator.pop(context);
if (onSuccess != null) { // TODO: Display a snackBar
onSuccess();
}
break; // TODO: Run custom onSuccess function
return;
case 400: case 400:
// Form submission / validation error
// Update field errors // Update field errors
for (var field in formFields) { for (var field in fields) {
field.data['errors'] = response.data[field.name]; field.data['errors'] = response.data[field.name];
if (field.hasErrors()) {
print("Field '${field.name}' has errors:");
for (String error in field.errorMessages()) {
print(" - ${error}");
}
}
} }
break; break;
// TODO: Other status codes?
} }
setState(() {
// Refresh the form
});
} }
@override
Widget build(BuildContext context) {
OneContext().showDialog( return Scaffold(
builder: (BuildContext context) { appBar: AppBar(
return StatefulBuilder( title: Text(title),
builder: (context, setState) { actions: [
return AlertDialog( IconButton(
title: Text(title), icon: FaIcon(FontAwesomeIcons.save),
actions: <Widget>[ onPressed: () {
// Cancel button
TextButton(
child: Text(L10().cancel),
onPressed: () {
Navigator.pop(context);
if (onCancel != null) { if (_formKey.currentState!.validate()) {
onCancel(); _formKey.currentState!.save();
}
},
),
// Save button
TextButton(
child: Text(L10().save),
onPressed: () {
// Validate the form
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
setState(() { _save(context);
sendRequest(context); }
_widgets = buildWidgets(); },
}); )
} ]
}, ),
) body: Form(
], key: _formKey,
content: Form( child: SingleChildScrollView(
key: _formKey, child: Column(
child: SingleChildScrollView( mainAxisAlignment: MainAxisAlignment.start,
child: Column( mainAxisSize: MainAxisSize.min,
mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, children: _buildForm(),
crossAxisAlignment: CrossAxisAlignment.start, ),
children: _widgets, padding: EdgeInsets.all(16),
) )
) )
) );
);
} }
);
}
);
} }

View File

@ -19,6 +19,7 @@ import 'package:inventree/widget/spinner.dart';
import 'package:inventree/widget/drawer.dart'; import 'package:inventree/widget/drawer.dart';
class InvenTreeHomePage extends StatefulWidget { class InvenTreeHomePage extends StatefulWidget {
InvenTreeHomePage({Key? key}) : super(key: key); InvenTreeHomePage({Key? key}) : super(key: key);
@override @override

View File

@ -60,7 +60,9 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
IconButton( IconButton(
icon: FaIcon(FontAwesomeIcons.edit), icon: FaIcon(FontAwesomeIcons.edit),
tooltip: L10().edit, tooltip: L10().edit,
onPressed: _editPartDialog, onPressed: () {
_editPartDialog(context);
},
) )
); );
} }
@ -170,7 +172,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
); );
} }
void _editPartDialog() { void _editPartDialog(BuildContext context) {
// Values which can be edited // Values which can be edited
var _name; var _name;
@ -180,6 +182,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
var _link; var _link;
launchApiForm( launchApiForm(
context,
L10().editPart, L10().editPart,
part.url, part.url,
{ {