{"id":31149481,"url":"https://github.com/cybroidtech/appwrite_offline","last_synced_at":"2025-09-18T17:53:16.336Z","repository":{"id":264272452,"uuid":"892898476","full_name":"cybroidtech/appwrite_offline","owner":"cybroidtech","description":"A Flutter Data adapter for Appwrite that provides offline support, real-time updates, and seamless integration with Flutter Data's powerful features.","archived":false,"fork":false,"pushed_at":"2025-08-16T11:49:43.000Z","size":333,"stargazers_count":10,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-16T13:41:26.749Z","etag":null,"topics":["appwrite","flutter","offline"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/appwrite_offline","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cybroidtech.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-11-23T02:14:06.000Z","updated_at":"2025-08-14T14:48:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"7591abf2-9d77-49e9-8d5b-859abdff4018","html_url":"https://github.com/cybroidtech/appwrite_offline","commit_stats":null,"previous_names":["cybroidtech/appwrite_offline"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/cybroidtech/appwrite_offline","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cybroidtech%2Fappwrite_offline","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cybroidtech%2Fappwrite_offline/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cybroidtech%2Fappwrite_offline/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cybroidtech%2Fappwrite_offline/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cybroidtech","download_url":"https://codeload.github.com/cybroidtech/appwrite_offline/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cybroidtech%2Fappwrite_offline/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275806095,"owners_count":25531917,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-18T02:00:09.552Z","response_time":77,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appwrite","flutter","offline"],"created_at":"2025-09-18T17:53:12.138Z","updated_at":"2025-09-18T17:53:16.330Z","avatar_url":"https://github.com/cybroidtech.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Appwrite Offline\n\nA Flutter Data adapter for Appwrite that provides offline support, real-time updates, and seamless integration with Flutter Data's powerful features.\n\n## Features\n\n- 🔄 **Automatic Synchronization**: Seamlessly sync data between local storage and Appwrite\n- 📱 **Offline Support**: Work with your data even when offline\n- ⚡ **Real-time Updates**: Listen to changes in your Appwrite collections in real-time\n- 🔍 **Advanced Querying**: Supported operators: '==', '!=', '\u003e', '\u003e=', '\u003c', '\u003c=', 'startsWith', 'endsWith', 'contains', 'search', 'between', 'in', 'isNull', 'isNotNull'.\n- 🎯 **Type-safe**: Fully typed models, queries, \u0026 partial updates\n- 🪄 **Easy Integration**: Simple setup process with minimal configuration\n\n## Prerequisites\n\nBefore using this package, make sure you:\n\n1. Have a working Appwrite backend setup\n2. Understand and follow [Flutter Data's setup guide](https://flutterdata.dev) carefully\n3. Are using Riverpod for state management\n\nThis package only handles the Appwrite integration with Flutter Data. All other Flutter Data configurations must be properly set up.\n\n## Installation\n\nAdd this to your package's pubspec.yaml file:\n\n```yaml\ndependencies:\n  appwrite_offline: ^0.0.5\n```\n\n## Setup\n\n1. Initialize Appwrite Offline in your app:\n\n```dart\nvoid main() {\n  // Initialize Appwrite Offline\n  AppwriteOffline.initialize(\n    projectId: 'your_project_id',\n    databaseId: 'your_database_id',\n    endpoint: 'https://your-appwrite-instance.com/v1',\n    jwt: 'optional-jwt-token', // If using authentication\n  );\n\n  runApp(\n    ProviderScope(\n      child: MyApp(),\n    ),\n  );\n}\n```\n2. Create your model class:\n\n```dart\n@DataRepository([AppwriteAdapter])\n@JsonSerializable()\nclass Product extends DataModel\u003cProduct\u003e {\n  @override\n  @JsonKey(readValue: $)\n  final String? id;\n  final String name;\n  final double price;\n  final BelongsTo\u003cCategory\u003e? category;\n  final HasMany\u003cReview\u003e? reviews;\n  @JsonKey(readValue: $)\n  final DateTime? createdAt;\n  @JsonKey(readValue: $)\n  final DateTime? updatedAt;\n\n  Product({\n    this.id,\n    required this.name,\n    required this.price,\n    this.category,\n    this.reviews,\n    this.createdAt,\n    this.updatedAt,\n  });\n\n  factory Product.fromJson(Map\u003cString, dynamic\u003e json) =\u003e \n      _$ProductFromJson(json);\n\n  Map\u003cString, dynamic\u003e toJson() =\u003e _$ProductToJson(this);\n}\n```\n\n3. Run the code generator:\n\n```bash\ndart run build_runner build -d\n```\n\nOR\n\n```bash\ndart run build_runner watch\n```\n\n## Usage\n\n### Basic Operations\n\n```dart\nclass ProductsScreen extends ConsumerWidget {\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    // Watch all products with offline sync support\n    final productsState = ref.products.watchAll(\n      syncLocal: true,\n    );\n\n    if (productsState.isLoading) {\n      return CircularProgressIndicator();\n    }\n\n    if (productsState.hasException) {\n      return Text('Error: ${productsState.exception}');\n    }\n\n    final products = productsState.model;\n    \n    return ListView.builder(\n      itemCount: products.length,\n      itemBuilder: (context, index) {\n        final product = products[index];\n        return ListTile(\n          title: Text(product.name),\n          subtitle: Text('\\$${product.price}'),\n        );\n      },\n    );\n  }\n}\n```\n### Creating and Updating\n\n```dart\nclass ProductFormScreen extends ConsumerWidget {\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    return ElevatedButton(\n      onPressed: () async {\n        // Create new product\n        final newProduct = await Product(\n          name: 'New Product',\n          price: 99.99,\n        ).save();\n        \n        // Update existing product\n        final updatedProduct = await Product(\n          name: 'Updated Product',\n          price: 149.99,\n        ).withKeyOf(existingProduct).save();\n        \n        // Partially update existing product\n        final updatedProduct = await Product(\n          name: 'Product Name',\n          price: 239.99,\n        ).withKeyOf(existingProduct).save(\n          params: {\n            \"updatedFields\": jsonEncode(['price']), \n            // This informs the adapter to only send these fields, hence improving performance\n          },\n        );\n      },\n      child: Text('Save Product'),\n    );\n  }\n}\n```\n\n### Watching with Relationships\n\n```dart\nclass ProductDetailsScreen extends ConsumerWidget {\n  final String productId;\n\n  const ProductDetailsScreen({required this.productId});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final productState = ref.products.watchOne(\n      productId,\n      syncLocal: true,\n      alsoWatch: (prod) =\u003e [\n        prod.category,\n        prod.reviews,\n      ],\n    );\n\n    if (productState.isLoading) {\n      return CircularProgressIndicator();\n    }\n\n    if (productState.hasException) {\n      return Text('Error: ${productState.exception}');\n    }\n\n    if (!productState.hasModel) {\n      return Text('Product not found');\n    }\n\n    final product = productState.model;\n    final category = product.category?.value;\n    final reviews = product.reviews?.toList() ?? [];\n\n    return Column(\n      children: [\n        Text(product.name),\n        if (category != null)\n          Text('Category: ${category.name}'),\n        Text('Reviews (${reviews.length}):'),\n        ...reviews.map((review) =\u003e Text(review.text)),\n      ],\n    );\n  }\n}\n```\n### Real-time Updates\n\n```dart\nclass ProductUpdatesScreen extends ConsumerWidget {\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    return StreamBuilder\u003cRealtimeMessage\u003e(\n      stream: ref.products.appwriteAdapter.subscribe(),\n      builder: (context, snapshot) {\n        if (snapshot.hasData) {\n          final message = snapshot.data!;\n          return ListTile(\n            title: Text('Collection Update'),\n            subtitle: Text('Event: ${message.event}'),\n          );\n        }\n        return SizedBox();\n      },\n    );\n  }\n}\n```\n\n### Advanced Queries\n\n```dart\nclass FilteredProductsScreen extends ConsumerWidget {\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final productsState = ref.products.watchAll(\n      syncLocal: true,\n      params: {\n        'where': {\n          'price': {'\u003e=', 100},\n          'name': {'contains': 'Premium'}\n        },\n        'order': 'price:DESC,name:ASC',\n        'limit': 10,\n        'offset': 0,\n      },\n    );\n\n    if (productsState.isLoading) {\n      return CircularProgressIndicator();\n    }\n\n    if (productsState.hasException) {\n      return Text('Error: ${productsState.exception}');\n    }\n\n    final products = productsState.model;\n\n    return ListView.builder(\n      itemCount: products.length,\n      itemBuilder: (context, index) {\n        final product = products[index];\n        return ListTile(\n          title: Text(product.name),\n          subtitle: Text('\\$${product.price}'),\n        );\n      },\n    );\n  }\n}\n```\n### Working with Permissions\n\nAppwrite permissions can be set when creating or updating documents by passing them as params to the save method.\n\n```dart\nclass ProductFormScreen extends ConsumerWidget {\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    return ElevatedButton(\n      onPressed: () async {\n        // Create product with permissions\n        final newProduct = await Product(\n          name: 'New Product',\n          price: 99.99,\n        ).save(\n          params: {\n            'permissions': jsonEncode([\n              {\n                'action': 'read',\n                'role': {\n                  'type': 'any',\n                }\n              },\n              {\n                'action': 'write',\n                'role': {\n                  'type': 'user',\n                  'value': 'user_id',\n                }\n              },\n              {\n                'action': 'update',\n                'role': {\n                  'type': 'team',\n                  'value': 'team_id',\n                }\n              },\n              {\n                'action': 'delete',\n                'role': {\n                  'type': 'team:*', // All teams\n                }\n              },\n            ]),\n          },\n        );\n      },\n      child: Text('Save Product'),\n    );\n  }\n}\n```\n\nAvailable permission configurations:\n- Actions: 'read', 'write', 'create', 'update', 'delete'\n- Role Types:\n  - 'any': Any user\n  - 'users': All authenticated users\n  - 'user': Specific user (requires value)\n  - 'team': Specific team (requires value)\n  - 'team:*': All teams\n\n### Working with HasMany Relationships\n\n```dart\nclass ProductReviewsScreen extends ConsumerWidget {\n  final String productId;\n\n  const ProductReviewsScreen({required this.productId});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final productState = ref.products.watchOne(\n      productId,\n      syncLocal: true,\n      alsoWatch: (prod) =\u003e [prod.reviews],\n    );\n\n    if (productState.isLoading) {\n      return CircularProgressIndicator();\n    }\n\n    if (productState.hasException) {\n      return Text('Error: ${productState.exception}');\n    }\n\n    if (!productState.hasModel) {\n      return Text('Product not found');\n    }\n\n    final product = productState.model;\n    // Always use toList() for HasMany relationships\n    final reviews = product.reviews?.toList() ?? [];\n\n    return Column(\n      children: [\n        Text('${product.name} - Reviews'),\n        if (reviews.isEmpty)\n          Text('No reviews yet'),\n        ...reviews.map((review) =\u003e ReviewCard(review)),\n      ],\n    );\n  }\n}\n```\n## Important Notes\n\n### DataState Handling\nAlways handle all DataState conditions in your widgets:\n- `isLoading`: Initial loading state\n- `hasException`: Error state with `exception` details\n- `hasModel`: Whether the model is available\n- `model`: The actual data\n\n### Relationship Best Practices\n1. Always use `toList()` when accessing HasMany relationships:\n   ```dart\n   // Correct\n   final reviews = product.reviews?.toList() ?? [];\n   \n   // Incorrect\n   final reviews = product.reviews?.value; // Don't use .value for HasMany\n   ```\n\n2. Use `alsoWatch` for efficient relationship loading:\n   ```dart\n   final productState = ref.products.watchOne(\n     productId,\n     syncLocal: true,\n     alsoWatch: (prod) =\u003e [\n       prod.category,  // BelongsTo relationship\n       prod.reviews,   // HasMany relationship\n     ],\n   );\n   ```\n\n### Offline Synchronization\n1. Always use `syncLocal: true` when watching data to enable offline support:\n   ```dart\n   ref.products.watchAll(syncLocal: true);\n   ref.products.watchOne(id, syncLocal: true);\n   ```\n\n2. Data will be automatically synchronized when the connection is restored\n\n## Convention Guidelines\n\n1. Collection IDs should be the plural form of the model name (e.g., \"products\" for Product model)\n2. Model names should be in PascalCase (e.g., ProductVariant)\n3. All required Appwrite collection attributes should be defined in the model class\n4. Use `@JsonKey(readValue: $)` for Appwrite metadata fields (id, createdAt, updatedAt)\n5. Follow Flutter Data's relationship conventions for BelongsTo and HasMany\n\n## Common Issues and Solutions\n\n1. **Collection Not Found**: Ensure your Appwrite collection ID matches the plural form of your model name\n2. **Permission Denied**: Check if permissions are properly set in the save method params\n3. **Relationship Loading Issues**: Make sure to use `alsoWatch` and `toList()` appropriately\n4. **Offline Data Not Syncing**: Verify `syncLocal: true` is set when watching data\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request. Here's how you can contribute:\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/AmazingFeature`)\n3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)\n4. Push to the branch (`git push origin feature/AmazingFeature`)\n5. Open a Pull Request\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Additional Resources\n\n- [Flutter Data Documentation](https://flutterdata.dev)\n- [Appwrite Documentation](https://appwrite.io/docs)\n- [Example Project](https://github.com/cybroidtech/appwrite_offline/tree/main/example)\n- [GitHub Repository](https://github.com/cybroidtech/appwrite_offline)\n\n## Support\n\nIf you find this package helpful, please give it a ⭐️ on [GitHub](https://github.com/cybroidtech/appwrite_offline)!\n\nFor bugs or feature requests, please [create an issue](https://github.com/cybroidtech/appwrite_offline/issues).\n\n## Acknowledgments\n\n- Thanks to the Flutter Data team for their excellent work\n- Thanks to the Appwrite team for their fantastic backend solution\n- Thanks to all contributors who help improve this package","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcybroidtech%2Fappwrite_offline","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcybroidtech%2Fappwrite_offline","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcybroidtech%2Fappwrite_offline/lists"}