Flutter for OpenHarmony:从零到一:构建购物APP的骨架与精美UI
引言
在移动互联网时代,一个直观、流畅且美观的用户界面(UI)是任何成功应用的敲门砖。对于开发者而言,如何高效地构建出符合设计规范的UI,是日常工作的核心挑战之一。Flutter,凭借其声明式UI框架和丰富的Material
Design组件库,为我们提供了一个近乎完美的解决方案。
本文将带你从零开始,手把手构建一个名为"淘淘购物"的APP。我们的目标不仅仅是让它能跑起来,更要让它看起来专业、用起来顺手。我们将聚焦于应用的基础骨架------底部导航栏,以及两个关键页面:"首页"和"我的"。通过这个过程,你将深入理解
Scaffold、AppBar、ListView、GridView等核心Widget的组合使用技巧,并掌握如何构建复杂的、响应式的布局。
完整效果展示

完整代码展示
dart
import 'package:flutter/material.dart';
void main() {
runApp(const ShoppingApp());
}
class ShoppingApp extends StatelessWidget {
const ShoppingApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '淘淘购物',
theme: ThemeData(
primaryColor: Colors.orange,
scaffoldBackgroundColor: Colors.grey[100],
useMaterial3: true,
),
home: const MainScreen(),
debugShowCheckedModeBanner: false,
);
}
}
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int _currentIndex = 0;
final List<Widget> _screens = [
const HomeScreen(),
const CategoryScreen(),
const CartScreen(),
const ProfileScreen(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: _screens[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.orange,
unselectedItemColor: Colors.grey,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(Icons.category),
label: '分类',
),
BottomNavigationBarItem(
icon: Icon(Icons.shopping_cart),
label: '购物车',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: '我的',
),
],
),
);
}
}
// 首页
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('淘淘购物'),
backgroundColor: Colors.orange,
elevation: 0,
actions: [
IconButton(
icon: const Icon(Icons.search),
onPressed: () {},
),
IconButton(
icon: const Icon(Icons.message),
onPressed: () {},
),
],
),
body: ListView(
children: [
// 搜索框
Container(
padding: const EdgeInsets.all(10),
color: Colors.orange,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 15),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: const TextField(
decoration: InputDecoration(
hintText: '搜索宝贝',
border: InputBorder.none,
prefixIcon: Icon(Icons.search, color: Colors.grey),
),
),
),
),
// 轮播图区域
Container(
height: 150,
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.orange[300],
borderRadius: BorderRadius.circular(10),
),
child: const Center(
child: Text(
'轮播图区域',
style: TextStyle(color: Colors.white, fontSize: 20),
),
),
),
// 快捷入口
Container(
padding: const EdgeInsets.all(15),
color: Colors.white,
child: GridView.count(
crossAxisCount: 5,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: const [
QuickEntry(Icons.shopping_bag, '天猫'),
QuickEntry(Icons.card_giftcard, '聚划算'),
QuickEntry(Icons.local_offer, '优惠券'),
QuickEntry(Icons.phone_android, '数码'),
QuickEntry(Icons.style, '服饰'),
QuickEntry(Icons.home, '家居'),
QuickEntry(Icons.restaurant, '美食'),
QuickEntry(Icons.flight, '旅行'),
QuickEntry(Icons.sports_basketball, '运动'),
QuickEntry(Icons.book, '图书'),
],
),
),
const SizedBox(height: 10),
// 推荐商品
Container(
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.all(15),
child: Text(
'为你推荐',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.all(10),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.75,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: _recommendedProducts.length,
itemBuilder: (context, index) {
return ProductCard(product: _recommendedProducts[index]);
},
),
],
),
),
],
),
);
}
}
// 快捷入口组件
class QuickEntry extends StatelessWidget {
final IconData icon;
final String label;
const QuickEntry(this.icon, this.label, {super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
Icon(icon, color: Colors.orange, size: 30),
const SizedBox(height: 5),
Text(label, style: const TextStyle(fontSize: 12)),
],
);
}
}
// 分类页
class CategoryScreen extends StatelessWidget {
const CategoryScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('商品分类'),
backgroundColor: Colors.orange,
),
body: Row(
children: [
// 左侧分类列表
Container(
width: 100,
color: Colors.grey[200],
child: ListView.builder(
itemCount: _categories.length,
itemBuilder: (context, index) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 20),
decoration: BoxDecoration(
color: index == 0 ? Colors.white : null,
border: index == 0
? const Border(
left: BorderSide(color: Colors.orange, width: 3))
: null,
),
child: Center(
child: Text(
_categories[index],
style: TextStyle(
color: index == 0 ? Colors.orange : Colors.black87,
fontWeight:
index == 0 ? FontWeight.bold : FontWeight.normal,
),
),
),
);
},
),
),
// 右侧商品列表
Expanded(
child: GridView.builder(
padding: const EdgeInsets.all(10),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 0.8,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: _subCategories.length,
itemBuilder: (context, index) {
return SubCategoryCard(category: _subCategories[index]);
},
),
),
],
),
);
}
}
// 子分类卡片
class SubCategoryCard extends StatelessWidget {
final String category;
const SubCategoryCard({super.key, required this.category});
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.orange[100],
borderRadius: BorderRadius.circular(10),
),
child: const Icon(Icons.image, color: Colors.orange, size: 30),
),
const SizedBox(height: 8),
Text(
category,
style: const TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
],
);
}
}
// 购物车页
class CartScreen extends StatefulWidget {
const CartScreen({super.key});
@override
State<CartScreen> createState() => _CartScreenState();
}
class _CartScreenState extends State<CartScreen> {
double _totalPrice = 0.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('购物车'),
backgroundColor: Colors.orange,
),
body: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: _cartItems.length,
itemBuilder: (context, index) {
return CartItemCard(
item: _cartItems[index],
onQuantityChanged: (quantity) {
setState(() {
_totalPrice = _calculateTotal();
});
},
);
},
),
),
// 底部结算栏
Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.3),
blurRadius: 5,
offset: const Offset(0, -2),
),
],
),
child: Row(
children: [
const Text(
'合计: ',
style: TextStyle(fontSize: 16),
),
Text(
'¥$_totalPrice',
style: const TextStyle(
fontSize: 24,
color: Colors.orange,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
padding: const EdgeInsets.symmetric(
horizontal: 40,
vertical: 12,
),
),
child: const Text('结算', style: TextStyle(fontSize: 16)),
),
],
),
),
],
),
);
}
double _calculateTotal() {
return _cartItems.fold(
0.0, (sum, item) => sum + item.price * item.quantity);
}
}
// 我的页面
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('我的淘淘'),
backgroundColor: Colors.orange,
),
body: ListView(
children: [
// 用户信息
Container(
padding: const EdgeInsets.all(20),
color: Colors.orange,
child: Column(
children: [
Container(
width: 80,
height: 80,
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child:
const Icon(Icons.person, size: 50, color: Colors.orange),
),
const SizedBox(height: 10),
const Text(
'用户昵称',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 5),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(15),
),
child: const Text(
'登录/注册',
style: TextStyle(color: Colors.white),
),
),
],
),
),
const SizedBox(height: 10),
// 订单状态
Container(
padding: const EdgeInsets.all(15),
color: Colors.white,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'我的订单',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Row(
children: const [
Text('全部订单', style: TextStyle(color: Colors.grey)),
Icon(Icons.chevron_right, color: Colors.grey),
],
),
],
),
const SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const [
OrderStatusIcon(Icons.payment, '待付款'),
OrderStatusIcon(Icons.inventory, '待发货'),
OrderStatusIcon(Icons.local_shipping, '待收货'),
OrderStatusIcon(Icons.star, '待评价'),
OrderStatusIcon(Icons.replay, '退款/售后'),
],
),
],
),
),
const SizedBox(height: 10),
// 功能列表
Container(
color: Colors.white,
child: Column(
children: const [
ListTile(
leading: Icon(Icons.favorite_border, color: Colors.orange),
title: Text('我的收藏'),
trailing: Icon(Icons.chevron_right),
),
Divider(height: 1),
ListTile(
leading: Icon(Icons.location_on, color: Colors.orange),
title: Text('收货地址'),
trailing: Icon(Icons.chevron_right),
),
Divider(height: 1),
ListTile(
leading: Icon(Icons.help_outline, color: Colors.orange),
title: Text('联系客服'),
trailing: Icon(Icons.chevron_right),
),
Divider(height: 1),
ListTile(
leading: Icon(Icons.settings, color: Colors.orange),
title: Text('设置'),
trailing: Icon(Icons.chevron_right),
),
],
),
),
],
),
);
}
}
// 订单状态图标
class OrderStatusIcon extends StatelessWidget {
final IconData icon;
final String label;
const OrderStatusIcon(this.icon, this.label, {super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
Icon(icon, color: Colors.orange, size: 28),
const SizedBox(height: 5),
Text(label, style: const TextStyle(fontSize: 12)),
],
);
}
}
// 商品卡片
class ProductCard extends StatelessWidget {
final Product product;
const ProductCard({super.key, required this.product});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
blurRadius: 5,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.orange[100],
borderRadius:
const BorderRadius.vertical(top: Radius.circular(10)),
),
child: const Center(
child: Icon(Icons.image, color: Colors.orange, size: 50),
),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: const TextStyle(fontSize: 14),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 5),
Row(
children: [
Text(
'¥${product.price.toStringAsFixed(2)}',
style: const TextStyle(
color: Colors.orange,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 5),
Text(
'${product.sales}人付款',
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
],
),
],
),
),
],
),
);
}
}
// 购物车商品卡片
class CartItemCard extends StatefulWidget {
final CartItem item;
final Function(int) onQuantityChanged;
const CartItemCard({
super.key,
required this.item,
required this.onQuantityChanged,
});
@override
State<CartItemCard> createState() => _CartItemCardState();
}
class _CartItemCardState extends State<CartItemCard> {
bool _isChecked = true;
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(10),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
blurRadius: 5,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
Checkbox(
value: _isChecked,
onChanged: (value) {
setState(() {
_isChecked = value ?? false;
});
},
),
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.orange[100],
borderRadius: BorderRadius.circular(5),
),
child: const Icon(Icons.image, color: Colors.orange),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.item.name,
style: const TextStyle(fontSize: 14),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'¥${widget.item.price.toStringAsFixed(2)}',
style: const TextStyle(
color: Colors.orange,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Row(
children: [
IconButton(
icon: const Icon(Icons.remove, size: 18),
onPressed: () {
if (widget.item.quantity > 1) {
widget.item.quantity--;
widget.onQuantityChanged(widget.item.quantity);
}
},
),
Text('${widget.item.quantity}'),
IconButton(
icon: const Icon(Icons.add, size: 18),
onPressed: () {
widget.item.quantity++;
widget.onQuantityChanged(widget.item.quantity);
},
),
],
),
],
),
],
),
),
],
),
);
}
}
// 数据模型
class Product {
final String name;
final double price;
final int sales;
Product({required this.name, required this.price, required this.sales});
}
class CartItem {
final String name;
final double price;
int quantity;
CartItem({
required this.name,
required this.price,
this.quantity = 1,
});
}
// 数据
final List<Product> _recommendedProducts = [
Product(name: '新款智能手机', price: 2999.00, sales: 1000),
Product(name: '无线蓝牙耳机', price: 199.00, sales: 5000),
Product(name: '智能手表', price: 899.00, sales: 2000),
Product(name: '运动鞋', price: 399.00, sales: 3000),
Product(name: '男士休闲裤', price: 159.00, sales: 1500),
Product(name: '女士连衣裙', price: 299.00, sales: 2500),
];
final List<String> _categories = [
'热门推荐',
'手机数码',
'家用电器',
'电脑办公',
'家居家装',
'服装服饰',
'鞋靴箱包',
'运动户外',
'美妆个护',
'食品生鲜',
];
final List<String> _subCategories = [
'手机',
'平板',
'耳机',
'充电宝',
'数据线',
'保护壳',
'笔记本',
'键盘',
'鼠标',
'显示器',
'音响',
'耳机',
'冰箱',
'洗衣机',
'空调',
'电视',
'微波炉',
'电饭煲',
];
final List<CartItem> _cartItems = [
CartItem(name: '新款智能手机', price: 2999.00, quantity: 1),
CartItem(name: '无线蓝牙耳机', price: 199.00, quantity: 2),
CartItem(name: '智能手表', price: 899.00, quantity: 1),
];
第一步:项目初始化与全局主题配置
进入项目后,打开lib/main.dart,这是我们应用的唯一入口。为了让我们的APP拥有统一的品牌调性,我们需要配置全局主题。观察主流购物APP,橙色系(如淘宝、京东)因其活力、热情和促销感而被广泛采用。我们也将沿用这一经典配色。
dart
// lib/main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const ShoppingApp());
}
class ShoppingApp extends StatelessWidget {
const ShoppingApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '淘淘购物', // 应用标题
theme: ThemeData(
primaryColor: Colors.orange, // 主色调设为橙色
scaffoldBackgroundColor: Colors.grey[100], // 页面背景为浅灰色
useMaterial3: true, // 使用最新的Material 3设计规范
),
home: const MainScreen(), // 应用启动后显示的首页
debugShowCheckedModeBanner: false, // 隐藏右上角的"DEBUG"水印
);
}
}

这段代码定义了应用的整体外观。
primaryColor会影响AppBar、FloatingActionButton等组件的默认颜色;scaffoldBackgroundColor则设置了所有页面的背景色,营造出一种干净、舒适的浏览环境。
第二步:构建应用骨架------底部导航栏
几乎所有的电商APP都采用底部导航栏(Bottom Navigation Bar)作为主要的页面切换方式,因为它符合拇指操作热区,用户体验极佳。在Flutter中,Scaffold widget为我们提供了现成的bottomNavigationBar属性。
我们创建一个MainScreen作为应用的根页面,它负责管理四个主要的功能模块:首页、分类、购物车和个人中心。
dart
// lib/main_screen.dart (可以单独放在一个文件中)
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int _currentIndex = 0; // 当前选中的索引
final List<Widget> _screens = [
const HomeScreen(),
const CategoryScreen(),
const CartScreen(),
const ProfileScreen(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: _screens[_currentIndex], // 根据索引显示对应页面
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index; // 点击时更新状态,触发UI重建
});
},
type: BottomNavigationBarType.fixed, // 固定模式,即使超过3个item也显示标签
selectedItemColor: Colors.orange, // 选中项的颜色
unselectedItemColor: Colors.grey, // 未选中项的颜色
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
BottomNavigationBarItem(icon: Icon(Icons.shopping_cart), label: '购物车'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'),
],
),
);
}
}

这里的关键在于
StatefulWidget的使用。因为底部导航栏的选中状态会改变,所以我们需要一个State来持有_currentIndex变量。当用户点击某个图标时,onTap回调会触发setState,通知Flutter框架UI需要根据新的_currentIndex值进行重建。这是一种非常典型的Flutter状态管理模式。
第三步:精心雕琢"首页"UI
首页是用户的流量入口,信息密度高,布局复杂。我们将其分解为几个核心区域:顶部AppBar、搜索框、轮播图、快捷入口和商品推荐列表。
1. 顶部AppBar与内嵌搜索
为了提升用户体验,我们将搜索框直接放在AppBar下方,并使其与AppBar融为一体。
dart
// 在HomeScreen的build方法中
appBar: AppBar(
title: const Text('淘淘购物'),
backgroundColor: Colors.orange,
elevation: 0, // 去掉阴影,使下方搜索框无缝衔接
actions: [
IconButton(icon: const Icon(Icons.search), onPressed: () {}),
IconButton(icon: const Icon(Icons.message), onPressed: () {}),
],
),

紧接着,我们用一个
Container包裹TextField,并通过设置内外两层Container的背景色(外层橙色,内层白色)和圆角,轻松实现了一个美观的搜索框。
2. 快捷入口 - GridView的妙用
快捷入口通常是一个多行多列的图标网格。GridView.count是实现这种布局的利器。我们将其crossAxisCount设为5,并禁用其滚动(NeverScrollableScrollPhysics),使其成为一个静态的网格。
dart
GridView.count(
crossAxisCount: 5,
shrinkWrap: true, // 让GridView只占用自身所需高度
physics: const NeverScrollableScrollPhysics(), // 禁用滚动
children: const [
QuickEntry(Icons.shopping_bag, '天猫'),
// ... 其他入口
],
),
QuickEntry是一个我们自定义的StatelessWidget,它接收图标和标签,组合成一个可复用的单元格。这种组件化的思想是构建大型UI的关键。
3. 商品推荐列表 - 嵌套滚动的处理
商品推荐区域本身也是一个GridView(2列),但它需要嵌套在外部的ListView中。直接嵌套会导致滚动冲突。解决方案是:
- 对内部的
GridView同样设置shrinkWrap: true,使其高度由子元素决定。 - 禁用内部
GridView的滚动:physics: const NeverScrollableScrollPhysics()。
这样,整个页面的滚动就由最外层的ListView统一接管,保证了流畅的用户体验。
第四步:打造个人中心------"我的"页面
"我的"页面相对结构化,主要由用户信息区、订单状态区和功能列表区组成。
1. 用户信息区
这个区域通常有鲜明的背景色(我们沿用橙色)以突出重要性。头像使用一个圆形的Container配合Icon模拟。登录/注册按钮通过BoxDecoration添加了边框,使其在橙色背景下依然清晰可辨。
2. 订单状态区
订单状态是典型的水平排列图标。我们再次使用Row和自定义的OrderStatusIcon组件来实现。每个状态项都是一个独立的Column,包含图标和文字。
3. 功能列表区
这部分是最标准的列表,ListTile是不二之选。它内置了leading(前置图标)、title(标题)和trailing(后置图标)属性,一行代码就能构建出专业感十足的列表项。Divider用于在列表项之间添加细线分隔,提升视觉层次感。
总结与思考
通过本文,我们成功地搭建起了一个购物APP的完整骨架,并完成了两个核心页面的静态UI。我们学习了:
- 全局主题配置 :如何通过
ThemeData统一应用风格。 - 应用导航 :利用
Scaffold和BottomNavigationBar构建标准的多页面应用。 - 复杂布局 :组合使用
ListView、GridView、Row、Column等基础Widget来实现设计师的稿子。 - 组件化开发 :将
QuickEntry、OrderStatusIcon等重复UI抽象成独立的Widget,提高代码复用性和可维护性。
🌐 加入社区
欢迎加入 开源鸿蒙跨平台开发者社区 ,获取最新资源与技术支持:
技术因分享而进步,生态因共建而繁荣 。
------ 晚霞的不甘 · 与您共赴鸿蒙跨平台开发之旅