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 來實現加載蒙層的效果。源碼已提交至:網絡章節源碼。注意運行時拉取最新的後臺代碼運行,以避免找不到後臺服務加載不了數據。

相關文章
相關標籤/搜索