Documentation Index
Fetch the complete documentation index at: https://docs-mstore.faisalaffan.com/llms.txt
Use this file to discover all available pages before exploring further.
Real-time Sync (MQTT)
Real-time synchronization menggunakan MQTT
๐ฏ Overview
MQTT adalah salah satu fitur utama dalam MStore Mobile yang menyediakan fungsionalitas untuk real-time synchronization menggunakan mqtt.
๐ Features
- โ
Real-time inventory updates
- โ
Transaction notifications
- โ
System announcements
- โ
Auto-reconnect
- โ
Topic subscription management
- โ
Message queuing
๐๏ธ Architecture
BLoC Implementation
BLoC: MqttBloc
// Events
abstract class MQTTEvent extends Equatable {}
class LoadMQTT extends MQTTEvent {}
class CreateMQTT extends MQTTEvent {}
class UpdateMQTT extends MQTTEvent {}
class DeleteMQTT extends MQTTEvent {}
// States
abstract class MQTTState extends Equatable {}
class MQTTInitial extends MQTTState {}
class MQTTLoading extends MQTTState {}
class MQTTLoaded extends MQTTState {}
class MQTTError extends MQTTState {}
// BLoC
class MQTTBloc extends Bloc<MQTTEvent, MQTTState> {
final MQTTRepository _repository;
MQTTBloc({required MQTTRepository repository})
: _repository = repository,
super(MQTTInitial()) {
on<LoadMQTT>(_onLoad);
on<CreateMQTT>(_onCreate);
on<UpdateMQTT>(_onUpdate);
on<DeleteMQTT>(_onDelete);
}
Future<void> _onLoad(
LoadMQTT event,
Emitter<MQTTState> emit,
) async {
emit(MQTTLoading());
final result = await _repository.getMQTTs();
result.fold(
(failure) => emit(MQTTError(failure.message)),
(data) => emit(MQTTLoaded(data)),
);
}
}
Repository Pattern
abstract class MQTTRepository {
Future<Either<Failure, List<MQTT>>> getMQTTs();
Future<Either<Failure, MQTT>> getMQTTById(String id);
Future<Either<Failure, MQTT>> createMQTT(MQTT data);
Future<Either<Failure, MQTT>> updateMQTT(String id, MQTT data);
Future<Either<Failure, void>> deleteMQTT(String id);
}
class MQTTRepositoryImpl implements MQTTRepository {
final MQTTApi _api;
final MQTTLocalRepository _localRepo;
@override
Future<Either<Failure, List<MQTT>>> getMQTTs() async {
try {
// Try local first (offline-first)
final local = await _localRepo.getMQTTs();
// Sync with API in background
final result = await _api.getMQTTs();
result.fold(
(failure) => null,
(data) => _localRepo.saveMQTTs(data),
);
return Right(local.isNotEmpty ? local : result.getOrElse(() => []));
} catch (e) {
return Left(UnexpectedFailure(e.toString()));
}
}
}
๐ก API Integration
Endpoints
Request/Response Examples
Get List
GET MQTT Broker
Authorization: Bearer {access_token}
Response:
{
"success": true,
"data": [
{
"id": "123",
"name": "Example",
"created_at": "2024-10-14T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 100
}
}
๐พ Local Database (Isar)
@collection
class MQTTLocal {
Id id = Isar.autoIncrement;
@Index()
String? mqttId;
String? name;
DateTime? createdAt;
DateTime? updatedAt;
DateTime? syncedAt;
bool? isSynced;
bool? isDeleted;
}
Queries
// Get all
final items = await isar.mqttLocals.where().findAll();
// Get by ID
final item = await isar.mqttLocals
.filter()
.mqttIdEqualTo(id)
.findFirst();
// Search
final results = await isar.mqttLocals
.filter()
.nameContains(query, caseSensitive: false)
.findAll();
// Get unsynced
final unsynced = await isar.mqttLocals
.filter()
.isSyncedEqualTo(false)
.findAll();
๐ Offline-First Strategy
Write Operations
- Save to local Isar immediately
- Show success to user
- Add to sync queue
- Background sync when online
- Update with server response
Read Operations
- Read from local Isar (fast)
- Show to user immediately
- Background fetch from API
- Update local cache if changed
- Notify UI if data updated
Conflict Resolution
- Strategy: Last-write-wins
- Timestamp: Server timestamp as source of truth
- Logging: All conflicts logged for audit
๐จ UI Components
Main Screen
class MQTTPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<MQTTBloc>()..add(LoadMQTT()),
child: Scaffold(
appBar: AppBar(title: Text('Real-time Sync (MQTT)')),
body: BlocBuilder<MQTTBloc, MQTTState>(
builder: (context, state) {
if (state is MQTTLoading) {
return Center(child: CircularProgressIndicator());
}
if (state is MQTTError) {
return ErrorWidget(message: state.message);
}
if (state is MQTTLoaded) {
return MQTTListView(items: state.items);
}
return SizedBox.shrink();
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => _navigateToCreate(context),
child: Icon(Icons.add),
),
),
);
}
}
๐งช Testing
Unit Tests
void main() {
group('MQTTBloc', () {
late MQTTBloc bloc;
late MockMQTTRepository mockRepository;
setUp(() {
mockRepository = MockMQTTRepository();
bloc = MQTTBloc(repository: mockRepository);
});
tearDown(() {
bloc.close();
});
test('initial state is MQTTInitial', () {
expect(bloc.state, equals(MQTTInitial()));
});
blocTest<MQTTBloc, MQTTState>(
'emits [Loading, Loaded] when Load succeeds',
build: () {
when(() => mockRepository.getMQTTs()).thenAnswer(
(_) async => Right([MQTT(id: '1', name: 'Test')]),
);
return bloc;
},
act: (bloc) => bloc.add(LoadMQTT()),
expect: () => [
MQTTLoading(),
isA<MQTTLoaded>(),
],
);
});
}
- Lazy Loading: Load data on demand
- Pagination: Implement pagination for large datasets
- Caching: Cache frequently accessed data
- Indexing: Use Isar indexes for fast queries
- Background Sync: Sync in background to avoid blocking UI
๐ Security
- Authorization: Check user permissions before operations
- Data Encryption: Sensitive data encrypted in Isar
- Input Validation: Validate all user inputs
- Audit Trail: Log all operations for audit
- Use Cupertino widgets
- Follow iOS HIG
- Handle safe area insets
Android
- Use Material widgets
- Follow Material Design
- Handle back button
Last Updated: October 14, 2024
Status: โ
Production Ready