DEV Community

HarmonyOS
HarmonyOS

Posted on

HarmonyOS Development Guide: Implementing User Personalized Settings Storage with Preferences

Read the original article:HarmonyOS Development Guide: Implementing User Personalized Settings Storage with Preferences

Contents

  • Preface

  • What is Preferences

    • Core Features
    • Applicable Scenarios
  • Case Demonstration

    • Feature Flow
    • Effect Preview
  • Code Implementation

    • Step 1: Project Initialization
      • 1.1 Importing Required Modules
      • 1.2 Defining Type APIs
      • 1.3 Implementing Initialization Function
      • 1.4 Lifecycle Integration
    • Step 2: Loading Main Page Styles
      • 2.1 Importing Dependency Modules
      • 2.2 Implementing Style Loading Logic
    • Step 3: Implementing Theme Settings Page
      • 3.1 Full Page Code
      • 3.2 Core Feature Analysis
    • Key Takeaways
  • Summary

    • Core Points
    • Practical Value
    • Best Practice Recommendations

Preface

Hello everyone, I am Ruocheng. Welcome to the HarmonyOS Practical Development Series. This series aims to provide developers with hands-on technical solutions and ready-to-use code samples to help you quickly master core HarmonyOS app development skills.

This article dives into how to use Preferences for persistent storage so your app can remember user personalized settings. Let us explore this powerful and practical feature together.

What is Preferences

Preferences is a lightweight data storage solution provided by HarmonyOS, with the following characteristics:

Core Features

  • Key-value storage:Stores data in a simple and efficient key-value format
  • Data type support:Supports numbers, strings, booleans, and their array types
  • Persistent storage:Data is automatically saved to the preferencesDir path and remains after app restart
  • Lightweight design:Suitable for storing user configurations, app settings, and other lightweight data

Applicable Scenarios

  • User personalized settings (such as theme and language)
  • App configuration data
  • User preference records
  • Simple state preservation

Note: These APIs are supported starting from API version 9. Please ensure your development environment meets this requirement.

Case Demonstration

This example implements a theme color setting feature, showing the complete data persistence flow:

Feature Flow

  1. Homepage display:Render interface using the saved theme color
  2. Settings page:Offer multiple theme color options for users
  3. Data persistence:Automatically save settings upon selection
  4. Restart verification:Settings remain effective after app restart

Effect Preview

As shown in the figure below, users can enter the settings page from the homepage, select a preferred theme color, and after setting, even if the app is closed and reopened, the personalized setting persists, demonstrating true persistent storage.

HarmonyOS Next 开发干货:Preferences 实现用户个性化设置存储-鸿蒙开发者社区

HarmonyOS Next 开发干货:Preferences 实现用户个性化设置存储-鸿蒙开发者社区

HarmonyOS Next 开发干货:Preferences 实现用户个性化设置存储-鸿蒙开发者社区

Code Implementation

Step 1: Project Initialization

First, initialize in EntryAbility and set the default theme color. This ensures the app has a default style upon first launch.

1.1 Importing Required Modules

Import the necessary dependencies in the EntryAbility.ets file:

import { preferences } from '@kit.ArkData';
import  {CustomStyle } from "../utils/types"
import { BusinessError } from '@kit.BasicServicesKit';
Enter fullscreen mode Exit fullscreen mode

1.2 Defining Type APIs

Define the relevant TypeScript APIs in the ../utils/types file:


export interface CustomStyle{
    setBgColor  :string
}

export interface autoColor{color: string, name: string}

Enter fullscreen mode Exit fullscreen mode

1.3 Implementing Initialization Function

Implement the custom style initialization logic in EntryAbility.ets:

    async initCustomStyle(): Promise<void> {
        try {
            const pref = await preferences.getPreferences(this.context, 'customStyle');
            const styleData = await pref.get('styleData', '');

            if (!styleData) {
                hilog.info(DOMAIN, 'testTag', 'No custom style found, saving default style.');
                const defaultStyle: CustomStyle = {
                    setBgColor: '#F9F7F5',
                };

                await pref.put('styleData', JSON.stringify(defaultStyle));
                await pref.flush();
                hilog.info(DOMAIN, 'testTag', 'Default custom style has been saved successfully.');
            } else {
                hilog.info(DOMAIN, 'testTag', 'Custom style already exists.');
            }
        } catch (err) {
            const error = err as BusinessError;
            hilog.error(DOMAIN, 'testTag', `Failed to initialize custom style. Code is ${error.code}, message is ${error.message}`);
        }
    }
Enter fullscreen mode Exit fullscreen mode

1.4 Lifecycle Integration

Call the initialization function in the onWindowStageCreate lifecycle.

HarmonyOS Next 开发干货:Preferences 实现用户个性化设置存储-鸿蒙开发者社区

Step 2: Loading Main Page Styles

2.1 Importing Dependency Modules

Import the required packages in the main page file:

import { preferences } from '@kit.ArkData';
import  {CustomStyle } from "../utils/types"
import { BusinessError } from '@kit.BasicServicesKit';

Enter fullscreen mode Exit fullscreen mode

2.2 Implementing Style Loading Logic

Write the core function to load and apply the theme color:

    @State autoBgColor:string =''
    async loadStyle() {
        try {
            const pref = await preferences.getPreferences(getContext(this), 'customStyle');
            const styleDataString = await pref.get('styleData', '');
            if (styleDataString) {
                const styleData = JSON.parse(styleDataString as string) as CustomStyle;
                this.autoBgColor = styleData.setBgColor;
            }
        } catch (err) {
            const error = err as BusinessError;
            console.error(`Failed to load custom style. Code is ${error.code}, message is ${error.message}`);
        }
    }
aboutToAppear(): void {
    this.loadStyle();
}

onPageShow(): void {
    this.loadStyle();
}

Enter fullscreen mode Exit fullscreen mode

Code explanation:

  • Call the loadStyle() method in the aboutToAppear and onPageShow lifecycle methods.
  • Use preferences.getPreferences() to obtain the storage instance.
  • Use the pref.get() method to read saved style data.
  • Parse the JSON string into an object and apply it to the interface.

Step 3: Implementing Theme Settings Page

3.1 Full Page Code

For ease of understanding and use, below is the full implementation of the theme settings page:


import  {autoColor,CustomStyle  }  from "../utils/types"
import { preferences } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';
import { promptAction, router, Router } from "@kit.ArkUI";

@Entry
@Component
struct Theme {
    // Define 18 different color data
    private colorData: autoColor[] = [
        { color: '#FF8C00', name: 'DarkOrange' },
        { color: '#FF6347', name: 'Tomato' },
        { color: '#32CD32', name: 'LimeGreen' },
        { color: '#1E90FF', name: 'DodgerBlue' },
        { color: '#FF1493', name: 'DeepPink' },
        { color: '#9370DB', name: 'MediumPurple' },
        { color: '#FFD700', name: 'Gold' },
        { color: '#20B2AA', name: 'LightSeaGreen' },
        { color: '#FF4500', name: 'OrangeRed' },
        { color: '#8A2BE2', name: 'BlueViolet' },
        { color: '#00CED1', name: 'DarkTurquoise' },
        { color: '#FF69B4', name: 'HotPink' },
        { color: '#7FFF00', name: 'Chartreuse' },
        { color: '#DC143C', name: 'Crimson' },
        { color: '#00BFFF', name: 'DeepSkyBlue' },
        { color: '#DA70D6', name: 'Orchid' },
        { color: '#FFA500', name: 'Orange' },
        { color: '#40E0D0', name: 'Turquoise' }
    ]
    // Define the app color
    async handleApplyBgColor(color:string){
         const autoColor:CustomStyle ={
             setBgColor:color
         }
        try {
            // Get the preference instance
            const pref = await preferences.getPreferences(getContext(this), 'customStyle');
            // Convert the style object to a string and store it
            await pref.put('styleData', JSON.stringify(autoColor));
            // Refresh data to disk
            await pref.flush();
            console.info('Custom style setting succeeded');
            this.getUIContext().getPromptAction().showToast({
                message: "Custom style setting succeeded",
                duration: 2000,
                showMode: promptAction.ToastShowMode.DEFAULT,
                bottom: 80
            });
        } catch (err) {
            const error = err as BusinessError;
            this.getUIContext().getPromptAction().showToast({
                message: "Custom style setting failed",
                duration: 2000,
                showMode: promptAction.ToastShowMode.DEFAULT,
                bottom: 80
            });
            console.error(`Setting failed ${error.code}, ${error.message}`);
        }
    }
    @Builder
    ColorCard(colorInfo:autoColor) {
        Column() {
            // Color display area
            Row()
                .width('100%')
                .height(60)
                .backgroundColor(colorInfo.color)
                .borderRadius({
                    topLeft: 8,
                    topRight: 8,
                    bottomLeft: 0,
                    bottomRight: 0
                })

            // Information display area
            Column() {
                Text(colorInfo.color)
                    .fontSize(12)
                    .fontWeight(FontWeight.Bold)
                    .fontColor('#333333')
                    .margin({ top: 6 })

                Text(colorInfo.name)
                    .fontSize(10)
                    .fontColor('#666666')
                    .margin({ top: 2, bottom: 6 })
            }
            .width('100%')
            .backgroundColor('#FFFFFF')
            .borderRadius({
                topLeft: 0,
                topRight: 0,
                bottomLeft: 8,
                bottomRight: 8
            })
        }
        .width('100%')
        .backgroundColor('#FFFFFF')
        .borderRadius(8)
        .shadow({
            radius: 4,
            color: '#1A000000',
            offsetX: 0,
            offsetY: 1
        })
        .onClick(()=>{
this.handleApplyBgColor(colorInfo.color)
        })
    }

    build() {
        Column() {
            Row(){
                 Button('Back').onClick(()=>{
                 router.replaceUrl({
                      url:'pages/Index'
                 })
                 })
            }
            Text('Color theme')
                .fontSize(20)
                .fontWeight(FontWeight.Bold)
                .margin({ top: 20, bottom: 16 })
                .fontColor('#333333')

            // Use Grid layout, 4-column display
            Grid() {
                ForEach(this.colorData, (item: autoColor, index: number) => {
                    GridItem() {
                        this.ColorCard(item)
                    }
                    .padding(4)
                })
            }
            .columnsTemplate('1fr 1fr 1fr 1fr') // 4 equally sized columns
            .rowsGap(8)
            .columnsGap(8)
            .padding({ left: 12, right: 12 })
            .layoutWeight(1)
        }
        .height('100%')
        .width('100%')
        .backgroundColor('#F8F8F8')
    }
}

Enter fullscreen mode Exit fullscreen mode

3.2 Core Feature Analysis

Theme color application function — key implementation of data persistence:

   // Define the app color
    async handleApplyBgColor(color:string){
         const autoColor:CustomStyle ={
             setBgColor:color
         }
        try {
            // Get the preference instance
            const pref = await preferences.getPreferences(getContext(this), 'customStyle');
            // Convert the style object to a string and store it
            await pref.put('styleData', JSON.stringify(autoColor));
            // Refresh data to disk
            await pref.flush();
            console.info('Custom style setting succeeded');
            this.getUIContext().getPromptAction().showToast({
                message: "Custom style setting succeeded",
                duration: 2000,
                showMode: promptAction.ToastShowMode.DEFAULT,
                bottom: 80
            });
        } catch (err) {
            const error = err as BusinessError;
            this.getUIContext().getPromptAction().showToast({
                message: "Custom style setting failed",
                duration: 2000,
                showMode: promptAction.ToastShowMode.DEFAULT,
                bottom: 80
            });
            console.error(`Setting failed ${error.code}, ${error.message}`);
        }
    }
Enter fullscreen mode Exit fullscreen mode

Key steps description:

  1. Create a data object:Encapsulate the user-selected color into a CustomStyle object
  2. Get storage instance:Get the storage instance named 'customStyle' using preferences.getPreferences()
  3. Data serialization:Use JSON.stringify() to convert objects into string format
  4. Write data:Call the pref.put() method to write data into storage
  5. Persistent storage:Use pref.flush() to ensure that data is written to disk immediately
  6. User feedback:Use a Toast message to inform users of the operation result

Key Takeaways

In simple terms, persistent storage using preferences mainly involves storing and retrieving data.

Store
           // Get the preference instance
            const pref = await preferences.getPreferences(getContext(this), 'customStyle');
            // Convert the style object to a string and store it
            await pref.put('styleData', JSON.stringify(autoColor));
            // Refresh data to disk
            await pref.flush();
Enter fullscreen mode Exit fullscreen mode

Retrieve

const pref = await preferences.getPreferences(getContext(this), 'customStyle');
            const styleDataString = await pref.get('styleData', '');
Enter fullscreen mode Exit fullscreen mode

Simple, isn't it? Once you master persistent storage, you can use it for a wide variety of features.

Summary

Through this article, we have learned the complete flow of using Preferences in HarmonyOS.

Core Points

  1. Simple and easy to use:Preferences provides a clean and lightweight key-value storage mechanism ideal for simple data persistence
  2. Lifecycle integration:Make good use of component lifecycle events to ensure timely data loading and app
  3. Error handling:Robust exception handling improves both app stability and user experience
  4. Data synchronization:Use flush() to make sure data is written to disk immediately, avoiding data loss

Practical Value

  • Enhanced user experience:Retains users' personalized settings for a consistent experience
  • Improved development efficiency:Simple APIs enable quick implementation of data persistence
  • Broad use cases:Suitable for theme customization, user preferences, app configuration, and more.

Best Practice Recommendations

  1. Wise data structures planning:Design clear and maintainable APIs for easier scalability.
  2. Asynchronous processing:Use async/await to process asynchronous operations and prevent blocking the main thread.
  3. User feedback:Always inform users of operation results to enhance the interaction experience.
  4. Error handling:Perfect error handling and logging for easy troubleshooting

We hope this article helps you quickly master the usage of Preferences and apply this powerful data storage feature flexibly in your own projects. That's all for today — class dismissed.

Written by Full-stack Developer Ruocheng

Top comments (0)