<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: flfljh</title>
    <description>The latest articles on DEV Community by flfljh (@flfljh).</description>
    <link>https://dev.to/flfljh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3270325%2F37919791-5fb3-4587-9f3e-2cb5c7936ef6.png</url>
      <title>DEV Community: flfljh</title>
      <link>https://dev.to/flfljh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/flfljh"/>
    <language>en</language>
    <item>
      <title>Modal Dialogs in HarmonyOS: Usage and Interaction Modes</title>
      <dc:creator>flfljh</dc:creator>
      <pubDate>Mon, 21 Jul 2025 05:43:32 +0000</pubDate>
      <link>https://dev.to/flfljh/modal-dialogs-in-harmonyos-usage-and-interaction-modes-2f4i</link>
      <guid>https://dev.to/flfljh/modal-dialogs-in-harmonyos-usage-and-interaction-modes-2f4i</guid>
      <description>&lt;h1&gt;
  
  
  Modal Dialogs in HarmonyOS: Usage and Interaction Modes
&lt;/h1&gt;

&lt;h3&gt;
  
  
  1. Overview
&lt;/h3&gt;

&lt;p&gt;Modal dialogs in HarmonyOS restrict user interaction to the dialog itself until dismissed. ArkUI provides multiple modal components tailored for different scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AlertDialog&lt;/strong&gt;: For alerts, confirmations, or prompts (supports titles, messages, up to 3 buttons, inputs, icons, checkboxes, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ActionSheet&lt;/strong&gt;: For presenting lists of actionable items (e.g., menu choices).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CustomDialog&lt;/strong&gt;: For fully customizable UIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Popup&lt;/strong&gt;, &lt;strong&gt;Menu&lt;/strong&gt;, &lt;strong&gt;ContextMenu&lt;/strong&gt;: For lightweight contextual interactions.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Global Modal Dialogs
&lt;/h3&gt;

&lt;h4&gt;
  
  
  AlertDialog
&lt;/h4&gt;

&lt;p&gt;Example with title, message, buttons, and optional features:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AlertDialog.show({
  title: 'Important Notice',
  subtitle: 'Subtitle Placeholder',
  message: 'This is a sample alert message.',
  autoCancel: true,
  alignment: DialogAlignment.Bottom,
  gridCount: 4,
  offset: { dx: 0, dy: -20 },
  primaryButton: {
    value: 'Cancel',
    action: () =&amp;gt; console.info('Cancel button clicked')
  },
  secondaryButton: {
    enabled: true,
    defaultFocus: true,
    style: DialogButtonStyle.HIGHLIGHT,
    value: 'Confirm',
    action: () =&amp;gt; console.info('Confirm button clicked')
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ActionSheet
&lt;/h4&gt;

&lt;p&gt;Example for selecting an option from a list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ActionSheet.show({
  title: 'Select a Fruit',
  subtitle: 'Choose your favorite',
  message: 'Available options:',
  autoCancel: true,
  confirm: {
    value: 'Confirm Selection',
    action: () =&amp;gt; console.log('Selection confirmed')
  },
  alignment: DialogAlignment.Bottom,
  offset: { dx: 0, dy: -10 },
  sheets: [
    { title: 'Apples', action: () =&amp;gt; console.log('Apples selected') },
    { title: 'Bananas', action: () =&amp;gt; console.log('Bananas selected') },
    { title: 'Pears', action: () =&amp;gt; console.log('Pears selected') }
  ]
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  CustomDialog
&lt;/h4&gt;

&lt;p&gt;Example of a text input dialog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Define custom dialog content
@Builder
function CustomInputDialog() {
  Column() {
    TextInput({ placeholder: 'Enter new text' });
    Row() {
      Button('Save').onClick(() =&amp;gt; {/* Save logic */});
      Button('Cancel').onClick(() =&amp;gt; {/* Close dialog */});
    }
  }
}

// Show dialog
PromptAction.openCustomDialog({
  builder: CustomInputDialog,
  title: 'Edit Text',
  primaryButton: { value: 'Save' },
  secondaryButton: { value: 'Cancel' }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Popup (Tooltip-like Behavior)
&lt;/h3&gt;

&lt;p&gt;Example of a popup triggered by a button click:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Button('Show Popup')
  .onClick(() =&amp;gt; this.showPopup = !this.showPopup)
  .bindPopup(this.showPopup, {
    message: 'This is a popup message!',
    placementOnTop: true,
    showInSubWindow: false,
    primaryButton: {
      value: 'OK',
      action: () =&amp;gt; {
        this.showPopup = false;
        console.info('OK clicked');
      }
    },
    secondaryButton: {
      value: 'Cancel',
      action: () =&amp;gt; {
        this.showPopup = false;
        console.info('Cancel clicked');
      }
    },
    onStateChange: (event) =&amp;gt; {
      console.info(`Popup visibility: ${event.isVisible}`);
      if (!event.isVisible) this.showPopup = false;
    }
  })
  .position({ x: 100, y: 150 });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Menu and ContextMenu
&lt;/h3&gt;

&lt;h4&gt;
  
  
  bindMenu (Standard Menu)
&lt;/h4&gt;

&lt;p&gt;Attach a menu to a component (no preview image):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Builder
function MyMenu() {
  Menu() {
    MenuItem({ content: 'Option 1' });
    MenuItem({ content: 'Option 2' });
    MenuItem({ content: 'Option 3' });
  }
}

Row() {
  Column() {
    Text('Click to show menu')
      .fontSize(50)
      .fontWeight(FontWeight.Bold);
  }
  .bindMenu(MyMenu)
  .width('100%');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  bindContextMenu (Context Menu with Preview)
&lt;/h4&gt;

&lt;p&gt;For long-press actions (e.g., desktop shortcuts):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Column() {
  Text('Long press for context menu')
    .width('100%')
    .margin({ top: 5 });
}
.bindContextMenu(() =&amp;gt; MenuBuilder.create(), ResponseType.LongPress, {
  placement: Placement.Left,
  preview: MenuPreviewMode.IMAGE
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. Subwindow Support
&lt;/h3&gt;

&lt;p&gt;For 2-in-1 devices, use &lt;code&gt;showInSubWindow: true&lt;/code&gt; to display dialogs outside the main window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CustomDialog.show({
  title: 'Subwindow Dialog',
  message: 'This dialog appears in a subwindow!',
  showInSubWindow: true
});

// Or via ContextMenu
.bindContextMenu(..., { showInSubWindow: true });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;HarmonyOS offers flexible modal components for diverse use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AlertDialog&lt;/strong&gt;: Critical alerts/confirmations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ActionSheet&lt;/strong&gt;: List-based selections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CustomDialog&lt;/strong&gt;: Tailored UIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Popup/Menu&lt;/strong&gt;: Contextual interactions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subwindow Support&lt;/strong&gt;: Extend dialogs beyond app boundaries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For advanced scenarios, combine with state management or custom animations.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>HarmonyOS Next: Maintaining and Extending a Custom Rich Text Component</title>
      <dc:creator>flfljh</dc:creator>
      <pubDate>Sat, 28 Jun 2025 01:33:07 +0000</pubDate>
      <link>https://dev.to/flfljh/harmonyos-next-maintaining-and-extending-a-custom-rich-text-component-1pi9</link>
      <guid>https://dev.to/flfljh/harmonyos-next-maintaining-and-extending-a-custom-rich-text-component-1pi9</guid>
      <description>&lt;h1&gt;
  
  
  HarmonyOS Next: Maintaining and Extending a Custom Rich Text Component
&lt;/h1&gt;

&lt;p&gt;Our business currently uses HarmonyOS Next’s built-in &lt;code&gt;HPRichText&lt;/code&gt; component. However, we encountered limitations such as &lt;strong&gt;missing &lt;code&gt;max-width&lt;/code&gt;/&lt;code&gt;max-height&lt;/code&gt; support&lt;/strong&gt; and &lt;strong&gt;text rendering issues after upgrading from v2 to v3&lt;/strong&gt;. To address these challenges, we decided to fork and customize the &lt;code&gt;HPRichText&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;This customization is based on the &lt;strong&gt;v2.2.4 release&lt;/strong&gt; of the community-maintained &lt;a href="https://github.com/asasugar/HPRichText/releases/tag/v2.2.4" rel="noopener noreferrer"&gt;HPRichText&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Enhancements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Support for &lt;code&gt;maxWidth&lt;/code&gt;, &lt;code&gt;maxHeight&lt;/code&gt;, &lt;code&gt;minWidth&lt;/code&gt;, &lt;code&gt;minHeight&lt;/code&gt; Attributes
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1-1. Add Constraint Sizing to &lt;code&gt;fancyImage&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.constraintSize({
  maxWidth: $$.maxWidth,
  maxHeight: $$.maxHeight,
  minWidth: $$.minWidth,
  minHeight: $$.minHeight,
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  1-2. Extend &lt;code&gt;ShapeAttr&lt;/code&gt; Interface
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export interface ShapeAttr {
  // Existing properties...
  maxWidth?: string | number;
  maxHeight?: string | number;
  minWidth?: string | number;
  minHeight?: string | number;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  1-3. Update HTML Parser
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.assignArtUIStyleObject(node, {
  // Existing properties...
  maxWidth: node?.artUIStyleObject?.maxWidth,
  maxHeight: node?.artUIStyleObject?.maxHeight,
  minWidth: node?.artUIStyleObject?.minWidth,
  minHeight: node?.artUIStyleObject?.minHeight,
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2. Auto-Set Original Width for Images on Load Completion
&lt;/h3&gt;

&lt;h4&gt;
  
  
  2-1. Rename &lt;code&gt;HPRichText&lt;/code&gt; to &lt;code&gt;MyRichText&lt;/code&gt;
&lt;/h4&gt;

&lt;h4&gt;
  
  
  2-2. Extract &lt;code&gt;Image&lt;/code&gt; Logic into &lt;code&gt;ImageNode&lt;/code&gt; Component
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import type { FancyImageOptions } from './index';
import type { ArtStyleObject, Attr } from '../../common/types/htmlParser';
import { LogUtil } from '../../../../common/utils/LogUtil';

@Extend(Image)
function fancyImage($$: FancyImageOptions = {}, attrs: Attr = {}) {
  .width($$.width)
  .height($$.height)
  .constraintSize({
    maxWidth: $$.maxWidth,
    maxHeight: $$.maxHeight,
    minWidth: $$.minWidth,
    minHeight: $$.minHeight,
  })
  .margin($$.margin)
  .padding($$.padding)
  .alt(attrs.alt)
  .opacity($$.opacity)
  .objectFit($$.objectFit);
}

@Component
export struct ImageNode {
  @Prop src: string;
  @Prop artUIStyleObject: ArtStyleObject;
  @Prop attr: Attr;
  onClickEvent?: (event: ClickEvent) =&amp;gt; void;

  build() {
    Image(this.src)
      .fancyImage(this.artUIStyleObject, this.attr)
      .onClick(event =&amp;gt; {
        if (this.onClickEvent) this.onClickEvent(event);
      })
      .onComplete(event =&amp;gt; {
        if (event &amp;amp;&amp;amp; this.artUIStyleObject) {
          // Auto-set original width if not defined
          if (!this.artUIStyleObject.width) {
            this.artUIStyleObject.width = event.width;
            LogUtil.info("Auto-set original width:", this.artUIStyleObject.width);
          }
        }
      });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Fixes and Improvements
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Compatibility&lt;/strong&gt;: Resolved text rendering regressions introduced in HPRichText v3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: Added explicit sizing constraints for images and text containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Optimized image loading by preserving original dimensions only when needed.&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>HarmonyOS Next: Implementing Photo Selection and Upload Functionality</title>
      <dc:creator>flfljh</dc:creator>
      <pubDate>Sat, 28 Jun 2025 01:28:29 +0000</pubDate>
      <link>https://dev.to/flfljh/harmonyos-next-implementing-photo-selection-and-upload-functionality-47pd</link>
      <guid>https://dev.to/flfljh/harmonyos-next-implementing-photo-selection-and-upload-functionality-47pd</guid>
      <description>&lt;h1&gt;
  
  
  HarmonyOS Next: Implementing Photo Selection and Upload Functionality
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Implementation Journey
&lt;/h2&gt;

&lt;p&gt;Initially, we used the &lt;code&gt;PickerUtil.selectPhoto&lt;/code&gt; method from the &lt;a href="https://ohpm.openharmony.cn/#/cn/detail/@pura%2Fharmony-utils" rel="noopener noreferrer"&gt;HarmonyUtils Library&lt;/a&gt; to select photos from the gallery, which returns a &lt;code&gt;fileUri&lt;/code&gt;. However, this library lacks an upload API. To address this, we researched Huawei’s official documentation and forums and discovered the &lt;a href="https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-request-V5#requestuploadfile9" rel="noopener noreferrer"&gt;HarmonyOS File Upload API&lt;/a&gt;, which allows direct file uploads.&lt;/p&gt;




&lt;h2&gt;
  
  
  Code Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Upload Function Wrapper
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { request, BusinessError } from '@kit.BasicServicesKit';
import { LogUtil } from '../common/utils/LogUtil';
import { PreferencesUtil } from '../common/utils/PreferencesUtil';
import UserCacheManager from '../common/utils/UserCacheManager';
import CommonHttp from './CommonHttp';
import Config from './Config';

// Normalize request URL
function requestSrc(val: string): string {
  return val.toLowerCase().startsWith('http') ? val : `${Config.api_hosts}${val}`;
}

// Convert params to FormData format
function reqParamsToData(params: Record&amp;lt;string, any&amp;gt; = {}): Array&amp;lt;request.RequestData&amp;gt; {
  const data: Array&amp;lt;request.RequestData&amp;gt; = [];
  Object.keys(params).forEach(key =&amp;gt; {
    if (params[key]) {
      data.push({ name: key, value: params[key].toString() });
    }
  });
  LogUtil.info(`reqParamsToData: ${JSON.stringify(data)}`);
  return data;
}

// Upload API interface
export interface RequestUploadResp {
  uploadTask: request.UploadTask;
  err?: Error;
}

export interface RequestUploadConfig {
  files: Array&amp;lt;request.File&amp;gt;;
}

// Global upload function
export const globalRequestUpload = (
  url: string,
  params: Record&amp;lt;string, any&amp;gt; = {},
  config: RequestUploadConfig
): Promise&amp;lt;RequestUploadResp&amp;gt; =&amp;gt; {
  // Fetch user context data
  const cateId = UserCacheManager.getSubjectCatId() || PreferencesUtil.getStringSync('_subjectCatId');
  const subjectId = UserCacheManager.getSubjectId() || PreferencesUtil.getStringSync('_subjectId');
  const goodAppType = UserCacheManager.getSubAppType() || PreferencesUtil.getStringSync('_subAppType');

  // Merge default params
  params.cate_id = params.cate_id || cateId;
  params.subject_id = params.subject_id || subjectId;
  params.good_app_type = params.good_app_type || goodAppType;

  // Construct request URL
  const urls = CommonHttp.requestMd5Params({});
  const reqUrl = requestSrc(`${url}${CommonHttp.formatGetUri(urls)}`);

  // Prepare headers and data
  const token = UserCacheManager.getLoginToken() || PreferencesUtil.getStringSync('access_token');
  const uploadConfig: request.UploadConfig = {
    url: reqUrl,
    header: {
      'Content-Type': 'multipart/form-data',
      Authorization: token
    },
    method: 'POST',
    files: config.files,
    data: reqParamsToData(params)
  };

  LogUtil.info(`uploadConfig: ${JSON.stringify(uploadConfig)}`);

  return new Promise((resolve, reject) =&amp;gt; {
    try {
      request.uploadFile(getContext(), uploadConfig)
        .then((task: request.UploadTask) =&amp;gt; resolve({ uploadTask: task }))
        .catch((err: BusinessError) =&amp;gt; {
          LogUtil.error(`Upload failed. Code: ${err.code}, Message: ${err.message}`);
          reject({ err });
        });
    } catch (err) {
      LogUtil.error(`Upload error: ${JSON.stringify(err)}`);
      reject({ err });
    }
  });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2. Photo Selection and URI Conversion
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Convert image URI to sandbox path (required for upload)
static async toInternalCacheUrl(selImg: string, fileType: string = 'jpg'): Promise&amp;lt;string&amp;gt; {
  if (!selImg) throw new Error('Invalid image URI');

  // Copy file to internal cache
  const cacheDir = getContext().cacheDir;
  const fileName = `${Date.now()}.${fileType}`;
  const originImg = await fileIo.open(selImg); // Open original file
  const source = image.createImageSource(originImg.fd); // Convert to ImageSource
  const packer = image.createImagePacker(); // Initialize packer
  const arrayBuffer = await packer.packing(source, { quality: 10, format: 'image/jpeg' }); // Compress

  // Write to cache
  const newFile = fileIo.openSync(
    `${cacheDir}/${fileName}`,
    fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE
  );
  fileIo.writeSync(newFile.fd, arrayBuffer); // Save compressed file

  return `internal://cache/${fileName}`; // Return sandbox URI
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Notes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Uploading directly via &lt;code&gt;fileUri&lt;/code&gt; returns a &lt;strong&gt;404 error&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;internal://cache/&lt;/code&gt; prefixed URIs (e.g., from &lt;code&gt;toInternalCacheUrl&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Workflow&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   selectPhoto → toInternalCacheUrl → globalRequestUpload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dependencies&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Requires &lt;code&gt;@pura/harmony-utils&lt;/code&gt; for photo selection.&lt;/li&gt;
&lt;li&gt;Uses Huawei’s &lt;code&gt;request.uploadFile&lt;/code&gt; API for file uploads.&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>HarmonyOS Next: Developing Custom Features Based on @xwf/image_preview (V1.0.1)</title>
      <dc:creator>flfljh</dc:creator>
      <pubDate>Sat, 28 Jun 2025 01:27:50 +0000</pubDate>
      <link>https://dev.to/flfljh/harmonyos-next-developing-custom-features-based-on-xwfimagepreview-v101-2kif</link>
      <guid>https://dev.to/flfljh/harmonyos-next-developing-custom-features-based-on-xwfimagepreview-v101-2kif</guid>
      <description>&lt;h1&gt;
  
  
  HarmonyOS Next: Developing Custom Features Based on @xwf/image_preview (V1.0.1)
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Library Link&lt;/strong&gt;: &lt;a href="https://ohpm.openharmony.cn/#/cn/detail/@xwf%2Fimage_preview/v/1.0.1" rel="noopener noreferrer"&gt;https://ohpm.openharmony.cn/#/cn/detail/@xwf%2Fimage_preview/v/1.0.1&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;We leverage the &lt;code&gt;@xwf/image_preview&lt;/code&gt; library for image preview with gesture zooming. However, we need to display previews in a &lt;strong&gt;modal popup&lt;/strong&gt; instead of navigating to a new page. Attempts to use the &lt;code&gt;ImageItemView&lt;/code&gt; component failed due to its dependency on &lt;code&gt;@Consumer&lt;/code&gt;, and we required single-click background dismissal (not grayscale mode switching). Thus, we forked and modified this version for our needs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using &lt;code&gt;MyImageItemView&lt;/code&gt; Component
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Rename Components&lt;/p&gt;

&lt;p&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ImageItemView&lt;/code&gt; → &lt;code&gt;MyImageItemView&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ImagePreview&lt;/code&gt; → &lt;code&gt;MyImagePreview&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Remove &lt;code&gt;@Consumer&lt;/code&gt; Code&lt;/strong&gt;:&lt;br&gt;
Delete dependency injection code from &lt;code&gt;MyImageItemView&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add &lt;code&gt;onSingleClickBg&lt;/code&gt; Event&lt;/strong&gt;:&lt;br&gt;
Implement a callback for background clicks.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Full Code for &lt;code&gt;MyImagePreview&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { CommonConstants } from '../constants/CommonConstants';
import { ImagePreviewOption, ImageType } from '../model/ImagePreviewOption';
import { MyImageItemView } from './MyImageItemView';

@Component
export struct MyImagePreview {
  // Image data
  option: ImagePreviewOption = { images: [], index: 0 };
  // Indicator style
  indicator: DotIndicator | DigitIndicator | boolean = new DotIndicator()
    .color(Color.Gray)
    .selectedColor(Color.Blue);
  // Page index change listener
  onChange: (index: number) =&amp;gt; void = () =&amp;gt; {};
  onSingleClickBg?: () =&amp;gt; void;

  private DISPLAY_COUNT: number = 1;
  private MIN_SCALE: number = 0.75;
  @State private opacityList: number[] = [];
  @State private scaleList: number[] = [];
  @State private translateList: number[] = [];
  @State private zIndexList: number[] = [];
  @State private bgc: Color = Color.Black;

  aboutToAppear(): void {
    for (let i = 0; i &amp;lt; this.option.images.length; i++) {
      this.opacityList.push(1.0);
      this.scaleList.push(1.0);
      this.translateList.push(0.0);
      this.zIndexList.push(0);
    }
  }

  build() {
    Stack() {
      Swiper() {
        ForEach(this.option.images, (image: ImageType, index: number) =&amp;gt; {
          MyImageItemView({ url: image, onSingleClickBg: this.onSingleClickBg })
            .width(CommonConstants.THOUSANDTH_1000)
            .height(CommonConstants.THOUSANDTH_1000)
            .opacity(this.opacityList[index])
            .scale({ x: this.scaleList[index], y: this.scaleList[index] })
            .translate({ x: this.translateList[index] })
            .zIndex(this.zIndexList[index]);
        });
      }
      .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
      .width(CommonConstants.THOUSANDTH_1000)
      .height(CommonConstants.THOUSANDTH_1000)
      .loop(false)
      .indicator(
        this.option.images.length &amp;gt; 1
          ? this.indicator
          : false
      )
      .displayCount(this.DISPLAY_COUNT, true)
      .index(this.option.index)
      .customContentTransition({
        // Timeout for removing pages from the render tree
        timeout: 1000,
        // Custom transition animation
        transition: (proxy: SwiperContentTransitionProxy) =&amp;gt; {
          if (
            proxy.position &amp;lt;= proxy.index % this.DISPLAY_COUNT ||
            proxy.position &amp;gt;= this.DISPLAY_COUNT + proxy.index % this.DISPLAY_COUNT
          ) {
            // Reset properties for pages outside the viewport
            this.opacityList[proxy.index] = 1.0;
            this.scaleList[proxy.index] = 1.0;
            this.translateList[proxy.index] = 0.0;
            this.zIndexList[proxy.index] = 0;
          } else {
            // Animate adjacent pages into view
            if (proxy.index % this.DISPLAY_COUNT === 0) {
              this.opacityList[proxy.index] = 1 - proxy.position / this.DISPLAY_COUNT;
              this.scaleList[proxy.index] = this.MIN_SCALE +
                (1 - this.MIN_SCALE) * (1 - proxy.position / this.DISPLAY_COUNT);
              this.translateList[proxy.index] = -proxy.position * proxy.mainAxisLength +
                (1 - this.scaleList[proxy.index]) * proxy.mainAxisLength / 2.0;
            } else {
              this.opacityList[proxy.index] = 1 - (proxy.position - 1) / this.DISPLAY_COUNT;
              this.scaleList[proxy.index] = this.MIN_SCALE +
                (1 - this.MIN_SCALE) * (1 - (proxy.position - 1) / this.DISPLAY_COUNT);
              this.translateList[proxy.index] = -(proxy.position - 1) * proxy.mainAxisLength -
                (1 - this.scaleList[proxy.index]) * proxy.mainAxisLength / 2.0;
            }
            this.zIndexList[proxy.index] = -1;
          }
        },
      })
      .onChange((index) =&amp;gt; {
        this.onChange(index);
      });
      // Additional event listeners can be added here
    }
    .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
    .backgroundColor(this.bgc)
    .width(CommonConstants.THOUSANDTH_1000)
    .height(CommonConstants.THOUSANDTH_1000);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Notes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Customization Focus&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Modified gesture handling and animation logic.&lt;/li&gt;
&lt;li&gt;Removed grayscale mode and implemented single-click dismissal.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical Details&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Uses &lt;code&gt;Swiper&lt;/code&gt; for image carousel management.&lt;/li&gt;
&lt;li&gt;Implements custom transition animations via &lt;code&gt;customContentTransition&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependencies&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Requires &lt;code&gt;@xwf/image_preview&lt;/code&gt; V1.0.1 (modified locally).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Let me know if you need further refinements or additional context!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>HarmonyOS Next: Adding Push Notifications to Atomic Services</title>
      <dc:creator>flfljh</dc:creator>
      <pubDate>Sat, 28 Jun 2025 01:27:23 +0000</pubDate>
      <link>https://dev.to/flfljh/harmonyos-next-adding-push-notifications-to-atomic-services-3l1h</link>
      <guid>https://dev.to/flfljh/harmonyos-next-adding-push-notifications-to-atomic-services-3l1h</guid>
      <description>&lt;h1&gt;
  
  
  HarmonyOS Next: Adding Push Notifications to Atomic Services
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Official Documentation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Atomic Service Push&lt;/strong&gt;: &lt;a href="https://developer.huawei.com/consumer/cn/doc/atomic-guides-V5/atomic-push-development-V5" rel="noopener noreferrer"&gt;https://developer.huawei.com/consumer/cn/doc/atomic-guides-V5/atomic-push-development-V5&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription Template Setup&lt;/strong&gt;: &lt;a href="https://developer.huawei.com/consumer/cn/doc/atomic-guides-V5/push-as-service-noti-V5" rel="noopener noreferrer"&gt;https://developer.huawei.com/consumer/cn/doc/atomic-guides-V5/push-as-service-noti-V5&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Differences from App Push
&lt;/h2&gt;

&lt;p&gt;Atomic services require additional configuration in &lt;strong&gt;AppGallery Connect&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Subscription Templates&lt;/strong&gt;: Define notification templates via the &lt;a href="https://developer.huawei.com/consumer/cn/doc/atomic-guides-V5/push-as-service-noti-V5" rel="noopener noreferrer"&gt;official guide&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Code Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;PushMsgUtils&lt;/code&gt; Class
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { UIAbility } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { serviceNotification } from '@kit.PushKit';
import { AppUtil } from './AppUtil';
import { LogUtil } from './LogUtil';
import { Want } from '@kit.AbilityKit';
import { AdUtils } from './AdUtils';
import { GlobalContext } from './GlobalContext';

interface MessageReceivedParams {
  want?: Want;
  method: "onCreate" | "onNewWant" | "enterDnTabBarPage";
}

export default class PushMsgUtils {
  /**
   * Request subscription to notification templates.
   * Replace `entityIds` with your actual template IDs.
   */
  static async requestSubscribeNotification() {
    try {
      LogUtil.info("Running requestSubscribeNotification...");
      const entityIds: string[] = ['13B6000E24008000', '13B90C82D3802680'];
      const type: serviceNotification.SubscribeNotificationType =
        serviceNotification.SubscribeNotificationType.SUBSCRIBE_WITH_HUAWEI_ID;

      const res: serviceNotification.RequestResult = await serviceNotification.requestSubscribeNotification(
        AppUtil.getContext(),
        entityIds,
        type
      );
      LogUtil.info(`Subscription succeeded: ${JSON.stringify(res.entityResult)}`);
    } catch (err) {
      const e: BusinessError = err as BusinessError;
      LogUtil.error(`Subscription failed: ${e.code} ${e.message}`);
    }
  }

  /**
   * Handle incoming push notifications.
   * @param params - Message context (`onCreate`, `onNewWant`, or `enterDnTabBarPage`).
   */
  static messageReceived(params: MessageReceivedParams) {
    if (params.want?.uri) {
      LogUtil.info(`Message received (${params.method}):`, JSON.stringify(params.want));

      // Extract target page from message data
      const routePage = params.want.parameters?.['page'] as string;

      if (params.method === "onNewWant") {
        // App is running (foreground/background)
        if (routePage) AdUtils.adClickJump(routePage);
        else LogUtil.error("Missing 'page' parameter for navigation");
      } else if (params.method === "onCreate") {
        // App is launching (cold start)
        if (routePage) 
          GlobalContext.getContext().setObject("messageReceived", routePage);
      }
    }

    if (params.method === "enterDnTabBarPage") {
      // Navigate after app initialization
      const routePage = GlobalContext.getContext().getObject("messageReceived") as string;
      if (routePage) {
        GlobalContext.getContext().deleteObject("messageReceived");
        AdUtils.adClickJump(routePage);
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Workflow Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Request Subscription&lt;/strong&gt;: Call &lt;code&gt;PushMsgUtils.requestSubscribeNotification()&lt;/code&gt; on app launch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Handle Messages&lt;/p&gt;

&lt;p&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;onCreate()&lt;/code&gt; to handle cold starts.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;onNewWant()&lt;/code&gt; to handle foreground/background notifications.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;enterDnTabBarPage()&lt;/code&gt; to navigate after initialization.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Template ID&lt;/strong&gt;: Replace &lt;code&gt;entityIds&lt;/code&gt; in &lt;code&gt;requestSubscribeNotification()&lt;/code&gt; with your actual subscription template IDs from AppGallery Connect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigation&lt;/strong&gt;: The &lt;code&gt;routePage&lt;/code&gt; parameter in messages determines the target UI page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Management&lt;/strong&gt;: Use &lt;code&gt;GlobalContext&lt;/code&gt; to persist navigation data across lifecycle events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me know if you need further refinements!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>HarmonyOS Flutter: Simulator Development and Debugging Steps</title>
      <dc:creator>flfljh</dc:creator>
      <pubDate>Sat, 28 Jun 2025 01:26:53 +0000</pubDate>
      <link>https://dev.to/flfljh/harmonyos-flutter-simulator-development-and-debugging-steps-1oa2</link>
      <guid>https://dev.to/flfljh/harmonyos-flutter-simulator-development-and-debugging-steps-1oa2</guid>
      <description>&lt;h1&gt;
  
  
  HarmonyOS Flutter: Simulator Development and Debugging Steps
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Development computer must be an M-series chip (ARM architecture) Mac computer&lt;/strong&gt;&lt;br&gt;
Currently, Flutter HarmonyOS development &lt;strong&gt;does not support X86 architecture simulators&lt;/strong&gt; and requires an &lt;strong&gt;ARM architecture simulator&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a Project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After setting up the development environment, create a project using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter create &lt;span class="nt"&gt;--platforms&lt;/span&gt; ohos ohos_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add HarmonyOS Support to Existing Projects
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter create &lt;span class="nt"&gt;--platforms&lt;/span&gt; ohos &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Signing Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;code&gt;ohos&lt;/code&gt; directory (HarmonyOS project) in DevEco.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;code&gt;File → Project Structure...&lt;/code&gt;, select &lt;code&gt;Signing Configs&lt;/code&gt;, check &lt;strong&gt;Automatically generate signature&lt;/strong&gt;, then click &lt;strong&gt;Sign In&lt;/strong&gt; with your Huawei account.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; hvigor WARN: The current module &lt;span class="s1"&gt;'ohos'&lt;/span&gt; has dependency which is not installed at its oh-package.json5.
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; hvigor Finished :entry:init... after 1 ms
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; hvigor Finished ::init... after 1 ms

Process finished with &lt;span class="nb"&gt;exit &lt;/span&gt;code 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create a Simulator&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.Open &lt;strong&gt;Device Manager&lt;/strong&gt; in DevEco (dropdown next to the run button).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2F%2F%2Fupload-images.jianshu.io%2Fupload_images%2F11424067-daa6a20f736cd36e.png%3FimageMogr2%2Fauto-orient%2Fstrip%7CimageView2%2F2%2Fw%2F1094" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2F%2F%2Fupload-images.jianshu.io%2Fupload_images%2F11424067-daa6a20f736cd36e.png%3FimageMogr2%2Fauto-orient%2Fstrip%7CimageView2%2F2%2Fw%2F1094" alt="img" width="1094" height="692"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;image.png&lt;/p&gt;

&lt;p&gt;2.Click &lt;strong&gt;+ New Emulator&lt;/strong&gt; in the bottom-right corner.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If images are not downloaded, click the download button first.&lt;/li&gt;
&lt;li&gt;After downloading, proceed to create the emulator.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2F%2F%2Fupload-images.jianshu.io%2Fupload_images%2F11424067-c37e9de24eff4350.png%3FimageMogr2%2Fauto-orient%2Fstrip%7CimageView2%2F2%2Fw%2F1200" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2F%2F%2Fupload-images.jianshu.io%2Fupload_images%2F11424067-c37e9de24eff4350.png%3FimageMogr2%2Fauto-orient%2Fstrip%7CimageView2%2F2%2Fw%2F1200" alt="img" width="1200" height="843"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;image.png&lt;/p&gt;

&lt;p&gt;3.Start the newly created emulator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run the Flutter Project&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ensure the simulator device (e.g., &lt;code&gt;127.0.0.1:5555(ohos-arm64)&lt;/code&gt;) appears in VSCode’s device list.&lt;/li&gt;
&lt;li&gt;Run the Flutter project as usual.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Troubleshooting&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. App Crashes on Startup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;X86 Simulators&lt;/strong&gt;: Delete &lt;code&gt;FloatingActionButton&lt;/code&gt; in &lt;code&gt;main.dart&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Impeller Rendering Issues&lt;/strong&gt;:
Create &lt;code&gt;ohos/entry/src/main/resources/rawfile/buildinfo.json5&lt;/code&gt; with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"enable_impeller"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Simulator Not Visible in VSCode
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Restart DevEco or VSCode.&lt;/li&gt;
&lt;li&gt;Ensure the &lt;code&gt;ohos&lt;/code&gt; directory is open in DevEco.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Incorrect Flutter Version with FVM
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Configure FVM in VSCode’s &lt;code&gt;settings.json&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dart.flutterSdkPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".fvm/versions/custom_3.22.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"flutter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"custom_3.22.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Verify Flutter version:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Flutter 3.22.0-ohos • channel oh-3.22.0 • https://gitee.com/harmonycommando_flutter/flutter.git
Framework • revision 85630b0330 &lt;span class="o"&gt;(&lt;/span&gt;13 天前&lt;span class="o"&gt;)&lt;/span&gt; • 2024-10-26 02:39:47 +0000
Engine • revision f6344b75dc
Tools • Dart 3.4.0 • DevTools 2.34.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.3 Disable global Flutter configuration in &lt;code&gt;~/.zshrc&lt;/code&gt; or &lt;code&gt;~/.bash_profile&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#export PATH="/Users/zacksleo/flutter/bin:$PATH"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Implementing Video Compression in HarmonyOS Flutter with ohos_videocompressor</title>
      <dc:creator>flfljh</dc:creator>
      <pubDate>Sat, 28 Jun 2025 01:26:06 +0000</pubDate>
      <link>https://dev.to/flfljh/implementing-video-compression-in-harmonyos-flutter-with-ohosvideocompressor-1od1</link>
      <guid>https://dev.to/flfljh/implementing-video-compression-in-harmonyos-flutter-with-ohosvideocompressor-1od1</guid>
      <description>&lt;h1&gt;
  
  
  Implementing Video Compression in HarmonyOS Flutter with ohos_videocompressor
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ohos_videocompressor&lt;/code&gt; is a high-performance video compression library for HarmonyOS. It provides robust video compression capabilities optimized for HarmonyOS applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High-efficiency video compression&lt;/li&gt;
&lt;li&gt;Support for multiple video formats&lt;/li&gt;
&lt;li&gt;Quality level customization&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation Methods
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Method 1: Clone with Submodules
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```bash
git clone https://gitee.com/openharmony-sig/ohos_videocompressor.git --recurse-submodules
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Method 2: Manual Download
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Download the main repository&lt;/li&gt;
&lt;li&gt;Download &lt;a href="https://gitee.com/openharmony/third_party_bounds_checking_function" rel="noopener noreferrer"&gt;third_party_bounds_checking_function&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Place the downloaded files in &lt;code&gt;videoCompressor/src/cpp/boundscheck&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Install via OHPM
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ohpm &lt;span class="nb"&gt;install&lt;/span&gt; @ohos/videocompressor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Basic Compression Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```javascript
import { VideoCompressor, CompressQuality, CompressorResponseCode } from '@ohos/videocompressor';

// Initialize compressor
const videoCompressor = new VideoCompressor();

// Execute compression
videoCompressor.compressVideo(getContext(), this.selectFilePath, CompressQuality.COMPRESS_QUALITY_HIGH)
  .then((data) =&amp;gt; {
    if (data.code == CompressorResponseCode.SUCCESS) {
      console.log("Compression successful. Output path: " + data.outputPath);
    } else {
      console.error("Compression failed. Error: " + data.message);
    }
  })
  .catch((err) =&amp;gt; {
    console.error("Compression error: " + err.message);
  });
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Supported Specifications
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Input Formats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;MP4&lt;/li&gt;
&lt;li&gt;MPEG-TS&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Video Codecs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decoding&lt;/strong&gt;: AVC (H.264), HEVC (H.265)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encoding&lt;/strong&gt;: AVC (H.264), HEVC (H.265)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Audio Codecs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decoding&lt;/strong&gt;: AAC&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encoding&lt;/strong&gt;: AAC&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  API Reference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;compressVideo(context: Context, inputFilePath: string, quality: CompressQuality): Promise&amp;lt;CompressionResult&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Parameter      | Type             | Description                          |
|----------------|------------------|--------------------------------------|
| `context`      | `Context`        | Application context                  |
| `inputFilePath`| `string`         | Path to source video file            |
| `quality`      | `CompressQuality`| Compression quality level            |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Quality Options&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```javascript
enum CompressQuality {
  COMPRESS_QUALITY_LOW,    // Fastest compression, lowest quality
  COMPRESS_QUALITY_MEDIUM, // Balanced speed and quality
  COMPRESS_QUALITY_HIGH    // Best quality, slower compression
}
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**Return Object**:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```javascript
interface CompressionResult {
  code: CompressorResponseCode; // SUCCESS or error code
  message: string;              // Status message
  outputPath?: string;          // Path to compressed file (on success)
}

enum CompressorResponseCode {
  SUCCESS,                     // Operation successful
  ERR_INVALID_INPUT,           // Invalid input file
  ERR_UNSUPPORTED_FORMAT,      // Unsupported file format
  ERR_COMPRESSION_FAILED,      // General compression failure
  ERR_INSUFFICIENT_STORAGE,    // Storage space issue
  ERR_PERMISSION_DENIED        // Permission error
}
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check File Permissions&lt;/strong&gt;: Ensure read/write access to source and destination paths&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle Storage Space&lt;/strong&gt;: Verify sufficient storage before compression&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality Selection&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;COMPRESS_QUALITY_LOW&lt;/code&gt; for quick social media uploads&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;COMPRESS_QUALITY_HIGH&lt;/code&gt; for archival purposes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling&lt;/strong&gt;: Always implement &lt;code&gt;.catch()&lt;/code&gt; for promise rejection handling&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Performance Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Compression time varies by device capability and video length&lt;/li&gt;
&lt;li&gt;HEVC encoding provides better compression but requires more processing power&lt;/li&gt;
&lt;li&gt;Typical compression ratios:

&lt;ul&gt;
&lt;li&gt;Low quality: 60-70% size reduction&lt;/li&gt;
&lt;li&gt;Medium quality: 40-50% size reduction&lt;/li&gt;
&lt;li&gt;High quality: 20-30% size reduction&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This implementation provides a streamlined solution for adding video compression capabilities to HarmonyOS Flutter applications, balancing performance and quality while maintaining broad format compatibility.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>HarmonyOS Flutter Performance Optimization: Slide Response Latency</title>
      <dc:creator>flfljh</dc:creator>
      <pubDate>Sat, 28 Jun 2025 01:25:39 +0000</pubDate>
      <link>https://dev.to/flfljh/harmonyos-flutter-performance-optimization-slide-response-latency-55k</link>
      <guid>https://dev.to/flfljh/harmonyos-flutter-performance-optimization-slide-response-latency-55k</guid>
      <description>&lt;h1&gt;
  
  
  HarmonyOS Flutter Performance Optimization: Slide Response Latency
&lt;/h1&gt;

&lt;p&gt;This article analyzes trace diagnostics for slide response latency in Flutter applications under touch interaction scenarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Finger Press (Initial Touch)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The finger press event establishes the foundation for all subsequent interactions, capturing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initial touch coordinates&lt;/li&gt;
&lt;li&gt;Targeted UI controls&lt;/li&gt;
&lt;li&gt;Interaction context&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Event Flow:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;mmi_service thread&lt;/strong&gt; handles multimodal input events&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flutter application&lt;/strong&gt; listens and responds to touch events&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Trace Characteristics:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;mmi_service&lt;/code&gt; thread:
&lt;code&gt;"service report touchId:[id], type: down"&lt;/code&gt; immediately after &lt;code&gt;"package touchEvent"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In application main thread:
&lt;code&gt;"DispatchTouchEvent"&lt;/code&gt; with coordinates and &lt;code&gt;type: 0&lt;/code&gt; (down event)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These traces correspond directly across threads:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Finger Slide (Movement)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Slide events follow a controlled delivery mechanism different from initial presses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Differences:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Not immediate&lt;/strong&gt;: Governed by &lt;code&gt;vsync-app&lt;/code&gt; signal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Initial position&lt;/strong&gt;: Matches press coordinates (first move event)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type identifier&lt;/strong&gt;: &lt;code&gt;type: move&lt;/code&gt; (numerical value: 2)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Event Sequence:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;mmi_service&lt;/code&gt; thread detects movement&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;VSyncGenerator&lt;/code&gt; produces synchronization signal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DVSync-app&lt;/code&gt; processes the signal&lt;/li&gt;
&lt;li&gt;Application main thread receives event&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Trace Flow:
&lt;/h3&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Slide Threshold (TouchSlop)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The system-defined minimum recognition distance for slide gestures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Default value&lt;/strong&gt;: 18 units&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable&lt;/strong&gt;: Developers can adjust per control&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Calculation Method:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Capture touch coordinates from main thread traces&lt;/li&gt;
&lt;li&gt;Compute coordinate offsets between events&lt;/li&gt;
&lt;li&gt;Verify if offset exceeds threshold:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; 
     &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
   &lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isSlide&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;TOUCH_SLOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;First Frame Rendering&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When slide distance exceeds threshold:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Flutter triggers UI update operations&lt;/li&gt;
&lt;li&gt;Actual rendering waits for next VSync signal&lt;/li&gt;
&lt;li&gt;One-frame delay occurs before visual update&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Trace Pattern:
&lt;/h3&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;First Frame Completion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Rendering completion identification:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Development tracing&lt;/strong&gt;: &lt;code&gt;RSHardwareThread&lt;/code&gt; in RS process&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated testing&lt;/strong&gt;: &lt;code&gt;dpu_gfx_primary&lt;/code&gt; thread (hardware signal proxy)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Completion Trace:
&lt;/h3&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Measuring Slide Response Latency&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Total response duration spans:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
Start: mmi_service "package touchEvent" (finger press)
End: RSHardwareThread/dpu_gfx_primary (first slide frame rendered)
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Optimization Focus Areas:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Touch event propagation time&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VSync signal generation latency&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frame scheduling efficiency&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Render service processing time&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hardware composition duration&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This analysis methodology enables precise identification of performance bottlenecks in Flutter slide interactions on HarmonyOS, providing actionable insights for latency reduction.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>HarmonyOS ArkWeb Basics - Cross-Origin Requests</title>
      <dc:creator>flfljh</dc:creator>
      <pubDate>Sat, 28 Jun 2025 01:25:16 +0000</pubDate>
      <link>https://dev.to/flfljh/harmonyos-arkweb-basics-cross-origin-requests-ei0</link>
      <guid>https://dev.to/flfljh/harmonyos-arkweb-basics-cross-origin-requests-ei0</guid>
      <description>&lt;h1&gt;
  
  
  HarmonyOS ArkWeb Basics - Cross-Origin Requests
&lt;/h1&gt;

&lt;p&gt;ArkWeb kernel restricts cross-origin access for file and resource protocols by default due to security considerations. This means that if a Web component attempts to load resources from different domains using file or resource protocols, the request will be blocked, preventing resource loading.&lt;/p&gt;

&lt;p&gt;For example, suppose your Web component is deployed on Domain A and you want to load local resources from Domain B, such as images or script files. Due to file and resource protocol restrictions, even if your Web component can access Domain B's URL, it cannot load local resources on Domain B.&lt;/p&gt;

&lt;p&gt;This cross-origin issue may cause:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limited functionality&lt;/strong&gt;: Web components may fail to load necessary resources, resulting in restricted functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Degraded user experience&lt;/strong&gt;: Users may encounter page loading failures or non-functional features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security risks&lt;/strong&gt;: Improper handling of cross-origin issues may lead to security vulnerabilities like Cross-Site Scripting (XSS).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solutions
&lt;/h2&gt;

&lt;p&gt;To address cross-origin issues in ArkWeb framework, consider these solutions:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Use HTTP/HTTPS protocols instead of file/resource protocols
&lt;/h3&gt;

&lt;p&gt;This is the most direct and effective solution. Modify resource URLs from file/resource protocols to HTTP/HTTPS protocols, ensuring the resource server supports cross-origin access.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;&lt;br&gt;
Change &lt;code&gt;file://example.com/path/to/image.png&lt;/code&gt; to&lt;br&gt;&lt;br&gt;
&lt;code&gt;http://example.com/path/to/image.png&lt;/code&gt; or&lt;br&gt;&lt;br&gt;
&lt;code&gt;https://example.com/path/to/image.png&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Use custom domains
&lt;/h3&gt;

&lt;p&gt;Create a custom domain for your local resources and ensure it resolves to your local resources. This avoids triggering ArkWeb kernel's cross-origin restrictions for file/resource protocols.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;&lt;br&gt;
Point custom domain &lt;code&gt;example.com&lt;/code&gt; to your local resource path and access via:&lt;br&gt;&lt;br&gt;
&lt;code&gt;http://example.com/path/to/image.png&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Use onInterceptRequest for local resource interception
&lt;/h3&gt;

&lt;p&gt;Use ArkWeb's &lt;code&gt;onInterceptRequest&lt;/code&gt; interface to intercept local resource requests and provide custom responses, bypassing cross-origin restrictions.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;&lt;br&gt;
Intercept file/resource protocol requests and return custom content responses.&lt;/p&gt;
&lt;h2&gt;
  
  
  Core Network Security Concepts
&lt;/h2&gt;

&lt;p&gt;Beyond solving cross-origin issues, understand these fundamental security concepts:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. CORS Policy
&lt;/h3&gt;

&lt;p&gt;Cross-Origin Resource Sharing (CORS) controls resource sharing between different domains. Configure CORS policies to allow/block cross-origin requests from specific domains.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;&lt;br&gt;
Set CORS policy on resource server to allow requests from Domain A while blocking others.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Same-Origin Policy
&lt;/h3&gt;

&lt;p&gt;A browser security mechanism preventing malicious websites from stealing data from other sites. Requires identical protocol, domain, and port for resource access.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. HTTPS Protocol
&lt;/h3&gt;

&lt;p&gt;Secure version of HTTP using SSL/TLS encryption to protect data transmission security. Essential for sensitive data like personal information and passwords.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code Example
&lt;/h2&gt;

&lt;p&gt;This example demonstrates using &lt;code&gt;onInterceptRequest&lt;/code&gt; to intercept local resource requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```javascript
import { webview } from '@ohos.web.webview';

@Entry
@Component
struct WebComponent {
  controller: webview.WebviewController = new webview.WebviewController();
  responseResource: webview.WebResourceResponse = new webview.WebResourceResponse();

  build() {
    Column() {
      Web({ src: $rawfile("index.html"), controller: this.controller })
        .onInterceptRequest((event) =&amp;gt; {
          if (event &amp;amp;&amp;amp; event.request.getRequestUrl().startsWith("file://")) {
            // Intercept file protocol requests
            this.responseResource.setResponseData($rawfile("local.png"));
            this.responseResource.setResponseEncoding('utf-8');
            this.responseResource.setResponseMimeType('image/png');
            this.responseResource.setResponseCode(200);
            this.responseResource.setReasonMessage('OK');
            return this.responseResource;
          }
          return null;
        });
    }
  }
}
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The ArkWeb framework provides powerful web application development capabilities but requires attention to network security, especially regarding cross-origin requests. By understanding common cross-origin issues and their solutions, along with fundamental network security knowledge, you can develop more secure and reliable web applications that protect user privacy and data security.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Frame Rendering Tracing for HarmonyOS Flutter Performance Optimization</title>
      <dc:creator>flfljh</dc:creator>
      <pubDate>Sat, 28 Jun 2025 01:24:35 +0000</pubDate>
      <link>https://dev.to/flfljh/frame-rendering-tracing-for-harmonyos-flutter-performance-optimization-4ab9</link>
      <guid>https://dev.to/flfljh/frame-rendering-tracing-for-harmonyos-flutter-performance-optimization-4ab9</guid>
      <description>&lt;h1&gt;
  
  
  Frame Rendering Tracing for HarmonyOS Flutter Performance Optimization
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When analyzing Flutter application performance, developers need to capture and examine trace data using specialized tools. This document introduces a targeted approach for tracing individual frame rendering, particularly useful for diagnosing frame rate drops or frame loss issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analysis Tools
&lt;/h2&gt;

&lt;p&gt;Recommended performance analysis tools:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://developer.huawei.com/consumer/cn/download/" rel="noopener noreferrer"&gt;DevEco Studio Profiler&lt;/a&gt;&lt;/strong&gt;
Comprehensive performance analysis suite for HarmonyOS applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://gitee.com/openharmony-sig/smartperf" rel="noopener noreferrer"&gt;SmartPerf&lt;/a&gt;&lt;/strong&gt;
Open-source performance monitoring solution&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Usage Guide&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
Refer to &lt;a href="https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/deep-recording-V5" rel="noopener noreferrer"&gt;DevEco Profiler Tool Introduction&lt;/a&gt; for detailed instructions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Frame Rendering Process
&lt;/h2&gt;

&lt;p&gt;The frame rendering workflow consists of three key stages:&lt;/p&gt;

&lt;p&gt;Understanding this workflow and identifying matching markers across stages is crucial for accurate frame tracing.&lt;/p&gt;

&lt;h3&gt;
  
  
  First Identifier: frame_number
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;frame_number&lt;/code&gt; serves as the primary identifier linking the UI and rasterization threads:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key observations&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;frame_number&lt;/code&gt; appears in both 1.ui and 1.raster threads&lt;/li&gt;
&lt;li&gt;This identifier connects UI processing with rasterization&lt;/li&gt;
&lt;li&gt;If missing, indicates non-Flutter rendering requiring further investigation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Second Identifier: ReuseBuffer/acquire buffer
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;ReuseBuffer&lt;/code&gt; identifier connects rasterization with the render service:&lt;/p&gt;

&lt;h4&gt;
  
  
  Raster Thread (1.raster)
&lt;/h4&gt;

&lt;p&gt;During &lt;code&gt;flutter::SkCanvas::Flush&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requests buffer memory from RS process&lt;/li&gt;
&lt;li&gt;Stores rendered frame content&lt;/li&gt;
&lt;li&gt;Visible in "binder transaction" trace&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Render Service Thread (render_service)
&lt;/h4&gt;

&lt;p&gt;During &lt;code&gt;RSMainThread::DoComposition&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retrieves frame content from buffer memory&lt;/li&gt;
&lt;li&gt;Matching &lt;code&gt;acquire buffer sequence&lt;/code&gt; ID confirms connection&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tracing Methodology
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Identify frame_number&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Locate matching values in UI and raster threads&lt;/li&gt;
&lt;li&gt;Confirm Flutter rendering context&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Track Buffer Transfer&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Match &lt;code&gt;ReuseBuffer&lt;/code&gt; ID in raster thread&lt;/li&gt;
&lt;li&gt;Verify corresponding &lt;code&gt;acquire buffer sequence&lt;/code&gt; in render service&lt;/li&gt;
&lt;li&gt;Confirm frame content transfer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Continuous Thread Tracing&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Render service → RSUniRenderThread → RSHardwareThread&lt;/li&gt;
&lt;li&gt;Assumes sequential processing within the same frame&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Practical Application
&lt;/h2&gt;

&lt;p&gt;This tracing approach enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Precise identification of rendering bottlenecks&lt;/li&gt;
&lt;li&gt;Isolation of frame-specific performance issues&lt;/li&gt;
&lt;li&gt;Differentiation between Flutter and native rendering problems&lt;/li&gt;
&lt;li&gt;Targeted optimization of critical rendering paths&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: While the first two stages have clear identifiers, subsequent stages (RSUniRenderThread to RSHardwareThread) currently rely on continuous thread tracing assumptions due to lack of explicit markers.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
    </item>
    <item>
      <title>Debugging Dart Code in HarmonyOS Flutter Applications</title>
      <dc:creator>flfljh</dc:creator>
      <pubDate>Sat, 28 Jun 2025 01:23:40 +0000</pubDate>
      <link>https://dev.to/flfljh/debugging-dart-code-in-harmonyos-flutter-applications-4l33</link>
      <guid>https://dev.to/flfljh/debugging-dart-code-in-harmonyos-flutter-applications-4l33</guid>
      <description>&lt;h1&gt;
  
  
  Debugging Dart Code in HarmonyOS Flutter Applications
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Debugging Approaches
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ETS Code&lt;/strong&gt;: Use DevEco Studio for debugging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dart Code&lt;/strong&gt;: Use VS Code or Android Studio for debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: When debugging HarmonyOS-adapted Flutter, include the local engine parameter:&lt;br&gt;&lt;br&gt;
&lt;code&gt;--local-engine=/path/to/engine/build&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  VS Code Configuration
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create &lt;code&gt;.vscode/launch.json&lt;/code&gt; in your project root&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following configurations:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"configurations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Flutter (Debug)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--local-engine=/Users/your_user/engine_build/src/out/ohos_debug_unopt_arm64"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Flutter (Profile)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"flutterMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"profile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--local-engine=/Users/your_user/engine_build/src/out/ohos_profile_arm64"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Flutter (Release)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"flutterMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"release"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--local-engine=/Users/your_user/engine_build/src/out/ohos_release_arm64"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Replace &lt;code&gt;/Users/your_user/...&lt;/code&gt; with your actual engine build path&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Start debugging using VS Code's debug panel&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Android Studio Configuration
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open your Flutter project in Android Studio&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;Run &amp;gt; Edit Configurations...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create a new Flutter configuration&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the engine parameter in "Additional arguments":&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--local-engine&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/Users/your_user/engine_build/src/out/ohos_debug_unopt_arm64
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply changes and start debugging&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Key Notes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Engine Paths&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debug: &lt;code&gt;ohos_debug_unopt_arm64&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Profile: &lt;code&gt;ohos_profile_arm64&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Release: &lt;code&gt;ohos_release_arm64&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Path Customization&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;/Users/your_user/...&lt;/code&gt; with your actual build path&lt;/li&gt;
&lt;li&gt;Ensure the path points to a valid engine build&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Debugging Workflow&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set breakpoints in Dart files&lt;/li&gt;
&lt;li&gt;Inspect variables during execution&lt;/li&gt;
&lt;li&gt;Use the debug console for runtime evaluation&lt;/li&gt;
&lt;li&gt;Analyze call stacks for error tracing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This setup enables full debugging capabilities for Dart code in HarmonyOS Flutter applications across different build modes.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Implementing an Exam Timer in HarmonyOS Next</title>
      <dc:creator>flfljh</dc:creator>
      <pubDate>Fri, 27 Jun 2025 01:24:38 +0000</pubDate>
      <link>https://dev.to/flfljh/implementing-an-exam-timer-in-harmonyos-next-2kif</link>
      <guid>https://dev.to/flfljh/implementing-an-exam-timer-in-harmonyos-next-2kif</guid>
      <description>&lt;h1&gt;
  
  
  Implementing an Exam Timer in HarmonyOS Next
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```typescript
@Entry
@Component
struct QuickTestMainPage {
  @State paperAllTime: number = 0; // Total exam duration (exam mode)
  @State remainTimeUi: string = "00:00"; // Formatted time display
  @State remainTime: number = 0; // Actual time value (countdown or stopwatch)

  private doExamTimer: number | null = null; // Timer reference

  aboutToAppear(): void { 
    this.timerInit(); // Initialize timer on component appearance
  }

  aboutToDisappear(): void {
    // Clean up timer when component disappears
    if (this.doExamTimer) {
      clearTimeout(this.doExamTimer);
      this.doExamTimer = null;
    }
  }

  // Initialize timer based on mode
  timerInit() {
    if (this.isTest) {
      // Exam mode: countdown from total time
      this.remainTime = this.paperAllTime - this.makeTime;
    } else {
      // Practice mode: stopwatch timing
      this.remainTime = this.makeTime;
    }
    this.updateTimeDisplay();
    this.startTimer();
  }

  // Update UI with formatted time
  updateTimeDisplay() {
    this.remainTimeUi = Utils.formatSeconds(this.remainTime);
  }

  // Core timer logic
  startTimer() {
    this.doExamTimer = setTimeout(() =&amp;gt; {
      if (this.isTest) {
        // Exam countdown mode
        this.remainTime--;
        this.makeTime = this.paperAllTime - this.remainTime;

        // Stop timer when time expires
        if (this.remainTime &amp;lt;= 0) {
          this.handleTimeExpired();
          return;
        }
      } else {
        // Practice stopwatch mode
        this.remainTime++;
        this.makeTime = this.remainTime;
      }

      this.updateTimeDisplay();
      this.startTimer(); // Recursive call for continuous timing
    }, 1000);
  }

  // Handle exam time expiration
  handleTimeExpired() {
    // Implement exam submission logic here
    console.log("Exam time has expired!");
  }

  build() {
    Column() {
      // Timer icon
      CustomIcon({
        iconType: CustomIconType.icon_timer,
        iconSize: 19,
        iconColor: StyleRes.getStyleColor(StyleColor.textBoldColor, this.lightMode)
      })

      // Time display
      Text(this.remainTimeUi)
        .fontColor(StyleRes.getStyleColor(StyleColor.texSubColor, this.lightMode))
        .fontSize(11)
    }
  }
}
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Implementation Details
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Dual Mode Timer&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exam Mode&lt;/strong&gt;: Countdown timer from total exam duration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Practice Mode&lt;/strong&gt;: Stopwatch tracking elapsed time&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Lifecycle Management&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initializes timer in &lt;code&gt;aboutToAppear()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Cleans up resources in &lt;code&gt;aboutToDisappear()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Prevents memory leaks with proper timeout handling&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Time Formatting&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses &lt;code&gt;Utils.formatSeconds()&lt;/code&gt; to convert seconds to "MM:SS" format&lt;/li&gt;
&lt;li&gt;Automatically updates UI display each second&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Time Expiration Handling&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stops countdown when time reaches zero&lt;/li&gt;
&lt;li&gt;Triggers special handling for exam submission&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Utility Function (formatSeconds)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```typescript
class Utils {
  static formatSeconds(seconds: number): string {
    const mins = Math.floor(seconds / 60).toString().padStart(2, '0');
    const secs = (seconds % 60).toString().padStart(2, '0');
    return `${mins}:${secs}`;
  }
}
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage Notes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Exam Mode Setup&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isTest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paperAllTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 60 minutes in seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Practice Mode Setup&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isTest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;makeTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Start from zero&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation provides a robust exam timing solution for HarmonyOS Next applications, featuring both countdown and stopwatch functionality with proper resource management and UI updates.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
