tvf_flutter_schema 1.1.0
tvf_flutter_schema: ^1.1.0 copied to clipboard
KOMBU IoT System - GraphQL Schema for Flutter/Dart (Mobile-focused). Provides GraphQL queries, mutations, and a direct GraphQL client for mobile app development with AWS Amplify. Uses GraphQL directly [...]
KOMBU Flutter Schema (Mobile-focused) #
GraphQL schema package for KOMBU IoT System mobile app. This package provides GraphQL queries, mutations, and a direct GraphQL client. Uses GraphQL directly without model classes - all responses are Map<String, dynamic>.
π¦ Installation #
Add to your pubspec.yaml:
dependencies:
tvf_flutter_schema:
path: ../packages/flutter-schema # Local development
# or
# git:
# url: https://github.com/your-org/kombu
# path: packages/flutter-schema
Then run:
flutter pub get
π Usage #
Import #
import 'package:tvf_flutter_schema/tvf_flutter_schema.dart';
Create GraphQL Client #
// Create a GraphQL client instance
final client = GraphQLClient();
Query User #
// Get user by ID
final result = await client.query(
document: UserQueries.getUser,
variables: {'id': userId},
);
if (result.hasErrors) {
print('Error: ${result.errors}');
} else {
final user = result.data?['getUser'];
print('User: ${user?['fullName']}');
print('Email: ${user?['email']}');
}
List Users #
final result = await client.query(
document: UserQueries.listUsers,
variables: {
'filter': {'status': {'eq': 'active'}},
'limit': 20,
},
);
if (!result.hasErrors) {
final items = result.data?['listUsers']?['items'] as List?;
for (final user in items ?? []) {
print('User: ${user['email']}');
}
final nextToken = result.data?['listUsers']?['nextToken'];
if (nextToken != null) {
// Load next page
final nextResult = await client.query(
document: UserQueries.listUsers,
variables: {
'filter': {'status': {'eq': 'active'}},
'limit': 20,
'nextToken': nextToken,
},
);
}
}
Update User Profile #
final result = await client.mutate(
document: UserMutations.updateUser,
variables: {
'input': {
'id': userId,
'fullName': 'New Name',
'phoneNumber': '+84123456789',
},
},
);
if (!result.hasErrors) {
final updatedUser = result.data?['updateUser'];
print('Updated: ${updatedUser?['fullName']}');
} else {
print('Error: ${result.errors}');
}
Deactivate User (Custom Mutation) #
final result = await client.mutate(
document: UserMutations.deactivateUser,
variables: {
'userId': userId, // or email if useEmail is true
'useEmail': false,
},
);
if (!result.hasErrors) {
final response = result.data?['deactivateUser'];
if (response?['success'] == true) {
print('User deactivated successfully');
} else {
print('Error: ${response?['error']}');
}
}
Reactivate User (Custom Mutation) #
final result = await client.mutate(
document: UserMutations.reactivateUser,
variables: {
'userId': userId,
'useEmail': false,
},
);
if (!result.hasErrors) {
final response = result.data?['reactivateUser'];
if (response?['success'] == true) {
print('User reactivated successfully');
} else {
print('Error: ${response?['error']}');
}
}
Remove User (Custom Mutation) #
β οΈ Warning: This action is IRREVERSIBLE and will permanently delete the user.
final result = await client.mutate(
document: UserMutations.removeUser,
variables: {
'userId': userId,
'useEmail': false,
},
);
if (!result.hasErrors) {
final response = result.data?['removeUser'];
if (response?['success'] == true) {
print('User removed successfully');
} else {
print('Error: ${response?['error']}');
}
}
List Devices #
final result = await client.query(
document: DeviceQueries.listDevices,
variables: {
'filter': {
'userId': {'eq': userId},
},
'limit': 20,
},
);
if (!result.hasErrors) {
final items = result.data?['listDevices']?['items'] as List?;
for (final device in items ?? []) {
print('Device: ${device['deviceName']}');
print('Status: ${device['status']}');
}
}
Create Device #
final result = await client.mutate(
document: DeviceMutations.createDevice,
variables: {
'input': {
'deviceId': 'ESP32-ABC123',
'userId': currentUserId,
'deviceName': 'Living Room Heater',
'deviceType': 'kombu_brewer',
'location': 'Living Room',
'status': 'offline',
},
},
);
if (!result.hasErrors) {
final device = result.data?['createDevice'];
print('Device created: ${device?['deviceName']}');
}
Update Device #
final result = await client.mutate(
document: DeviceMutations.updateDevice,
variables: {
'input': {
'id': deviceId,
'deviceName': 'New Device Name',
'location': 'Kitchen',
},
},
);
if (!result.hasErrors) {
final device = result.data?['updateDevice'];
print('Device updated: ${device?['deviceName']}');
}
Push Command to IoT Device #
Option 1: Using Helper Method (Recommended)
// Push command using helper method (automatically handles payload stringification)
final result = await PushCommandHelper.pushCommand(
topic: 'kombu/device-id/down/command',
payload: {
'command': 'setTemperature',
'temperature': 75,
'heater': 'f1',
},
);
if (result.success) {
print('Command sent successfully! Message ID: ${result.messageId}');
} else {
print('Failed to send command: ${result.error}');
}
Option 2: Using GraphQL Client
final result = await client.mutate(
document: DeviceMutations.pushCommand,
variables: {
'topic': 'kombu/device-id/down/command',
'payload': jsonEncode({
'command': 'setTemperature',
'temperature': 75,
'heater': 'f1',
}),
},
);
if (!result.hasErrors) {
final response = result.data?['pushCommand'];
if (response?['success'] == true) {
print('Success: ${response?['messageId']}');
} else {
print('Error: ${response?['error']}');
}
}
Register Device Token (Push Notification) #
Option 1: Using Helper (Recommended)
final helper = PushNotificationHelper();
await helper.registerDeviceToken(userId);
This method automatically:
- Queries existing tokens
- Updates token if exists
- Creates new token if not exists
Option 2: Using GraphQL Client
// Create device token
final result = await client.mutate(
document: DeviceTokenMutations.createDeviceToken,
variables: {
'input': {
'tokenId': 'token_123',
'userId': 'user-id',
'deviceToken': 'fcm-token',
'platform': 'android',
'deviceId': 'device-id',
'isActive': true,
},
},
);
if (!result.hasErrors) {
final token = result.data?['createDeviceToken'];
print('Token created: ${token?['tokenId']}');
}
Get Notifications #
final result = await client.query(
document: NotificationQueries.notificationsByUser,
variables: {
'userId': userId,
'limit': 20,
},
);
if (!result.hasErrors) {
final items = result.data?['notificationsByUser']?['items'] as List?;
for (final notification in items ?? []) {
print('Notification: ${notification['title']}');
print('Status: ${notification['status']}');
}
}
Mark Notification as Read #
final result = await client.mutate(
document: NotificationMutations.updateNotification,
variables: {
'input': {
'id': notificationId,
'status': 'read',
'readAt': DateTime.now().toIso8601String(),
},
},
);
if (!result.hasErrors) {
final notification = result.data?['updateNotification'];
print('Notification updated: ${notification?['status']}');
}
GraphQL Subscriptions #
// Subscribe to real-time updates
final subscription = client.subscribe(
document: '''
subscription OnUserUpdate(\$id: ID!) {
onUpdateUser(id: \$id) {
id
fullName
email
}
}
''',
variables: {'id': userId},
);
await for (final event in subscription) {
if (!event.hasErrors) {
final user = event.data?['onUpdateUser'];
print('User updated: ${user?['fullName']}');
} else {
print('Subscription error: ${event.errors}');
}
}
π Available APIs #
GraphQL Client #
GraphQLClient()- Create a new GraphQL client instanceclient.query()- Execute a GraphQL queryclient.mutate()- Execute a GraphQL mutationclient.subscribe()- Subscribe to GraphQL subscriptions
Queries #
User
UserQueries.getUser- Get user by IDUserQueries.listUsers- List users with pagination
Device
DeviceQueries.getDevice- Get device by IDDeviceQueries.listDevices- List devices with filters
DeviceToken
DeviceTokenQueries.getDeviceToken- Get device token by IDDeviceTokenQueries.listDeviceTokens- List device tokensDeviceTokenQueries.tokensByUser- Query tokens by user
Notification
NotificationQueries.getNotification- Get notification by IDNotificationQueries.listNotifications- List notificationsNotificationQueries.notificationsByUser- Query notifications by user (sorted by sentAt)
Mutations #
User
UserMutations.updateUser- Update user profileUserMutations.deactivateUser- Deactivate user (custom mutation)UserMutations.reactivateUser- Reactivate user (custom mutation)UserMutations.removeUser- Remove user permanently (custom mutation)
Device
DeviceMutations.createDevice- Create new deviceDeviceMutations.updateDevice- Update device (name, location, etc.)DeviceMutations.pushCommand- Push command to AWS IoT Core topic
DeviceToken
DeviceTokenMutations.createDeviceToken- Register device tokenDeviceTokenMutations.updateDeviceToken- Update device tokenDeviceTokenMutations.deleteDeviceToken- Unregister device token
Notification
NotificationMutations.updateNotification- Update notification (mark as read)
Helpers #
Push Command Helper
PushCommandHelper.pushCommand()- Push command using helper method (recommended)PushCommandHelper.pushCommandRaw()- Push command using raw GraphQL mutation (advanced)
Push Notification Helper
PushNotificationHelper.registerDeviceToken()- Auto register/update device tokenPushNotificationHelper.createDeviceToken()- Create device token manuallyPushNotificationHelper.updateDeviceToken()- Update device token manually
π Authentication #
All GraphQL operations require authentication via AWS Cognito. Make sure to:
- Initialize Amplify with Cognito configuration
- Sign in user before making API calls
- Authentication headers are handled automatically by Amplify
π Notes #
Direct GraphQL Usage #
This package uses GraphQL directly without model classes:
- All responses are
Map<String, dynamic> - No need to parse JSON into model objects
- Access data directly:
result.data?['getUser']?['email'] - More flexible and lightweight
What's NOT Included #
This mobile-focused schema does NOT include:
- DeviceTelemetry: Use MQTT subscriptions for real-time telemetry
- DeviceConfiguration: Backend/internal only
- Timer: May be added in future versions
- Alert: May be added in future versions
- Firmware: Admin-only operations
User Creation #
User creation is handled by AWS Cognito sign-up, not GraphQL. Use Amplify.Auth.signUp() instead.
Real-time Data #
For real-time device telemetry and status updates, use MQTT subscriptions via Amplify PubSub, not GraphQL queries.
π οΈ Development #
Build #
cd packages/flutter-schema
flutter pub get
flutter analyze
Format #
dart format lib/
π License #
See LICENSE file for details.