将一个 Flutter 项目转为用 ArkUI-X 框架实现(4)

91 阅读3分钟

Flutter → ArkUI-X 页面转换指南(4)

本部分主要介绍

  • 网络请求
  • 数据持久化/状态管理

六、网络请求

Flutter 中使用插件 dio: ^5.8.0, ArkUI-X 中使用插件 @ohos/axios": ^2.2.12

1. 添加网络权限

文件:/entry/src/main/module.json5

{
  "module": {
    "name": "entry",
    ...
    // 以下是添加网络权限的具体代码
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
 
  }
}

2. 封装网络请求

文件:/entry/src/main/ets/network/HttpManager.ets

import axios, { InternalAxiosRequestConfig, AxiosResponse, AxiosError, AxiosInstance } from '@ohos/axios';
import { promptAction } from '@kit.ArkUI';

/* ------------------  拦截器  ------------------ */

/**
 * 请求拦截器:
 */
// 请求成功
function requestSuccessHandler(config: InternalAxiosRequestConfig) {
  // 对请求数据做点什么
  const token = 'token....'
  if (token) {
    config.headers['Authorization'] = 'Bearer ' + token //设置token
  }
  return config
}

// 请求失败
function requestFailHandler(error: AxiosError) {
  // 对请求错误做些什么
  return Promise.reject(error)
}

/**
 * 响应拦截器:
 */
// 响应成功
function responseSuccessHandler(response: AxiosResponse) {
  // 响应码 200 的处理
  return response;
}

// 响应失败
function responseFailHandler(error: AxiosError) {
  // 响应码 非200 的处理
  return Promise.reject(error)
}


/* ------------------  请求管理类  ------------------ */

// 创建实例
const instance: AxiosInstance = axios.create({
  baseURL: 'http://localhost:8088/', // 请求地址
  timeout: 300000, // 响应超时时间
  headers: {
    'Content-Type': 'application/json;charset=utf-8',
    'Content-Language': 'en',
  },
});

// 添加拦截器
instance.interceptors.request.use(requestSuccessHandler, requestFailHandler);
instance.interceptors.response.use(responseSuccessHandler, responseFailHandler);

// 封装请求方法
class HttpManager {
  get<T>(url: string, params?: object): Promise<AxiosResponse<T>> {
    return instance.get<T, AxiosResponse<T>>(url, { params })
  }

  // 封装post方法
  post<T>(url: string, data?: object, config?: object): Promise<AxiosResponse<T>> {
    return instance.post<T, AxiosResponse<T>>(url, data, config)
  }

  // 封装put方法
  put<T>(url: string, data?: object, config?: object): Promise<AxiosResponse<T>> {
    return instance.put<T, AxiosResponse<T>>(url, data, config)
  }

  // 封装delete方法
  delete<T>(url: string, data?: object): Promise<AxiosResponse<T>> {
    return instance.delete<T, AxiosResponse<T>>(url, data)
  }
}

export const httpManager = new HttpManager();

七、数据持久化/状态管理

参考文章:使用 Preferences 实现数据持久化

1. 应用数据持久化 Preferences

Flutter 中使用插件 shared_preferences: ^2.5.2, ArkUI-X 中使用原生的 Preferences

  • 用户首选项(Preferences)为应用提供Key-Value键值型的轻量级数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。
  • Key 键为 string 类型,要求非空且长度不超过1024个字节。
  • 如果 Value 值为 string 类型,请使用UTF-8编码格式,可以为空。
  • 数据持久化需要在调用 preferences.put(key, value) 后调用 preferences.flush() 接口以实现数据持久化。若不调用 flush(),数据可能仅保存在内存中而不会写入持久化存储,导致应用退出后数据丢失。
封装数据持久化类 PreferencesManager
import { preferences } from '@kit.ArkData';

// 存储键名常量
export class PreferKeys {
  static readonly USER_NAME = "username";
  static readonly TOKEN = "token";
}

/**
 * 应用数据持久化类
 */
class PreferencesManager {

  // 首选项的实例
  private dataPreferences: preferences.Preferences | null = null;
  // 管理类的单例
  private static instance: PreferencesManager;

  // 获取单例
  static getInstance(): PreferencesManager {
    if (!PreferencesManager.instance) {
      PreferencesManager.instance = new PreferencesManager();
    }
    return PreferencesManager.instance;
  }

  // 初始化:必须在应用启动时 (EntryAbility 中) 调用一次
  init(entryAbilityContext: Context) {
    // 应用数据持久化
    let options: preferences.Options = { name: 'myStore' };
    this.dataPreferences = preferences.getPreferencesSync(entryAbilityContext, options);
  }

  // 统一存储 (Put)
  put(key: string, value: string | number | boolean) {
    if (!this.dataPreferences) throw new Error('Preferences not initialized');
    this.dataPreferences.putSync(key, value);
    this.dataPreferences.flush(); // 确保写入磁盘
  }
  
  // 统一读取 (Get)
  get(key: string, defaultValue: string | number | boolean) {
    if (!this.dataPreferences) return defaultValue;
    return (this.dataPreferences.getSync(key, defaultValue));
  }

  // 统一删除 (Delete)
  delete(key: string) {
    if (!this.dataPreferences) throw new Error('Preferences not initialized');
    this.dataPreferences.deleteSync(key);
    this.dataPreferences.flush();
  }

}

export default PreferencesManager.getInstance();

初始化 ets/entryability/EntryAbility.ets 文件中

import PreferencesManager from '../storage/PreferencesManager';


export default class EntryAbility extends UIAbility {

onWindowStageCreate(windowStage: window.WindowStage): void {
  onWindowStageCreate(windowStage: window.WindowStage): void {     
    // Main window is created, set main page for this ability
    ...
    // 应用数据持久化
    PreferencesManager.init(this.context);
    ...
  }
}

页面中使用 ets/pages/login/LoginPage.ets

import PreferencesManager, { PreferKeys } from '../../storage/PreferencesManager';

@ComponentV2
export struct LoginPage {
  ...
  // 初始化
  aboutToAppear() {
    // 获取缓存中数据
    this.account = PreferencesManager.get(PreferKeys.USER_NAME, '') as string;
  }

  // 登陆
  _handleLogin() {
    // 将 token 存入缓存中
    PreferencesManager.put(PreferKeys.TOKEN, this.account)
  }
  ...
}

2. 组件状态管理

状态管理对比

状态管理V1-V2迁移指导

Flutter 状态管理ArkUI-X V1装饰器ArkUI-X V2装饰器
setState()@State@Local
Widget 参数传递@Prop@Param
父子组件双向绑定@Link@Param/@Event
--@ObjectLink@Param
Provider@Provide@Provider
InheritedWidget@Consume@Consumer
--@Watch@Monitor

全局状态管理

Flutter 状态管理ArkUI-X V1装饰器ArkUI-X V2装饰器
--LocalStorage@ObservedV2/@Trace
--AppStorageAppStorageV2
--PersistentStoragePersistenceV2
示例代码

1)页面内部变量 setState 更新数据

// Flutter 组件

// 基本类型
int _selectedIndex = 0;
// 自定义对象
List<LanguageItem> _languageList = [];

// 赋值
@override
void initState() {
  super.initState();
  
  _languageList.add(new LanguageItem("繁體中文", false, "zh", "HK"));
  _languageList.add(new LanguageItem("简体中文", false, "zh", "CN"));
  _languageList.add(new LanguageItem("English", false, "en", "US"));
  
  _selectedIndex = 1; // 真实情况是从缓存取一下
  
  setState(() {});
}

// ArkUI-X 组件

// 基本类型
@Local _selectedIndex:number = 0;
// 自定义对象
@Local _languageList: Array<LanguageItem> = [];

/******  生命周期  ******/
aboutToAppear() {
  this._languageList.push({
    title: "繁體中文",
    isSelected: false,
    languageCode: "zh",
    countryCode: "HK"
  })
  this._languageList.push({
    title: "简体中文",
    isSelected: false,
    languageCode: "zh",
    countryCode: "CN"
  })
  this._languageList.push({
    title: "English",
    isSelected: false,
    languageCode: "en",
    countryCode: "US"
  })

  this._selectedIndex = 1;
}