Flutter 使用 Dio 的 Post 请求增加动态

简介: 本篇介绍了新增数据页面的示例,同时对于编辑和添加的页面重复部分通过封装共用的表单组件简化了页面结构和提高复用性。

前言

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

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

导航栏增加操作按钮

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

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 的时候需要注意定义路由的次序,否则可能会导致路由跳转不正确。

添加页面

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

  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 类,将通用的部分统一封装进去,提高复用性。

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 的字段补齐,而不是之前那样写死,这样更灵活。并且,代码将更为简洁,以添加页面为例。

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

防重提交

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

一般防重提交的处理方法一种是点击后禁用,等待网络请求结果返回后再启用按钮。另外一种方式就是增加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 来实现加载蒙层的效果。源码已提交至:网络章节源码。注意运行时拉取最新的后台代码运行,以免找不到后台服务加载不了数据。


欢迎关注个人公众号:岛上码农

相关文章
|
6月前
|
iOS开发 UED
Flutter 动态修改应用图标功能指南
探索Flutter中动态应用图标的实现方法,了解如何为用户提供独特体验,促进用户升级和应用内购买。
195 0
Flutter 动态修改应用图标功能指南
|
5天前
|
存储 缓存 Dart
Flutter&鸿蒙next 封装 Dio 网络请求详解:登录身份验证与免登录缓存
本文详细介绍了如何在 Flutter 中使用 Dio 封装网络请求,实现用户登录身份验证及免登录缓存功能。首先在 `pubspec.yaml` 中添加 Dio 和 `shared_preferences` 依赖,然后创建 `NetworkService` 类封装 Dio 的功能,包括请求拦截、响应拦截、Token 存储和登录请求。最后,通过一个登录界面示例展示了如何在实际应用中使用 `NetworkService` 进行身份验证。希望本文能帮助你在 Flutter 中更好地处理网络请求和用户认证。
120 1
|
5月前
|
存储 开发框架 JavaScript
深入探讨Flutter中动态UI构建的原理、方法以及数据驱动视图的实现技巧
【6月更文挑战第11天】Flutter是高效的跨平台移动开发框架,以其热重载、高性能渲染和丰富组件库著称。本文探讨了Flutter中动态UI构建原理与数据驱动视图的实现。动态UI基于Widget树模型,状态变化触发UI更新。状态管理是关键,Flutter提供StatefulWidget、Provider、Redux等方式。使用ListView等可滚动组件和StreamBuilder等流式组件实现数据驱动视图的自动更新。响应式布局确保UI在不同设备上的适应性。Flutter为开发者构建动态、用户友好的界面提供了强大支持。
95 2
|
4月前
|
JSON Dart API
Flutter dio http 封装指南说明
本文介绍了如何实现一个通用、可重构的 Dio 基础类,包括单例访问、日志记录、常见操作封装以及请求、输出、报错拦截等功能。
103 2
Flutter dio http 封装指南说明
|
3月前
|
存储 缓存 安全
Flutter Dio进阶:使用Flutter Dio拦截器实现高效的API请求管理和身份验证刷新
Flutter Dio进阶:使用Flutter Dio拦截器实现高效的API请求管理和身份验证刷新
272 0
|
6月前
|
开发框架 前端开发 JavaScript
【Flutter前端技术开发专栏】Flutter中的动态UI构建与数据驱动视图
【4月更文挑战第30天】Flutter是一款高效跨平台移动开发框架,以其热重载、高性能渲染和丰富组件库著称,简化了动态UI和数据驱动视图的实现。本文深入讨论了动态UI构建原理,包括基于Widget树模型的UI更新和状态管理,如使用StatefulWidget和数据流库(如Provider、Redux)。此外,文中还介绍了实现技巧,如使用ListView等可滚动组件、StreamBuilder进行数据流驱动的UI更新,以及应用响应式布局以适应不同设备。Flutter为开发者提供了构建高效动态界面的强大工具。
168 0
【Flutter前端技术开发专栏】Flutter中的动态UI构建与数据驱动视图
|
6月前
|
存储 缓存 开发框架
Flutter的网络请求:使用Dart进行HTTP请求的技术详解
【4月更文挑战第26天】了解Flutter网络请求,本文详述使用Dart进行HTTP请求
|
6月前
|
安全 网络安全 开发工具
对象存储oss使用问题之flutter使用http库进行post请求文件上传返回400如何解决
《对象存储OSS操作报错合集》精选了用户在使用阿里云对象存储服务(OSS)过程中出现的各种常见及疑难报错情况,包括但不限于权限问题、上传下载异常、Bucket配置错误、网络连接问题、跨域资源共享(CORS)设定错误、数据一致性问题以及API调用失败等场景。为用户降低故障排查时间,确保OSS服务的稳定运行与高效利用。
252 1
|
Kotlin
Flutter 动态表单Dynamic FormField架构设计
Flutter 动态表单Dynamic FormField架构设计
353 0
Flutter 动态表单Dynamic FormField架构设计