Flutter 入門與實戰(二十四):使用 Dio的 Patch請求完成詳情編輯

島上碼農發表於2021-07-05

前言

這是 Dio 網路請求的第三篇,我們將從淺到深完成 Dio 的學習,前面兩篇如下:

本篇介紹表單更新請求,利用 Patch 請求更新動態資料,需要做得準備工作如下:

  • 拉取後臺新的程式碼,專案地址:後臺原始碼,拉到本地後,在專案目錄執行 node seed.js 生成測試資料。
  • 執行後臺應用:在專案目錄node index.js 即可執行後臺介面應用,專案預設介面地址為:http://localhost:3900/api/

整理程式碼

回顧一下上一篇的程式碼,發現上一篇的提醒錯誤程式碼和業務無關,可以抽離為一個公共的方法,方便後面在其他地方呼叫,新建一個 utils/dialogs.dart 檔案,將錯誤提示方法移到裡面:

import 'package:flutter/material.dart';

class Dialogs {
  static void showInfo(BuildContext context, String info) {
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
      content: Text(info),
    ));
  }
}

複製程式碼

之前我們在列表是在 initialState 方法裡主動重新整理請求資料的,實際上 EasyRefresh 本身提供了一個屬性firstRefresh來設定首次是否自動重新整理,因此我們可以移除之前的主動重新整理程式碼,將 firstRefresh 設定為 true 即可。

編輯頁面實現

首先新增一個 dynamic_edit.dart 檔案,裡面有三個表單和一個按鈕,分別對應的是標題、內容和圖片連結地址(這裡我們暫時不考慮圖片上傳)。表單利用的是我們之前封裝的通用表單元件,可以參考之前的:Flutter 入門與實戰(十):封裝一個通用的文字輸入框。這裡在沒有請求到資料的時候我們顯示“載入中...”,若請求成功則顯示實際的表單內容,構建介面的程式碼如下:

_getFormWidgets() {
  if (_formData == null)
    return Center(
      child: Text('載入中...'),
    );
  return Container(
    child: Column(
      children: [
        FormUtil.textField(
          'title',
          _formData['title']['value'] ?? '',
          controller: _formData['title']['controller'] ?? null,
          hintText: '請輸入標題',
          prefixIcon: Icons.title,
          onChanged: _handleTextFieldChanged,
          onClear: _handleClear,
        ),
        FormUtil.textField(
          'content',
          _formData['content']['value'] ?? '',
          controller: _formData['content']['controller'] ?? null,
          hintText: '請輸入內容',
          prefixIcon: Icons.content_paste,
          onChanged: _handleTextFieldChanged,
          onClear: _handleClear,
        ),
        FormUtil.textField(
          'imageUrl',
          _formData['imageUrl']['value'] ?? '',
          controller: _formData['imageUrl']['controller'] ?? null,
          hintText: '請輸入圖片連結',
          prefixIcon: Icons.image,
          onChanged: _handleTextFieldChanged,
          onClear: _handleClear,
        ),
        ButtonUtil.primaryTextButton('儲存', () {
          _handleSubmit();
        }, context),
      ],
    ),
  );
}
複製程式碼

在列表項的長按彈層中我們增加了一個編輯按鈕,點選後跳轉到編輯頁面,具體參考原始碼即可。

獲取詳情資料

在編輯前,我們要通過 id 請求後臺資料來填充表單,這裡使用之前講過的 get 請求,方法如下。若請求成功,返回狀態碼200時,構建表單的_formData資料,也就是表單所需要的資料,同時更新介面。如果請求失敗則呼叫封裝的資訊提示方法,顯示錯誤資訊。

void _getDynamic(String id) async {
    try {
      var response = await DynamicService.get(id);
      if (response.statusCode == 200) {
        dynamicEntity = DynamicEntity.fromJson(response.data);
        setState(() {
          _formData = {
            'title': {
              'value': dynamicEntity.title,
              'controller': TextEditingController(text: dynamicEntity.title),
              'obsecure': false,
            },
            'content': {
              'value': dynamicEntity.content,
              'controller': TextEditingController(text: dynamicEntity.content),
              'obsecure': false,
            },
            'imageUrl': {
              'value': dynamicEntity.imageUrl,
              'controller': TextEditingController(text: dynamicEntity.imageUrl),
              'obsecure': false,
            },
          };
        });
      } 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());
    }
  }

複製程式碼

提交更新資料

在提交資料前我們需要做校驗,這裡為了簡單起見,只是保證資料不為空即可,後續我們可以校驗工具來對各個欄位進行準確的校驗。校驗通過後,從_fromData中提取實際要提交的表單資料。

_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.update(dynamicEntity.id, 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());
  }
}
複製程式碼

實際可以發現,我們的網路請求除了請求方法不同以外,其他程式碼幾乎如出一轍,這也是後面的篇章需要統一封裝的原因,要不太多重複程式碼了,萬一哪天改起來會很痛苦。DynamicService.update接收要更新的資料的 id 和要更新的表單資料,實際就是簡單呼叫了 Diopatch 方法。

static Future update(String id, Map<String, dynamic> data) async {
  var result = await Dio().patch(host + 'dynamics/' + id, data: data);

  return result;
}
複製程式碼

執行效果

編輯完成後,我們重新重新整理資料,可以看到內容和圖片都發生了改變(如何儲存成功後自動同步列表,我們講完新增後再一起來解決)。

螢幕錄製2021-07-05 下午9.59.35.gif

(錄屏有點尷尬,貼上圖片連結半天沒貼上上?)

總結

本篇介紹了詳情資料的獲取,實體物件的部分修改來展示 Dio的 patch 請求。可以看到,Dio 提供的一系列 Restful 請求的方式基本相同,這樣給我們統一封裝留下了空間。下一篇我們介紹如何建立資料以及操作成功後如何同步更新後的資料到列表,網路相關程式碼原始碼地址為:gitee.com/island-code…

相關文章