Flutter 入门与实战(二十五):使用 Post 请求增长动态

「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!前端

前言

以前几篇分别介绍了利用 Dio 完成后台数据的获取、删除和编辑,相关文章以下:git

本篇介绍如何使用 Post 请求建立动态数据,本篇相关知识点以下:后端

  • 导航栏右侧的 actions
  • 路由匹配前后顺序,优先匹配先定义的路由
  • 添加与编辑的异同
  • post 请求
  • 防重点击

导航栏增长操做按钮

在导航栏右上角增长操做按钮是十分常见的状况,Flutter 的 AppBar 组件提供了actions 参数,用于设置右上角的操做按钮,actions 是一个 List<Widget>,意味着能够添加多个组件。本例增长了一个图标按钮,用于进入添加动态页面:markdown

appBar: AppBar(
    title: Text('动态', style: Theme.of(context).textTheme.headline4),
    actions: [
      IconButton(
          icon: Icon(Icons.add),
          onPressed: () {
            RouterManager.router
                .navigateTo(context, RouterManager.dynamicAddPath);
          })
    ],
    brightness: Brightness.dark,
  ),
    
  //...
复制代码

RouterManager.dynamicAddPath是添加页面的路由路径常量,为 /dynamics/add。可是,咱们会发现这个路由规则和/dynamics/:id ,即动态详情的实际是能够匹配的,一不当心就跳到了详情页而不是添加页面,这个时候该怎么处理呢?网络

Fluro路由匹配的前后次序

Fluro 的路由匹配次序是按照定义路由的前后次序进行匹配的,所以须要把更具体的路由放置在范围匹配的前面,即定义添加页面路由时要放置在详情路由的前面。这点实际上和 React Router相似,匹配到了就跳出规则,再也不往下匹配。所以,在使用 Fluro 的时候须要注意定义路由的次序,不然可能会致使路由跳转不正确。app

添加页面

添加页面的表单和编辑页面同样,只是没有从后台读取数据填充表单内容。咱们先直接复制以前的 dynamic_edit.dart 文件,并重命名为 dynamic_add.dart,同时将DynamicEdit 替换为 DynamicAdd。与编辑页面的不一样之处在于:less

  1. _formData 须要提早定义,以下所示。
Map<String, Map<String, Object>> _formData = {
  'title': {
    'value': '',
    'controller': TextEditingController(),
    'obsecure': false,
  },
  'content': {
    'value': '',
    'controller': TextEditingController(),
    'obsecure': false,
  },
  'imageUrl': {
    'value': '',
    'controller': TextEditingController(),
    'obsecure': false,
  },
};
复制代码
  1. _getFormWidgets构建表单组件时无需返回加载提示,直接返回表单便可。
  2. 网络请求修改成 Post 请求。
_handleSubmit() async {
  if ((_formData['title']['value'] as String).trim() == '') {
    Dialogs.showInfo(this.context, '标题不能为空');
    return;
  }

  if ((_formData['content']['value'] as String).trim() == '') {
    Dialogs.showInfo(this.context, '内容不能为空');
    return;
  }

  if ((_formData['imageUrl']['value'] as String).trim() == '') {
    Dialogs.showInfo(this.context, '图片连接不能为空');
    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, '添加成功');
    } 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());
  }
}
复制代码

咱们会发现有不少方法是相似的,好比表单、表单校验以及编辑时表单数据处理。所以这些共同的地方能够进行封装,可是须要考虑实际业务添加和编辑的表单内容可能不一样,好比某些字段不容许编辑等,所以考虑共通性,咱们作更通用的处理,提取一个 dynamic_form.dart 类,将通用的部分统一封装进去,提升复用性。async

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));

    return widgets;
  }
}
复制代码

封装完以后,编辑和添加页面的_formData须要增长将构建 TextField 的字段补齐,而不是以前那样写死,这样更灵活。而且,代码将更为简洁,以添加页面为例。ide

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('添加动态'),
      brightness: Brightness.dark,
    ),
    body: DynamicForm(
      _formData,
      _handleTextFieldChanged,
      _handleClear,
      '提交',
      _handleSubmit,
    ),
  );
}
复制代码

防重提交

调试过程当中发现,点击提交按钮时保存的数据会有多条。Flutter 如何防重提交? image.pngoop

通常防重提交的处理方法一种是点击后禁用,等待网络请求结果返回后再启用按钮。另一种方式就是增长Loading蒙层,在网络请求没结束前使用蒙层将页面遮挡,从而避免操做表单及按钮。这里咱们采用第二种方式,经过蒙层的方式指示能够避免操做表单,也可以给出加载指示。

在 pub 上提供了一个 flutter_easyloading 的插件,能够知足这要求。具体使用是在main.dart的 MatertialApp 的 builder 参数传递EasyLoading.init()方法,初始化一个全局的EasyLoading对象,以后就能够在页面中随时调用了。显示的时候调用 showXXX 方法,消失的时候调用 dismiss 方法,能够设置多种 loading 样式,也支持自定义 loading 组件以及自定义参数,具体能够参考:flutter_easyloading。咱们在提交前显示EasyLoading,接收到数据后移除EasyLoading便可。

_handleSubmit() async {
  //...校验代码
	EasyLoading.showInfo('请稍候...', maskType: EasyLoadingMaskType.black);
  //...网络请求代码
  EasyLoading.dismiss();
}
复制代码

总结

本篇介绍了新增数据页面的示例,同时对于编辑和添加的页面重复部分经过封装共用的表单组件简化了页面结构和提升复用性。考虑实际操做的重复点击,还引入了 flutter_easyloading 来实现加载蒙层的效果。源码已提交至:网络章节源码。注意运行时拉取最新的后台代码运行,以避免找不到后台服务加载不了数据。

相关文章
相关标签/搜索