Building a Children's Flag Learning App with HarmonyOS: Core Speech Kit & ArkTS Deep Dive
A HarmonyOS developer shares the technical journey of building 'Shengqu Guoqitong', a children's flag learning app, detailing why HarmonyOS was chosen over Android, Flutter, and React Native, and how Core Speech Kit's offline TTS/ASR and ArkTS declarative UI enabled efficient development of voice-driven, interactive educational features.
Project Demo
The article showcases a HarmonyOS app called "Shengqu Guoqitong" (声趣国旗通) designed for children aged 5-8 to learn national flags through voice interaction, drawing, and puzzle games.
Introduction: From Pain Points to Solution
Existing flag-learning apps rely on static text and images, creating high cognitive barriers for young children with limited literacy. The author chose HarmonyOS to build an app centered on three principles: cartoonish, ultra-simple, and highly interactive. The development leveraged HarmonyOS Core Speech Kit and ArkTS declarative UI.
Technology Selection: Why HarmonyOS?
Horizontal Comparison: Mainstream Solutions Analysis
The author spent a week comparing four mobile development options across speech capability, development efficiency, performance, ecosystem maturity, and overall score:
HarmonyOS : Speech ⭐⭐⭐⭐⭐ (native, offline), Development ⭐⭐⭐⭐⭐ (ArkTS declarative UI), Performance ⭐⭐⭐⭐⭐ (native), Ecosystem ⭐⭐⭐⭐ (rapidly growing), Score 95
Android (Kotlin) : Speech ⭐⭐⭐ (requires third-party SDK), Development ⭐⭐⭐⭐ (Jetpack Compose), Performance ⭐⭐⭐⭐⭐ (mature), Ecosystem ⭐⭐⭐⭐⭐ (complete), Score 85
Flutter : Speech ⭐⭐ (plugin-dependent), Development ⭐⭐⭐⭐⭐ (hot reload), Performance ⭐⭐⭐⭐ (near-native), Ecosystem ⭐⭐⭐⭐ (active), Score 75
React Native : Speech ⭐⭐ (third-party plugins), Development ⭐⭐⭐ (moderate learning curve), Performance ⭐⭐⭐ (JS bridge overhead), Ecosystem ⭐⭐⭐⭐ (mature), Score 65
HarmonyOS was selected for five core reasons detailed below.
1. Core Speech Kit: The Killer Feature for Children's Education
Why Speech Capability Matters
For 5-8 year olds, voice interaction is not just convenient but necessary to lower cognitive barriers:
Limited literacy : Children know 500-1500 characters; complex text UIs hinder experience.
Active verbal expression : Kids prefer speaking over typing; voice matches cognitive habits.
Immersive learning : Multi-sensory audio-visual stimulation boosts memory retention by 60%+ (educational psychology research).
HarmonyOS Core Speech Kit Technical Advantages
① True Offline Capability, Zero Network Dependency
Traditional third-party SDKs (iFlytek, Baidu) require cloud API calls, 200-500ms latency, per-call costs (~¥0.002), and upload privacy data. Core Speech Kit runs fully offline, <50ms latency, zero cost, no call limits, and processes data locally complying with Children's Personal Information Protection Regulations. Real-world test: all voice features work in subway/airplane mode.
② Mandarin Recognition Accuracy 95%+
A blogger's experiment with 10 children (6-8 years) speaking 20 country names showed:
HarmonyOS ASR: 95.5% accuracy, ✅ specifically optimized for child voice, ✅ good dialect tolerance
iFlytek: 92.3%, ✅ supported, ⭐ excellent dialect tolerance
Baidu Speech: 89.7%, ⚠️ average child adaptation, ✅ good dialect tolerance
Google Speech: 76.2%, ❌ not adapted for Chinese children, ❌ poor dialect tolerance
③ Flexible Pause Control and Speed Adjustment
Core Speech Kit supports SSML markup for precise rhythm control:
// Smart pauses give children time to understand
const text = `
中国的首都是北京[p500]
货币是人民币[p500]
语言是普通话[p1000]
你记住了吗?
`;
TextToSpeechManager.getInstance().speak(text);Traditional TTS often reads in one breath, leaving children unable to keep up.
④ Multi-Voice Support for Affinity
Six voices available (standard male, standard female, child voice, etc.). The author chose standard female (person: 0) + interactive broadcast style (style: 'interaction-broadcast'), which tested highest acceptance among children.
2. ArkTS + ArkUI: Triple Development Efficiency
Revolutionary Declarative UI Experience
The author implemented the same continent card (rounded corners, shadow, gradient background, click animation) in three frameworks:
HarmonyOS ArkTS: 15 lines, single file
// HarmonyOS ArkTS: 15 lines
Column() {
Text('亚洲')
.fontSize(18)
.fontWeight(FontWeight.Bold)
Text('48个国家')
.fontSize(14)
.fontColor('#666')
}
.width('100%')
.padding(16)
.backgroundColor('#FF6B6B')
.borderRadius(16)
.shadow({ radius: 8, color: '#1A000000', offsetY: 2 })
.onClick(() => this.navigateToContinentList('亚洲'))Android XML + Kotlin: 35 lines, needs XML + Kotlin + resource files
<!-- Android XML + Kotlin: 35 lines -->
<!-- activity_card.xml -->
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="8dp">
<LinearLayout
android:orientation="vertical"
android:padding="16dp"
android:background="@drawable/gradient_bg">
<TextView
android:text="亚洲"
android:textSize="18sp"
android:textStyle="bold"/>
<TextView
android:text="48个国家"
android:textSize="14sp"
android:textColor="#666666"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Also need gradient_bg.xml, click listeners, etc. -->Code Quantity Comparison:
HarmonyOS ArkTS : 15 lines, one file
Android Traditional : 35 lines, XML + Kotlin + resources
Flutter : 25 lines, relatively clean but deep nesting
Elegant State Management
ArkTS provides @State, @Link, @StorageLink decorators for reactive state management:
// Volume setting auto-syncs to all pages
@StorageLink('volume') volume: number = 50;
Slider({ value: this.volume })
.onChange((value) => {
this.volume = value; // Auto-triggers updates in all components using this state
})This approach is 50%+ more concise than Redux/Vuex with near-zero learning cost.
3. Stage Model: True "Write Once, Deploy Multi-Device"
Native Multi-Device Form Factor Support
HarmonyOS Stage Model supports multiple device types at architecture level, not just responsive layout:
// module.json5 configuration
{
"deviceTypes": [
"phone", // Phone
"tablet", // Tablet
"2in1", // Foldable
"tv" // Smart Screen
]
}The author prepared responsive grid layout for future multi-device adaptation:
// Responsive grid layout
Grid() {
ForEach(this.continents, (item) => {
GridItem() {
ContinentCard(item)
}
})
}
.columnsTemplate('1fr 1fr') // Phone: 2 columns
// Future tablet can auto-switch: .columnsTemplate('1fr 1fr 1fr') // Tablet: 3 columnsDevice Collaboration Potential
Though current version focuses on phone, Stage Model reserves interfaces for future distributed capabilities:
Phone: Child learns flags on phone
↓ One-click flow
Smart Screen: Auto-cast to living room TV, whole family learns together
↓ Learning data sync
Tablet: Continue on tablet, seamless progressSuch device collaboration is difficult in iOS/Android ecosystems.
4. DevEco Studio Toolchain: Thoughtful Experience
Intelligent Code Completion
ArkTS code completion accuracy >90%, supporting component property hints, quick Kit module imports, real-time syntax checking. Example: typing Text() triggers auto-suggestions for all properties sorted by usage frequency, drastically reducing documentation lookup time.
Previewer Real-Time Rendering
Previewer enables real-time UI preview without running simulator:
@Preview
@Component
struct ContinentCardPreview {
build() {
ContinentCard('亚洲', '48个国家', $r('app.color.continent_asia'))
}
}UI updates within 0.5 seconds after save, 10x+ faster than traditional compile-install-run cycle.
5. Ecosystem Dividends: Catching HarmonyOS Explosion Window
Market Opportunity
Official Huawei data (2024): HarmonyOS devices >800M, developers >2.54M (rapid growth), ecosystem covering 18 domains. Early entrants enjoy:
✅ More app market exposure
✅ Strong official support (tech support, traffic bias)
✅ Less competition (scarce quality children's education apps)
Technical Forward-Looking
HarmonyOS has shed AOSP legacy: deeper system optimization, unified design language, long-term evolution roadmap. Choosing HarmonyOS means growing with China's independent OS.
Final Decision: Balance of Technical Ideal and Commercial Reality
Weighted analysis across five dimensions:
Speech Capability (⭐⭐⭐⭐⭐ Core Competitiveness): Native support, best experience → ✅ Decisive Advantage
Development Efficiency (⭐⭐⭐⭐⭐ Impacts Launch Time): 65% Efficiency Gain → ✅ Significant Advantage
Performance Experience (⭐⭐⭐⭐ User Retention Key): Native performance, silky smooth → ✅ Clear Advantage
Multi-Device Extension (⭐⭐⭐ Long-term Value): Stage Model Native Support → ✅ Promising Future
Ecosystem Maturity (⭐⭐⭐ Short-term Risk): Rapidly Growing → ⚠️ Acceptable
Result: Completed in 2 weeks what would have taken 6 weeks.
Core Feature Implementation: Zero-to-One Technical Breakthroughs
Feature 1: Voice Broadcast — Making Flags "Speak"
Challenge: How to Help Young Children Quickly Understand Flag Info?
Traditional text display has high cognitive barrier for 5-8 year olds. Need more intuitive approach.
Solution: Deep Integration of Core Speech Kit TTS
Encapsulated a TextToSpeechManager singleton class. Core code:
// TextToSpeechManager.ets (simplified)
import { textToSpeech } from '@kit.CoreSpeechKit';
export class TextToSpeechManager {
private static instance: TextToSpeechManager;
private ttsEngine: textToSpeech.TextToSpeechEngine | null = null;
// Create TTS Engine
createEngine() {
let initParamsInfo: textToSpeech.CreateEngineParams = {
language: 'zh-CN',
person: 0, // Voice: Standard Female
online: 1, // Offline Mode
extraParams: {
"style": "interaction-broadcast", // Interactive Broadcast Style
"locate": "CN"
}
};
textToSpeech.createEngine(initParamsInfo, (err, engine) => {
if (!err) {
this.ttsEngine = engine;
console.info('TTS Engine Created Successfully');
}
});
}
// Speak Text
speak(text: string) {
// Stop current broadcast before starting new
if (this.ttsEngine?.isBusy()) {
this.ttsEngine?.stop();
}
const speakParams = {
requestId: new Date().getTime().toString(),
extraParams: {
"speed": AppModel.speed, // User-adjustable speed
"volume": AppModel.volume, // User-adjustable volume
"pitch": 1
}
};
this.ttsEngine?.speak(text, speakParams);
}
}Technical Highlights
Smart Pause Control : Used [p500] markers for pause control on flag detail page.
const text = `
${country.name}的首都是${country.capital}[p500]货币是${country.currency}[p500]语言是${country.language}
`;
TextToSpeechManager.getInstance().speak(text);Makes speech more natural, easier for kids to understand.
State Management : isBusy() checks engine status to avoid multiple audio stream conflicts.
User Configurable : Settings page provides volume and speed sliders.
@Builder
SettingsSlider() {
Row() {
Text('音量:')
Slider({ value: this.volume, min: 0, max: 100 })
.onChange((value) => {
AppModel.volume = value;
})
}
}Feature 2: Speech Recognition — Let Kids "Speak" Answers
Challenge: How to Verify Children Truly Understand Flag Info?
Traditional multiple-choice or fill-in-blank are operationally complex for young kids. Need more natural interaction.
Solution: Voice Q&A Based on Core Speech Kit ASR
In "Flag Challenge" feature, implemented press-to-speak, release-to-recognize interaction:
// TestDetailPage.ets (Core Logic)
Button() {
Image($r('app.media.ic_microphone'))
.height(36)
.width(36)
}
.onTouch(async (event: TouchEvent) => {
if (event.type == TouchType.Down) {
// 🎤 Press: Start Recording Recognition
await this.requestPermission(async (isAuth) => {
if (isAuth) {
await SpeechRecognizerManager.getInstance().startRecording();
} else {
this.showPermissionAlert(); // Guide user to authorize
}
});
} else if (event.type == TouchType.Up) {
// 🛑 Release: Stop Recording, Analyze Result
SpeechRecognizerManager.getInstance().finishRecognizer();
this.checkAnswer();
}
})Speech Recognition Core Implementation
Encapsulated SpeechRecognizerManager:
export class SpeechRecognizerManager {
private asrEngine: speechRecognizer.SpeechRecognitionEngine | null = null;
// Create ASR Engine
createEngine() {
let initParamsInfo: speechRecognizer.CreateEngineParams = {
language: 'zh-CN',
online: 1, // Offline Mode
extraParams: {
"recognizerMode": "short" // Short Speech Mode (<60s)
}
};
speechRecognizer.createEngine(initParamsInfo, (err, engine) => {
if (!err) {
this.asrEngine = engine;
}
});
}
// Initialize Listener
initListener(onResult?: (result: string) => void) {
const listener = {
// Recognition Result Callback
onResult(sessionId: string, result: speechRecognizer.SpeechRecognitionResult) {
// Remove punctuation for matching
const cleanText = result.result.replace(/[\p{P}\p{S}]/gu, '');
if (onResult) onResult(cleanText);
},
// Recognition Complete Callback
onComplete(sessionId: string) {
console.info('Recognition Complete');
},
// Error Callback
onError(sessionId: string, errorCode: number, errorMessage: string) {
console.error(`Recognition Failed: ${errorMessage}`);
}
};
this.asrEngine?.setListener(listener);
}
}Technical Highlights
Permission Management Optimization : Dynamic microphone permission request via abilityAccessCtrl with friendly guide dialog:
async requestPermission() {
const atManager = abilityAccessCtrl.createAtManager();
const result = await atManager.requestPermissionsFromUser(
this.getUIContext().getHostContext(),
["ohos.permission.MICROPHONE"]
);
if (result.authResults[0] !== 0) {
// User denied, show guide dialog
this.showPermissionGuideDialog();
}
}Smart Answer Matching : After removing punctuation, use includes() for fuzzy matching:
handlerAnswer() {
const isCorrect = this.yourAnswer.includes(this.country.name);
const result = isCorrect ? '恭喜您,答对了' : '答错了,再想想';
// Voice + Text Dual Feedback
SilToast.showToast(this.getUIContext(), result);
TextToSpeechManager.getInstance().speak(result);
}Audio Stream Processing : AudioCapturer captures mic stream in real-time, writes to ASR engine:
this.mAudioCapturer.init((dataBuffer: ArrayBuffer) => {
let uint8Array = new Uint8Array(dataBuffer);
this.asrEngine?.writeAudio(this.sessionId, uint8Array);
});Feature 3: Canvas Drawing — Remember Flags by "Hand"
Challenge: How to Deepen Children's Memory of Flags?
Psychology research shows hands-on practice yields 3x+ better memory retention than passive viewing.
Solution: Flag Drawing Based on ArkTS Canvas
// FlagDrawingPage.ets (Core Drawing Logic)
Canvas(this.context)
.width('100%')
.height('100%')
.onTouch((event) => {
if (event.type === TouchType.Down) {
this.x = event.touches[0].x;
this.y = event.touches[0].y;
this.context.beginPath();
this.tempPath = new Path2D();
this.tempPath.moveTo(this.x, this.y);
}
if (event.type === TouchType.Move) {
if (this.isEraserMode) {
// Eraser Mode
this.context.clearRect(event.touches[0].x, event.touches[0].y, 20, 20);
} else {
// Drawing Mode
this.context.strokeStyle = this.penColorList[this.penColor];
this.tempPath.lineTo(event.touches[0].x, event.touches[0].y);
this.context.stroke(this.tempPath);
}
}
})Technical Highlights
Real-Time Rendering Optimization : Path2D object caches path, reduces redraws: private tempPath: Path2D = new Path2D(); Shape Tool Support : Basic shapes (rectangle, circle, triangle):
// Draw Circle
if (this.shapeNum === 1) {
let radius = Math.sqrt((x - x0) ** 2 + (y - y0) ** 2);
this.context.arc(x0, y0, radius, 0, 360);
this.context.fillStyle = this.penColor;
this.context.fill();
}Reference Image Feature : Displays target flag above canvas to lower difficulty:
Column() {
Image(this.flagSrc)
.height('20%')
.padding(16)
}Feature 4: Flag Klotski — Fun Puzzle Game
Challenge: How to Keep Learning Process Engaging?
Pure memorization bores kids. Need gamification.
Solution: Configurable Puzzle System Based on ArkTS
// PinTuPage.ets (Configuration-Driven Design)
@StorageLink('GameConfig') gameConfig: FigMessage = {
w: 1000, // Image Width
h: 500, // Image Height
rows: 2, // Rows
cols: 3, // Columns
isOver: false, // Completion Flag
imgUrl: $rawfile('images/guoqi/az.png')
};Technical Highlights
Dynamic Difficulty Adjustment : User selects 2x2, 3x3, etc.:
Grid() {
ForEach(this.pieces, (piece: Piece) => {
GridItem() {
Image(piece.imgSrc)
.width(this.pieceWidth)
.height(this.pieceHeight)
}
.onClick(() => this.movePiece(piece))
})
}
.columnsTemplate(this.gameConfig.cols.toString())
.rowsTemplate(this.gameConfig.rows.toString())Shuffle Algorithm : Fisher-Yates ensures solvable initial state:
shuffle() {
// Fisher-Yates Shuffle
for (let i = this.pieces.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[this.pieces[i], this.pieces[j]] = [this.pieces[j], this.pieces[i]];
}
}Win Detection : Real-time check if puzzle complete:
checkWin() {
return this.pieces.every((piece, index) =>
piece.currentIndex === piece.correctIndex
);
}Data Management: Clever Use of AppStorage for State Sharing
Challenge: How to Share User Settings (Volume, Speed) and App State Across Multiple Pages?
HarmonyOS AppStorage solves this perfectly:
// AppModel.ets (Global State Management)
export class AppModel {
static volume: number = 50; // Volume
static speed: number = 1.0; // Speed
static firstBroadcast: boolean = true; // First Broadcast Flag
static currentIndex: number = 0; // Current Tab Index
static tabsController = new TabsController();
}
// Usage in Component
@StorageLink('volume') volume: number = 50;
Slider({ value: this.volume })
.onChange((value) => {
AppModel.volume = value; // Global Effect
})Why Not LocalStorage?
AppStorage is global singleton, suitable for app-level state.
LocalStorage suits page-level state isolation. PersistentStorage suits data needing persistence (e.g., favorites list).
@StorageLink achieves two-way binding; changes sync immediately to all pages.
Performance Optimization: Smooth Display of 200+ Flag Data
App contains 201 countries' flags and details. Optimization strategies:
1. Data Lazy Loading
// Load Only Current Continent's Countries
export function getCountriesByContinent(continent: string): Country[] {
return COUNTRIES.filter(country => country.continent === continent);
}2. Virtual List Rendering
List() {
LazyForEach(this.dataSource, (country: Country) => {
ListItem() {
CountryCard(country)
}
})
}
.cachedCount(5) // Cache 5 Items3. Image Resource Optimization
Use $rawfile to load local resources, avoiding network requests.
Flag images uniformly compressed under 50KB.
Use .objectFit(ImageFit.ScaleDown) to maintain aspect ratio.
Development Insights: Three HarmonyOS Surprises
1. Core Speech Kit Is Truly Powerful
Previously integrating voice required third-party SDKs — complex config, high cost. Now HarmonyOS native offline voice capability eliminates those pains. Mandarin recognition accuracy >95% (real-tested), fully meeting children's education needs.
2. ArkTS Declarative UI Doubles Development Efficiency
Example: Card with rounded corners and shadow in ArkTS:
Column() {
Text('Card Content')
}
.borderRadius(16)
.shadow({ radius: 8, color: '#1A000000' })Traditional XML+Code would need at least 10 lines.
3. Stage Model Lifecycle Management Is Clearer
aboutToAppear()and aboutToDisappear() simplify resource management:
aboutToAppear() {
TextToSpeechManager.getInstance().createEngine();
}
aboutToDisappear() {
TextToSpeechManager.getInstance().stop(); // Avoid Memory Leak
}Future Outlook: Embracing HarmonyOS Ecosystem's Infinite Possibilities
Core features done, but just the beginning. Planned for future versions:
Multi-Modal Interaction : Leverage HarmonyOS device collaboration for phone+tablet dual-screen learning.
AI Capability Upgrade : Integrate HarmonyOS AI Kit for personalized learning recommendations.
Distributed Data Sync : Auto-sync learning progress across devices.
Accessibility Optimization : Deep adapt HarmonyOS accessibility framework for special-needs children.
Conclusion: Technology's Temperature Lies in Serving Every Person
As a developer, I believe good technology should serve people. "Shengqu Guoqitong" is not just a technical practice, but my reflection on "How to Make Children's Education Better with Tech". HarmonyOS's powerful capabilities turned this idea into reality. I truly felt the vitality and potential of the HarmonyOS ecosystem.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
51CTO HarmonyOS Developer Community
The HarmonyOS Developer Community is a learning-oriented community for developers to learn, communicate, ask questions, and share.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
