“This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!”

preface

The previous articles respectively introduced the acquisition, deletion and editing of background data by Dio. Related articles are as follows:

  • Introduction and Combat (xXII) : First meeting, Internet request king of dio.
  • Introduction to Flutter and Combat (XXIII) : Using Dio requests to delete data.
  • Introduction to Flutter and Combat (24) : Use Dio’s Patch request to complete detailed editing

This section describes how to create dynamic data using Post requests.

  • Actions on the right of the navigation bar
  • Route matching sequence: Routes defined first are matched first
  • Similarities and differences between adding and editing
  • A post request
  • Key stroke prevention

Added operation buttons to the navigation bar

It is quite common to add an action button to the upper right corner of the navigation bar. The AppBar component of the Flutter provides an Actions parameter that sets the action button in the upper right corner. The Actions is a List

which means that multiple components can be added. This example adds a button to go to the Add Dynamic page:

appBar: AppBar(
    title: Text('dynamic', style: Theme.of(context).textTheme.headline4),
    actions: [
      IconButton(
          icon: Icon(Icons.add),
          onPressed: () {
            RouterManager.router
                .navigateTo(context, RouterManager.dynamicAddPath);
          })
    ],
    brightness: Brightness.dark,
  ),
    
  / /...
Copy the code

RouterManager dynamicAddPath is routing path constants, add page for/dynamics/add. However, if we find that the routing rule matches the actual /dynamics/:id (dynamic details), we will jump to the details page instead of the add page. What should we do?

Fluro Route matching sequence

Fluro’s route match order is in the order in which the routes are defined, so you need to place the more specific routes before the range match, that is, define adding page routes before adding detail routes. This is actually similar to the React Router. When a match is made, it breaks the rule and no longer matches down. Therefore, when using Fluro, you need to be careful about the order in which you define your routes, or you may end up with incorrect route jumps.

Add a page

The add page form is the same as the edit page, except that there is no data read from the background to fill in the form content. Dart, rename it dynamic_add.dart, and replace DynamicEdit with DynamicAdd. Different from the edit page:

  1. _formData needs to be defined in advance, as shown below.
Map<String.Map<String.Object>> _formData = {
  'title': {
    'value': ' '.'controller': TextEditingController(),
    'obsecure': false,},'content': {
    'value': ' '.'controller': TextEditingController(),
    'obsecure': false,},'imageUrl': {
    'value': ' '.'controller': TextEditingController(),
    'obsecure': false,}};Copy the code
  1. _getFormWidgets builds a form component without having to return to the load prompt. It simply returns to the form.
  2. The network request is changed to Post request.
_handleSubmit() async {
  if ((_formData['title'] ['value'] as String).trim() == ' ') {
    Dialogs.showInfo(this.context, 'Title cannot be empty');
    return;
  }

  if ((_formData['content'] ['value'] as String).trim() == ' ') {
    Dialogs.showInfo(this.context, 'Content cannot be empty');
    return;
  }

  if ((_formData['imageUrl'] ['value'] as String).trim() == ' ') {
    Dialogs.showInfo(this.context, 'Image links cannot be empty');
    return;
  }

  try {
    Map<String.String> newFormData = {};
    _formData.forEach((key, value) {
      newFormData[key] = value['value'];
    });
    var response = await DynamicService.post(newFormData);
    if (response.statusCode == 200) {
      Dialogs.showInfo(context, 'Add successful');
    } else {
      Dialogs.showInfo(this.context, response.statusMessage); }}on DioError catch (e) {
    Dialogs.showInfo(this.context, e.message);
  } catch (e) {
    Dialogs.showInfo(this.context, e.toString()); }}Copy the code

We’ll see that many of the methods are similar, such as forms, form validation, and form data processing at edit time. Therefore, these common areas can be encapsulated, but it needs to consider that the form content added and edited by the actual business may be different, for example, some fields are not allowed to be edited, so considering the commonality, we do more general processing, extract a dynamic_form.dart class, and encapsulate the common parts in a unified way to improve the reusability.

class DynamicForm extends StatelessWidget {
  final Map<String.Map<String.Object>> formData;
  final Function handleTextFieldChanged;
  final ValueChanged<String> handleClear;
  final String buttonName;
  final Function handleSubmit;
  const DynamicForm(this.formData, this.handleTextFieldChanged,
      this.handleClear, this.buttonName, this.handleSubmit,
      {Key key})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(children: _getForm(context)),
    );
  }

  List<Widget> _getForm(BuildContext context) {
    List<Widget> widgets = [];
    formData.forEach((key, formParams) {
      widgets.add(FormUtil.textField(key, formParams['value'],
          controller: formParams['controller']????null,
          hintText: formParams['hintText']????' ',
          prefixIcon: formParams['icon'],
          onChanged: handleTextFieldChanged,
          onClear: handleClear));
    });

    widgets
        .add(ButtonUtil.primaryTextButton(buttonName, handleSubmit, context));

    returnwidgets; }}Copy the code

After wrapping, editing and adding the page’s _formData needs to add the fields that will build the TextField, instead of writing dead as before, which is more flexible. Also, the code will be more concise, such as adding a page.

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Add dynamic'),
      brightness: Brightness.dark,
    ),
    body: DynamicForm(
      _formData,
      _handleTextFieldChanged,
      _handleClear,
      'submit',
      _handleSubmit,
    ),
  );
}
Copy the code

The heavy submitted

During debugging, it was found that there would be multiple data saved when clicking the submit button. How does Flutter prevent resubmission?

One of the general anti-resubmission methods is to disable the button after clicking, and then enable the button after the network request result returns. Another way is to add a Loading mask to block the page before the network request ends, so as to avoid operating forms and buttons. In this case, we use the second method, using a mask to avoid manipulating the form and also to give the loading indication.

A plugin for Flutter_EasyLoading is available on pub, which can meet this requirement. This is done by passing the EasyLoading.init() method to the MatertialApp Builder parameter in Main.dart to initialize a global EasyLoading object, which can then be called at any time in the page. The showXXX method is called when it is displayed, and the dismiss method is called when it is disappeared. You can set various loading styles, and can also customize loading components and parameters. For details, see Flutter_easyLoading. We display EasyLoading before commit and remove EasyLoading after receiving data.

_handleSubmit() async {
  / /... Check the code
	EasyLoading.showInfo('Just a moment, please... ', maskType: EasyLoadingMaskType.black);
  / /... Network request code
  EasyLoading.dismiss();
}
Copy the code

conclusion

This article introduces an example of adding new data pages, while simplifying page structure and improving reusability by encapsulating common form components for duplicated pages that are edited and added. Considering the repeated clicks in the actual operation, flutter_easyLoading is also introduced to realize the effect of loading the mask layer. The source code has been submitted to: Network Chapter source code. Note that the runtime pulls the latest background code to run, in case the service cannot find the background and cannot load the data.