DEV Community

linzhongxue
linzhongxue

Posted on

Practical Development of Smart Real Estate and Renovation Applications Based on HarmonyOS Next

Practical Development of Smart Real Estate and Renovation Applications Based on HarmonyOS Next

1. Project Overview and Feature Planning

In today's digital real estate era, developing a comprehensive real estate and renovation application can provide users with one-stop services from property browsing to interior design. We will build such an application based on HarmonyOS Next and AppGallery Connect, featuring the following core functionalities:

  1. Property listing display and search
  2. 3D floor plan viewing
  3. Renovation design and sharing
  4. Contractor/designer matching
  5. Building materials marketplace

2. Development Environment and Project Initialization

2.1 Creating a HarmonyOS Project

Create a new project in DevEco Studio using the "Empty Ability" template with the following configuration:

// Basic configuration in entry/build-profile.json5
{
  "app": {
    "signingConfigs": [],
    "compileSdkVersion": 9,
    "compatibleSdkVersion": 9,
    "products": [
      {
        "name": "default",
        "signingConfig": "default",
        "compileSdkVersion": "9",
        "compatibleSdkVersion": "9",
        "runtimeOS": "HarmonyOS"
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

2.2 Integrating AppGallery Connect Services

Add necessary dependencies in oh-package.json5:

{
  "dependencies": {
    "@hw-agconnect/auth-ohos": "^1.8.0.300",
    "@hw-agconnect/database-ohos": "^1.8.0.300",
    "@hw-agconnect/storage-ohos": "^1.8.0.300",
    "@hw-agconnect/cloudfunctions-ohos": "^1.8.0.300"
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Property Listing Module Implementation

3.1 Property Data Model Design

// Property data model definition
class HouseInfo {
  id: string = '';           // Property ID
  title: string = '';        // Property title
  price: number = 0;         // Price (10K yuan)
  area: number = 0;          // Area (sqm)
  roomType: string = '';     // Layout type
  location: string = '';     // Location
  coverUrl: string = '';     // Cover image URL
  vrUrl?: string;            // VR viewing link
  tags: string[] = [];       // Tags
  createTime: string = '';   // Creation time
}
Enter fullscreen mode Exit fullscreen mode

3.2 Property Listing Implementation

// Property listing page
@Entry
@Component
struct HouseListPage {
  @State houseList: HouseInfo[] = []
  @State loading: boolean = true

  aboutToAppear() {
    this.loadHouseData()
  }

  // Load property data
  async loadHouseData() {
    try {
      const cloudDB = AGConnectCloudDB.getInstance()
      const query = cloudDB.createQuery(HouseInfo)
      const snapshot = await cloudDB.executeQuery(query)
      this.houseList = snapshot.getSnapshotObjects()
    } catch (error) {
      console.error('Failed to load property data:', error)
    } finally {
      this.loading = false
    }
  }

  build() {
    Column() {
      // Search bar
      SearchBar({ placeholder: 'Enter community name or area' })
        .width('90%')
        .margin(10)

      if (this.loading) {
        LoadingProgress()
          .height(100)
      } else {
        List({ space: 15 }) {
          ForEach(this.houseList, (item: HouseInfo) => {
            ListItem() {
              HouseItemCard({ houseInfo: item })
            }
          })
        }
        .width('100%')
        .layoutWeight(1)
      }
    }
  }
}

// Property card component
@Component
struct HouseItemCard {
  @Prop houseInfo: HouseInfo

  build() {
    Column() {
      // Cover image
      Image(this.houseInfo.coverUrl)
        .width('100%')
        .height(180)
        .objectFit(ImageFit.Cover)

      // Property information
      Column({ space: 5 }) {
        Text(this.houseInfo.title)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })

        Row() {
          Text(`${this.houseInfo.price}万`)
            .fontColor('#FF5722')
            .fontSize(18)

          Text(${this.houseInfo.area}㎡`)
            .margin({ left: 10 })

          Text(${this.houseInfo.roomType}`)
            .margin({ left: 10 })
        }

        Text(this.houseInfo.location)
          .fontSize(14)
          .fontColor('#666')

        // Tags
        Wrap() {
          ForEach(this.houseInfo.tags, (tag: string) => {
            Text(tag)
              .fontSize(12)
              .padding(5)
              .backgroundColor('#F5F5F5')
              .borderRadius(4)
              .margin({ right: 5, bottom: 5 })
          })
        }
        .margin({ top: 5 })
      }
      .padding(10)
    }
    .width('90%')
    .margin({ top: 5, bottom: 5 })
    .borderRadius(8)
    .backgroundColor(Color.White)
    .shadow({ radius: 4, color: '#10000000', offsetX: 1, offsetY: 1 })
  }
}
Enter fullscreen mode Exit fullscreen mode

4. 3D Floor Plan Viewing Feature

4.1 Integrating 3D Model Viewer

// 3D floor plan viewing page
@Entry
@Component
struct House3DViewPage {
  @State modelUrl: string = ''
  @State loading: boolean = true

  aboutToAppear() {
    // Get model URL from route parameters
    const params = router.getParams() as Record<string, string>
    if (params && params['modelUrl']) {
      this.modelUrl = params['modelUrl']
    }
  }

  build() {
    Stack() {
      // 3D model container
      Web({
        src: this.modelUrl,
        controller: new webview.WebviewController()
      })
      .onPageEnd(() => {
        this.loading = false
      })

      if (this.loading) {
        Column() {
          LoadingProgress()
          Text('Loading model...')
            .margin({ top: 10 })
        }
        .width('100%')
        .height('100%')
        .justifyContent(FlexAlign.Center)
      }

      // Back button
      Button()
        .width(40)
        .height(40)
        .borderRadius(20)
        .backgroundColor('#FFFFFF')
        .margin({ top: 30, left: 15 })
        .onClick(() => {
          router.back()
        })
    }
    .width('100%')
    .height('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

5. Renovation Design Module Development

5.1 Renovation Plan Data Model

// Renovation plan data model
class DecorationPlan {
  id: string = '';               // Plan ID
  houseId: string = '';          // Associated property ID
  title: string = '';            // Plan title
  style: string = '';            // Design style
  cost: number = 0;              // Budget (10K yuan)
  designer: string = '';         // Designer
  designerAvatar: string = '';   // Designer avatar
  images: string[] = [];         // Renderings
  createTime: string = '';       // Creation time
}
Enter fullscreen mode Exit fullscreen mode

5.2 Renovation Plan List and Details

// Renovation plan list page
@Entry
@Component
struct DecorationListPage {
  @State planList: DecorationPlan[] = []
  @State selectedHouse: HouseInfo | null = null

  aboutToAppear() {
    this.loadDecorationPlans()
  }

  async loadDecorationPlans() {
    try {
      const cloudDB = AGConnectCloudDB.getInstance()
      const query = cloudDB.createQuery(DecorationPlan)
      const snapshot = await cloudDB.executeQuery(query)
      this.planList = snapshot.getSnapshotObjects()
    } catch (error) {
      console.error('Failed to load renovation plans:', error)
    }
  }

  build() {
    Column() {
      // Filter options
      Row({ space: 10 }) {
        Button('All')
          .borderRadius(15)
          .fontSize(14)

        Button('Modern Minimalist')
          .borderRadius(15)
          .fontSize(14)

        Button('Nordic Style')
          .borderRadius(15)
          .fontSize(14)

        Button('New Chinese')
          .borderRadius(15)
          .fontSize(14)
      }
      .width('90%')
      .margin({ top: 10, bottom: 10 })

      // Plan list
      Grid() {
        ForEach(this.planList, (item: DecorationPlan) => {
          GridItem() {
            DecorationPlanCard({ plan: item })
          }
        })
      }
      .columnsTemplate('1fr 1fr')
      .rowsTemplate('auto')
      .columnsGap(10)
      .rowsGap(10)
      .width('90%')
      .height('100%')
    }
  }
}

// Renovation plan card
@Component
struct DecorationPlanCard {
  @Prop plan: DecorationPlan

  build() {
    Column() {
      // Rendering cover
      Stack() {
        Image(this.plan.images[0])
          .width('100%')
          .height(120)
          .objectFit(ImageFit.Cover)

        // Designer information
        Row() {
          Image(this.plan.designerAvatar)
            .width(24)
            .height(24)
            .borderRadius(12)

          Text(this.plan.designer)
            .fontSize(12)
            .margin({ left: 5 })
        }
        .padding(5)
        .backgroundColor('#80000000')
        .borderRadius(12)
        .position({ x: 5, y: 5 })
      }

      // Plan information
      Column({ space: 3 }) {
        Text(this.plan.title)
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
          .maxLines(1)

        Text(this.plan.style)
          .fontSize(12)
          .fontColor('#666')

        Text(`Budget: ${this.plan.cost}万`)
          .fontSize(12)
          .fontColor('#FF5722')
      }
      .padding(8)
      .width('100%')
    }
    .borderRadius(8)
    .backgroundColor(Color.White)
    .shadow({ radius: 2, color: '#10000000', offsetX: 1, offsetY: 1 })
  }
}
Enter fullscreen mode Exit fullscreen mode

6. Building Materials Marketplace Module Implementation

6.1 Building Material Product Data Model

// Building material product model
class BuildingMaterial {
  id: string = '';           // Product ID
  name: string = '';         // Product name
  category: string = '';     // Category
  brand: string = '';        // Brand
  price: number = 0;         // Price
  unit: string = '';         // Unit
  coverUrl: string = '';     // Cover image
  specs: string[] = [];      // Specifications
  details: string = '';      // Description
}
Enter fullscreen mode Exit fullscreen mode

6.2 Product List and Detail Page

// Building materials product list page
@Entry
@Component
struct MaterialListPage {
  @State materialList: BuildingMaterial[] = []
  @State activeCategory: string = 'All'

  aboutToAppear() {
    this.loadMaterials()
  }

  async loadMaterials() {
    try {
      const cloudDB = AGConnectCloudDB.getInstance()
      let query = cloudDB.createQuery(BuildingMaterial)

      if (this.activeCategory !== 'All') {
        query = cloudDB.createQuery(
          BuildingMaterial,
          `category == '${this.activeCategory}'`
        )
      }

      const snapshot = await cloudDB.executeQuery(query)
      this.materialList = snapshot.getSnapshotObjects()
    } catch (error) {
      console.error('Failed to load building materials:', error)
    }
  }

  build() {
    Column() {
      // Category filter
      Scroll() {
        Row({ space: 10 }) {
          ForEach(['All', 'Flooring', 'Tiles', 'Bathroom', 'Lighting', 'Paint'], (item: string) => {
            Button(item)
              .borderRadius(15)
              .fontSize(14)
              .backgroundColor(this.activeCategory === item ? '#FF5722' : '#F5F5F5')
              .fontColor(this.activeCategory === item ? Color.White : '#333')
              .onClick(() => {
                this.activeCategory = item
                this.loadMaterials()
              })
          })
        }
        .padding(10)
      }
      .width('100%')
      .height(50)

      // Product list
      List({ space: 15 }) {
        ForEach(this.materialList, (item: BuildingMaterial) => {
          ListItem() {
            MaterialItemCard({ material: item })
          }
        })
      }
      .width('100%')
      .layoutWeight(1)
    }
  }
}

// Product card component
@Component
struct MaterialItemCard {
  @Prop material: BuildingMaterial

  build() {
    Row({ space: 10 }) {
      Image(this.material.coverUrl)
        .width(100)
        .height(100)
        .borderRadius(4)
        .objectFit(ImageFit.Cover)

      Column({ space: 5 }) {
        Text(this.material.name)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .maxLines(1)

        Text(this.material.brand)
          .fontSize(14)
          .fontColor('#666')

        Row() {
          Text(${this.material.price}`)
            .fontColor('#FF5722')
            .fontSize(18)

          Text(`/${this.material.unit}`)
            .fontSize(14)
            .fontColor('#999')
            .margin({ left: 2 })
        }

        // Specification tags
        Wrap() {
          ForEach(this.material.specs, (spec: string) => {
            Text(spec)
              .fontSize(12)
              .padding(3)
              .backgroundColor('#F5F5F5')
              .borderRadius(2)
              .margin({ right: 5, bottom: 5 })
          })
        }
      }
      .layoutWeight(1)
    }
    .padding(10)
    .width('90%')
    .backgroundColor(Color.White)
    .borderRadius(8)
    .margin({ top: 5, bottom: 5 })
  }
}
Enter fullscreen mode Exit fullscreen mode

7. Application Publishing and Operations

7.1 Application Packaging and Release

After completing development, follow these steps to publish the application:

  1. Configure application signing in DevEco Studio
  2. Execute Build > Build HAP(s)/APP(s) to generate the release package
  3. Log in to AppGallery Connect to submit the application for review
  4. Configure application metadata, screenshots, and promotional videos

7.2 Integrating Analytics

// Integrate analytics service at application entry
import { AGConnectAnalytics } from '@hw-agconnect/analytics-ohos';

export default class AppAnalytics {
  static init() {
    AGConnectAnalytics.getInstance().setAnalyticsEnabled(true)
    // Set user attributes
    AGConnectAnalytics.getInstance().setUserProfile('user_type', 'normal')
  }

  // Record page views
  static trackPageView(pageName: string) {
    AGConnectAnalytics.getInstance().onEvent('page_view', { page: pageName })
  }

  // Record user actions
  static trackUserAction(action: string, params?: Record<string, string>) {
    AGConnectAnalytics.getInstance().onEvent(action, params)
  }
}

// Call in page lifecycle
AppAnalytics.init()
AppAnalytics.trackPageView('HomePage')
Enter fullscreen mode Exit fullscreen mode

Through this comprehensive development process, we've implemented a fully functional real estate and renovation application. From property display to 3D viewing, from renovation design to building material selection, this solution covers the core scenarios of real estate renovation. The combination of HarmonyOS Next's distributed capabilities and AppGallery Connect's backend services provides developers with powerful technical support.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.