Việc xây dựng một giao diện trang chủ phức tạp đòi hỏi cấu trúc mã nguồn rõ ràng để tránh tình trạng các thành phần bị phụ thuộc quá chặt chẽ (tight coupling). Khi phát triển ứng dụng Flutter cho OpenHarmony, việc phân tách module không chỉ giúp mã nguồn dễ bảo trì mà còn tối ưu hóa hiệu suất hiển thị trên các thiết bị chạy HarmonyOS.
Cấu trúc phân lớp của trang chủ
Kiến trúc trang chủ được thiết kế dựa trên CustomScrollView để tận dụng sức mạnh của Slivers, cho phép tạo ra các hiệu ứng cuộn mượt mà và tiêu đề (AppBar) có khả năng co giãn.
- Lớp điều hướng: Sử dụng
Scaffoldkết hợp vớiIndexedStackđể duy trì trạng thái của các tab. - Thành phần Header:
SliverAppBarchứa thông tin tiêu đề và các nút chức năng nhanh. - Thành phần nội dung: Sử dụng
SliverToBoxAdapterđể bao bọc các widget như Thẻ chào mừng, Danh mục lối tắt, và Biểu đồ thống kê.
Cấu hình Quản lý trạng thái (State Management)
Sử dụng Provider để quản lý luồng dữ liệu độc lập giữa các module khác nhau như tâm trạng, bài tập thiền và kết quả khảo sát.
// Cấu hình tại entry point (main.dart)
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => EmotionalTrackerModel()),
ChangeNotifierProvider(create: (_) => MindfulnessSessionModel()),
ChangeNotifierProvider(create: (_) => AssessmentProvider()),
],
child: const AppRouter(),
)
Triển khai giao diện Dashboard chính
Dưới đây là cách triển khai màn hình chính với cấu trúc Sliver để tối ưu hóa hiệu suất trên môi trường OpenHarmony.
class MentalHealthDashboard extends StatelessWidget {
final Function(int)? onTabChange;
const MentalHealthDashboard({super.key, this.onTabChange});
@override
Widget build(BuildContext context) {
return CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: [
_buildDynamicHeader(),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: [
const SizedBox(height: 16),
_buildInsightCard(context),
_buildActionGrid(context),
_buildProgressOverview(context),
_buildUtilitySlider(context),
const SizedBox(height: 100), // Khoảng trống cho Navigation Bar
],
),
),
),
],
);
}
Widget _buildDynamicHeader() {
return SliverAppBar(
expandedHeight: 180,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
background: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFF4A90E2), Color(0xFF9013FE)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: const SafeArea(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Chào buổi sáng',
style: TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold),
),
Text(
'Hãy bắt đầu ngày mới thật tích cực nhé!',
style: TextStyle(color: Colors.white70, fontSize: 14),
),
],
),
),
),
),
),
);
}
Widget _buildInsightCard(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 4)),
],
),
child: const Column(
children: [
Icon(Icons.lightbulb_outline, color: Colors.orange, size: 30),
SizedBox(height: 10),
Text(
'"Hành trình vạn dặm luôn bắt đầu từ một bước chân đầu tiên."',
textAlign: TextAlign.center,
style: TextStyle(fontStyle: FontStyle.italic, fontSize: 15),
),
],
),
);
}
Widget _buildActionGrid(BuildContext context) {
return GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 1.4,
children: [
_buildActionItem(Icons.mood, 'Tâm trạng', Colors.redAccent, () => onTabChange?.call(1)),
_buildActionItem(Icons.self_improvement, 'Thiền định', Colors.blueAccent, () => onTabChange?.call(2)),
],
);
}
Widget _buildActionItem(IconData icon, String label, Color themeColor, VoidCallback pressAction) {
return InkWell(
onTap: pressAction,
child: Container(
decoration: BoxDecoration(
color: themeColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(15),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: themeColor),
const SizedBox(height: 8),
Text(label, style: TextStyle(color: themeColor, fontWeight: FontWeight.w600)),
],
),
),
);
}
// Các widget con khác như buildProgressOverview, buildUtilitySlider...
}
Tối ưu hóa đặc thù cho nền tảng OpenHarmony
1. Xử lý hiệu suất cuộn
Trên các thiết bị OpenHarmony, việc hiển thị quá nhiều widget tĩnh trong một danh sách dài có thể gây hiện tượng "jank". Giải pháp là thay thế các Column tĩnh bằng ListView.builder hoặc chia nhỏ các phần nội dung vào từng SliverToBoxAdapter khác nhau để cơ chế lazy loading của Flutter hoạt động hiệu quả hơn.
2. Quản lý trạng thái với IndexedStack
Khi chuyển đổi giữa các tab trên thanh điều hướng phía dưới, mặc định các Widget có thể bị rebuild gây tốn tài nguyên. Việc sử dụng IndexedStack giúp giữ nguyên trạng thái các màn hình, nhưng cần lưu ý xử lý Consumer của Provider bên trong các trang con để dữ liệu được cập nhật kịp thời mà không cần reload toàn bộ trang chủ.
class MainNavigationWrapper extends StatefulWidget {
const MainNavigationWrapper({super.key});
@override
State<MainNavigationWrapper> createState() => _MainNavigationWrapperState();
}
class _MainNavigationWrapperState extends State<MainNavigationWrapper> {
int _activeTab = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false, // Tránh đẩy UI khi bàn phím ảo hiện lên trên OpenHarmony
body: IndexedStack(
index: _activeTab,
children: [
MentalHealthDashboard(onTabChange: (idx) => setState(() => _activeTab = idx)),
const MoodEntryScreen(),
const MeditationGallery(),
],
),
bottomNavigationBar: CustomBottomBar(
current: _activeTab,
onTap: (idx) => setState(() => _activeTab = idx),
),
);
}
}
3. Hoạt ảnh và Độ trễ (Animation Staggering)
Để đảm bảo giao diện mượt mà trên các dòng máy OpenHarmony tầm trung, không nên khởi chạy tất cả hiệu ứng Fade-in cùng một lúc. Thay vào đó, hãy sử dụng thuộc tính delay trong các package hoạt ảnh để các thành phần xuất hiện tuần tự, giảm tải cho GPU tại thời điểm build trang.
Các lưu ý quan trọng khi triển khai
- Vùng an toàn (Safe Area): Do các thiết bị OpenHarmony có thiết kế màn hình đa dạng (tai thỏ, nốt ruồi), luôn bao bọc nội dung trong
SafeAreađể đảm bảo các nút chức năng không bị che lấp. - Sự kiện hệ thống: Đảm bảo xử lý tốt sự kiện nút "Back" của hệ thống HarmonyOS bằng cách sử dụng
PopScopeđể tránh việc thoát ứng dụng đột ngột từ trang chủ. - Quản lý bộ nhớ: Giải phóng các Controller (Animation, Scroll) trong hàm
disposecủa các StatefulWidgets để tránh rò rỉ bộ nhớ trên môi trường thực thi của OpenHarmony.