BRL IOT App
Ứng dụng di động IOT được phát triển bằng Flutter, dựa trên thiết kế từ Figma.
Cấu trúc dự án
lib/
├── constants/ # Các hằng số và cấu hình
├── models/ # Các model dữ liệu
├── screens/ # Các màn hình
│ └── home_screen.dart
├── theme/ # Theme và style
│ └── app_theme.dart
├── utils/ # Các tiện ích
├── widgets/ # Các widget tái sử dụng
│ ├── custom_header.dart
│ ├── custom_bottom_navigation.dart
│ └── home_content.dart
└── main.dart # Entry point
Layout chính
Ứng dụng được chia thành 3 phần chính:
- Header: Hiển thị thông tin chào mừng và avatar người dùng
- Body: Nội dung chính của ứng dụng, bao gồm:
- Thông báo đăng nhập
- Danh sách thiết bị
- Phần thêm thiết bị mới
- Khuyến mãi
- Khám phá IOT
- Dịch vụ và hỗ trợ
- Bottom Navigation: Thanh điều hướng với 4 tab và nút quét ở giữa
- Thiết bị
- Thông báo
- Tin tức
- Tài khoản
Yêu cầu
- Flutter SDK: >=3
- .2.6 <4.0.0
- Các font SF Pro Display (Regular, Medium, Semibold)
- Các hình ảnh cần thiết trong thư mục assets/images/ và assets/icons/
Cài đặt
- Clone repository
- Chạy
flutter pub getđể cài đặt các dependencies - Chạy
flutter runđể khởi động ứng dụng
Lưu ý
- Cần thêm các font SF Pro Display vào thư mục assets/fonts/
- Cần thêm các hình ảnh cần thiết vào thư mục assets/images/ và assets/icons/
BRL Flutter App - Notification System Documentation
📋 Tổng quan dự án
BRL Flutter App là ứng dụng IoT với hệ thống thông báo hoàn chỉnh, sử dụng kiến trúc Clean Architecture và BLoC pattern để quản lý state.
🏗️ Kiến trúc hệ thống
Cấu trúc thư mục
lib/
├── core/ # Lớp cốt lõi
│ ├── constants/
│ │ └── api_constants.dart # Các hằng số API
│ ├── network/
│ │ └── dio_client.dart # HTTP Client với Dio
│ ├── errors/
│ │ └── api_exception.dart # Xử lý lỗi API
│ └── storage/
│ └── storage_service.dart # Lưu trữ local với SharedPreferences
├── data/ # Lớp dữ liệu
│ ├── models/
│ │ ├── notification_model.dart # Model thông báo
│ │ └── api_models.dart # Model request/response API
│ ├── services/
│ │ └── notification_service.dart # Service gọi API
│ └── repositories/
│ └── notification_repository.dart # Repository pattern
├── presentation/ # Lớp giao diện
│ └── blocs/notification/
│ ├── notification_bloc.dart # BLoC chính
│ ├── notification_event.dart # Các events
│ └── notification_state.dart # Các states
└── screens/
├── notification_screen.dart # Màn hình danh sách thông báo
└── notification_detail_screen.dart # Màn hình chi tiết thông báo
🔄 Luồng hoạt động hiện tại
1. Khởi tạo ứng dụng
App Start → StorageService.init() → Load User Data → Initialize BLoC
2. Luồng tải thông báo
UI (NotificationScreen)
↓
BLoC (LoadNotifications event)
↓
Repository (getNotifications)
↓
Service (getNotifications)
↓
DioClient (POST request)
↓
API Server (/notifications/filter)
↓
Response → Models → BLoC → UI Update
3. Luồng chi tiết từng bước
Bước 1: UI khởi tạo
// notification_screen.dart
@override
void initState() {
super.initState();
_notificationBloc = NotificationBloc(repository: NotificationRepository());
_notificationBloc.add(const LoadNotifications(refresh: true));
}
Bước 2: BLoC xử lý event
// notification_bloc.dart
Future<void> _onLoadNotifications(
LoadNotifications event,
Emitter<NotificationState> emit,
) async {
// Emit loading state
emit(NotificationLoading());
// Gọi repository
final response = await _repository.getNotifications(...);
// Xử lý response và emit state mới
if (response.success) {
emit(NotificationLoaded(...));
} else {
emit(NotificationError(...));
}
}
Bước 3: Repository gọi Service
// notification_repository.dart
Future<ApiResponse<List<NotificationModel>>> getNotifications(...) async {
return await _notificationService.getNotifications(...);
}
Bước 4: Service thực hiện API call
// notification_service.dart
Future<ApiResponse<List<NotificationModel>>> getNotifications(...) async {
// Lấy userId từ storage
final userId = StorageService.getUserId() ?? "007";
// Tạo request filter
final request = NotificationFilterRequest.forUser(userId: userId, ...);
// Gọi API
final response = await _dioClient.post(
ApiConstants.notificationFilter,
data: request.toJson(),
);
// Parse response và return
return ApiResponse.success(data: notifications);
}
Bước 5: DioClient xử lý HTTP request
// dio_client.dart
Future<Response<T>> post<T>(...) async {
try {
// Tự động thêm token vào header
// Log request/response
return await _dio.post<T>(...);
} on DioException catch (e) {
throw ApiException.fromDioError(e);
}
}
📊 Các tính năng chính đã triển khai
1. Quản lý thông báo
- ✅ Tải danh sách thông báo theo userId
- ✅ Phân trang (pagination)
- ✅ Làm mới (refresh)
- ✅ Đánh dấu đã đọc/chưa đọc
- ✅ Xóa thông báo
- ✅ Lọc theo loại, trạng thái, độ ưu tiên
- ✅ Tìm kiếm thông báo
- ✅ Sắp xếp theo thời gian
2. State Management với BLoC
// Các Events có sẵn
- LoadNotifications // Tải thông báo
- LoadNotificationById // Tải chi tiết thông báo
- MarkNotificationAsRead // Đánh dấu đã đọc
- MarkAllNotificationsAsRead // Đánh dấu tất cả đã đọc
- DeleteNotification // Xóa thông báo
- FilterNotifications // Lọc thông báo
- SearchNotifications // Tìm kiếm
- RefreshNotifications // Làm mới
- LoadMoreNotifications // Tải thêm
// Các States có sẵn
- NotificationInitial // Trạng thái ban đầu
- NotificationLoading // Đang tải
- NotificationLoaded // Đã tải xong
- NotificationError // Lỗi
- NotificationDetailLoaded // Chi tiết đã tải
- NotificationOperationSuccess // Thao tác thành công
3. Xử lý lỗi toàn diện
// api_exception.dart
- Connection timeout
- Network errors
- HTTP status codes (400, 401, 403, 404, 500)
- Custom error messages trong tiếng Việt
- Retry mechanisms
4. Local Storage
// storage_service.dart
- User ID và User Name
- Access Token và Refresh Token
- Generic storage methods
- Automatic initialization
🎯 Cách sử dụng trong UI
1. Khởi tạo BLoC trong Widget
class NotificationScreen extends StatefulWidget {
@override
void initState() {
super.initState();
_notificationBloc = NotificationBloc(repository: NotificationRepository());
_notificationBloc.add(const LoadNotifications(refresh: true));
}
}
2. Lắng nghe state changes
BlocConsumer<NotificationBloc, NotificationState>(
listener: (context, state) {
// Xử lý side effects (hiển thị snackbar, navigation...)
if (state is NotificationError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.message))
);
}
},
builder: (context, state) {
// Xây dựng UI dựa trên state
if (state is NotificationLoading) {
return CircularProgressIndicator();
} else if (state is NotificationLoaded) {
return _buildNotificationList(state.filteredNotifications);
}
return Container();
},
)
3. Thực hiện các thao tác
// Đánh dấu đã đọc
context.read<NotificationBloc>().add(
MarkNotificationAsRead(notificationId)
);
// Lọc thông báo
context.read<NotificationBloc>().add(
const FilterNotifications(showOnlyUnread: true)
);
// Tìm kiếm
context.read<NotificationBloc>().add(
SearchNotifications('từ khóa')
);
🔧 Configuration
1. API Constants
// lib/core/constants/api_constants.dart
class ApiConstants {
static const String baseUrl = 'https://your-api-domain.com/api';
static const String notificationFilter = '/notifications/filter';
// Timeouts
static const int connectTimeout = 30000;
static const int receiveTimeout = 30000;
// Storage keys
static const String userIdKey = 'user_id';
static const String accessTokenKey = 'access_token';
}
2. Dependencies trong pubspec.yaml
dependencies:
# State Management
flutter_bloc: ^8.1.3
equatable: ^2.0.5
# API & Network
dio: ^5.4.0
# Storage
shared_preferences: ^2.2.2
# Utilities
logger: ^2.0.2
🌐 API Integration
Request Format
POST /notifications/filter
{
"metadata": { "type": "" },
"filter": [
{
"property": "userId",
"type": "string",
"condition": "equals",
"value": "007"
}
],
"sort": [
{
"orderBy": "createTime",
"orderDesc": true
}
],
"paging": {
"indexPage": 0,
"numberOfPage": 100
}
}
Response Format
{
"success": true,
"message": "Success",
"data": {
"data": [
{
"from": { "id": "system", "type": "system" },
"to": { "userId": "007", "userName": "User Name" },
"header": {
"metadata": {
"priority": 1,
"type": "alert",
"status": "unread",
"channel": "mobile",
"createTime": "2025-01-20T10:30:00Z"
}
},
"body": {
"title": "Notification Title",
"content": "Notification content"
}
}
],
"totalCount": 100
}
}
🎨 UI Features
1. Notification List
- ✅ Nhóm theo ngày tháng
- ✅ Hiển thị trạng thái đã đọc/chưa đọc
- ✅ Badge cho thông báo khẩn cấp
- ✅ Pull-to-refresh
- ✅ Infinite scroll (load more)
2. Visual Indicators
- 🔵 Chấm xanh: Thông báo chưa đọc
- 🟠 Badge cam: Thông báo khẩn cấp
- ⚪ Màu mờ: Thông báo đã đọc
3. Interactions
- 👆 Tap: Xem chi tiết và đánh dấu đã đọc
- 🔄 Pull down: Refresh danh sách
- ⚙️ More button: Menu tùy chọn
🚀 Cách mở rộng
1. Thêm loại thông báo mới
// Thêm vào FilterCondition
FilterCondition(
property: 'type',
type: 'string',
condition: 'equals',
value: 'new_type',
)
2. Thêm API endpoint mới
// api_constants.dart
static const String newEndpoint = '/notifications/new-endpoint';
// notification_service.dart
Future<ApiResponse<T>> newApiCall() async {
final response = await _dioClient.get(ApiConstants.newEndpoint);
// Process response...
}
3. Thêm state và event mới
// notification_event.dart
class NewNotificationEvent extends NotificationEvent {
// Implementation...
}
// notification_state.dart
class NewNotificationState extends NotificationState {
// Implementation...
}
// notification_bloc.dart
on<NewNotificationEvent>(_onNewNotificationEvent);
🐛 Debugging Tips
1. Enable Logging
// dio_client.dart sẽ tự động log requests/responses trong debug mode
// Kiểm tra console để xem:
// - REQUEST[POST] => PATH: /notifications/filter
// - RESPONSE[200] => PATH: /notifications/filter
// - ERROR[400] => PATH: /notifications/filter
2. BLoC Inspector
// Sử dụng flutter inspector để theo dõi state changes
// Hoặc thêm prints trong BLoC:
print('Current state: $state');
print('Processing event: $event');
3. Common Issues
- Token expired: Kiểm tra StorageService.getAccessToken()
- Network error: Kiểm tra kết nối internet
- Parsing error: Kiểm tra format JSON response
- State not updating: Kiểm tra BlocProvider scope
📝 Notes
- User ID hiện tại: Hardcode "007" trong NotificationService (dòng 16)
- Base URL: Cần thay đổi trong ApiConstants (dòng 3)
- Token management: Tự động thêm vào header trong DioClient
- Error messages: Đã dịch sang tiếng Việt trong ApiException
- Pagination: Mặc định 20 items/page, có thể thay đổi trong NotificationBloc
🔄 Next Steps
- Cập nhật Base URL trong
api_constants.dart - Implement token refresh logic trong DioClient
- Thêm push notifications integration
- Optimize performance với caching
- Add unit tests cho các components
- Implement offline support với local database
Tài liệu này được cập nhật lần cuối: $(date)
Description
Languages
Dart
60.9%
Makefile
31.4%
JavaScript
2.4%
PureBasic
1.1%
C++
1.1%
Other
2.9%