Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,36 @@ scripts:
concurrency: 1
packageFilters:
dirExists: test
description: Run tests for all packages
flutter: false
description: Run tests for Dart packages

test:flutter:
run: flutter test
exec:
concurrency: 1
packageFilters:
dirExists: test
flutter: true
description: Run tests for Flutter packages

test:all:
run: melos run test && melos run test:flutter
description: Run tests for all packages (Dart and Flutter)

test:coverage:
run: dart test --coverage=coverage
exec:
concurrency: 1
packageFilters:
dirExists: test
description: Run tests with coverage for all packages
flutter: false
description: Run tests with coverage for Dart packages

test:flutter:coverage:
run: flutter test --coverage
exec:
concurrency: 1
packageFilters:
dirExists: test
flutter: true
description: Run tests with coverage for Flutter packages
1 change: 1 addition & 0 deletions packages/syncache_flutter/.flutter-plugins-dependencies
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/devayo/.pub-cache/hosted/pub.dev/connectivity_plus-7.0.0/","native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"connectivity_plus","path":"/home/devayo/.pub-cache/hosted/pub.dev/connectivity_plus-7.0.0/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"connectivity_plus","path":"/home/devayo/.pub-cache/hosted/pub.dev/connectivity_plus-7.0.0/","native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"connectivity_plus","path":"/home/devayo/.pub-cache/hosted/pub.dev/connectivity_plus-7.0.0/","native_build":false,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"connectivity_plus","path":"/home/devayo/.pub-cache/hosted/pub.dev/connectivity_plus-7.0.0/","native_build":true,"dependencies":[],"dev_dependency":false}],"web":[{"name":"connectivity_plus","path":"/home/devayo/.pub-cache/hosted/pub.dev/connectivity_plus-7.0.0/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":[]}],"date_created":"2026-03-14 13:27:00.475588","version":"3.41.2","swift_package_manager_enabled":{"ios":false,"macos":false}}
23 changes: 23 additions & 0 deletions packages/syncache_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.0] - 2026-03-14

### Added

- `SyncacheScope` - InheritedWidget for dependency injection of cache instances
- `MultiSyncacheScope` - Helper for providing multiple cache types without deep nesting
- `CacheBuilder` - StreamBuilder-style widget for reactive cache data display
- `CacheConsumer` - Consumer pattern widget with separate listener callback
- `SyncacheLifecycleObserver` - App lifecycle and reconnect handling
- Automatic refetch on app resume (configurable minimum pause duration)
- Automatic refetch on connectivity restoration
- `FlutterNetwork` - Connectivity detection using `connectivity_plus`
- Debounced connectivity change events
- Concurrent initialization handled via Completer pattern
- `SyncacheValueListenable` - ValueListenable wrapper for use with ValueListenableBuilder
- `LifecycleConfig` - Configuration class for lifecycle behavior customization
21 changes: 21 additions & 0 deletions packages/syncache_flutter/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Hameed Abdullateef

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
236 changes: 236 additions & 0 deletions packages/syncache_flutter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
# syncache_flutter

Flutter integration for [Syncache](https://pub.dev/packages/syncache) - lifecycle management, widgets, and connectivity detection.

## Installation

```yaml
dependencies:
syncache: ^0.1.0
syncache_flutter: ^0.1.0
```

## Quick Start

### 1. Provide the cache with SyncacheScope

Wrap your app (or a subtree) with `SyncacheScope` to provide cache instances to descendants:

```dart
import 'package:syncache/syncache.dart';
import 'package:syncache_flutter/syncache_flutter.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();

// Initialize connectivity detection
final network = FlutterNetwork();
await network.initialize();

// Create your cache
final userCache = Syncache<User>(
store: MemoryStore<User>(),
network: network,
);

runApp(
SyncacheScope<User>(
cache: userCache,
network: network,
child: MyApp(),
),
);
}
```

### 2. Display cached data with CacheBuilder

Use `CacheBuilder` to reactively display cached data:

```dart
class UserProfile extends StatelessWidget {
final String userId;

const UserProfile({required this.userId});

@override
Widget build(BuildContext context) {
return CacheBuilder<User>(
cacheKey: 'user:$userId',
fetch: (request) => api.getUser(userId),
builder: (context, snapshot) {
if (snapshot.hasError) {
return ErrorWidget(snapshot.error!);
}
if (!snapshot.hasData) {
return const CircularProgressIndicator();
}
return Text('Hello, ${snapshot.data!.name}');
},
);
}
}
```

## Features

### SyncacheScope

Provides cache instances to the widget subtree via `InheritedWidget`:

```dart
// Access cache anywhere in the subtree
final cache = SyncacheScope.of<User>(context);

// Access the lifecycle observer
final observer = SyncacheScope.observerOf<User>(context);
```

### MultiSyncacheScope

Provide multiple cache types without deep nesting:

```dart
MultiSyncacheScope(
network: flutterNetwork,
configs: [
SyncacheScopeConfig<User>(userCache),
SyncacheScopeConfig<Post>(postCache),
SyncacheScopeConfig<Settings>(settingsCache),
],
child: MyApp(),
)
```

### CacheBuilder

StreamBuilder-style widget for reactive cache display:

```dart
CacheBuilder<User>(
cacheKey: 'user:123',
fetch: fetchUser,
policy: Policy.staleWhileRefresh,
ttl: Duration(minutes: 5),
initialData: cachedUser,
buildWhen: (previous, current) => previous.id != current.id,
builder: (context, snapshot) {
// Build UI based on snapshot state
},
)
```

### CacheConsumer

Consumer pattern with separate listener for side effects:

```dart
CacheConsumer<User>(
cacheKey: 'user:123',
fetch: fetchUser,
listener: (context, data) {
// Handle side effects (e.g., show snackbar, navigate)
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('User updated: ${data.name}')),
);
},
builder: (context, snapshot) {
// Build UI
},
)
```

### FlutterNetwork

Connectivity detection using `connectivity_plus`:

```dart
final network = FlutterNetwork(
debounceDuration: Duration(milliseconds: 500),
);
await network.initialize();

// Check current status
print('Online: ${network.isOnline}');

// Listen to connectivity changes
network.onConnectivityChanged.listen((isOnline) {
print('Connectivity changed: $isOnline');
});
```

### Lifecycle Management

Configure automatic refetching on app resume and connectivity restoration:

```dart
SyncacheScope<User>(
cache: userCache,
network: network,
config: LifecycleConfig(
refetchOnResume: true,
refetchOnResumeMinDuration: Duration(minutes: 1),
refetchOnReconnect: true,
onRefetchError: (key, error, stackTrace) {
logger.warning('Failed to refetch $key: $error');
},
),
child: MyApp(),
)
```

### SyncacheValueListenable

Use with `ValueListenableBuilder` for more control:

```dart
final listenable = cache.toValueListenable(
key: 'user:123',
fetch: fetchUser,
);

ValueListenableBuilder<AsyncSnapshot<User>>(
valueListenable: listenable,
builder: (context, snapshot, child) {
// Build UI
},
)

// Trigger manual refresh
await listenable.refresh();

// Don't forget to dispose
listenable.dispose();
```

## API Reference

### Widgets

| Widget | Description |
|--------|-------------|
| `SyncacheScope<T>` | InheritedWidget for cache dependency injection |
| `MultiSyncacheScope` | Provides multiple cache types without nesting |
| `CacheBuilder<T>` | StreamBuilder-style reactive cache display |
| `CacheConsumer<T>` | Consumer pattern with listener callback |

### Classes

| Class | Description |
|-------|-------------|
| `FlutterNetwork` | Connectivity detection with debouncing |
| `SyncacheLifecycleObserver<T>` | App lifecycle and reconnect handling |
| `LifecycleConfig` | Configuration for lifecycle behavior |
| `SyncacheValueListenable<T>` | ValueListenable wrapper for cache streams |
| `WatcherRegistration<T>` | Registration info for lifecycle-based refetching |

## Requirements

- Dart SDK: ^3.0.0
- Flutter: >=3.10.0
- syncache: ^0.1.0
- connectivity_plus: ^7.0.0

## License

MIT License - see [LICENSE](LICENSE) for details.
10 changes: 10 additions & 0 deletions packages/syncache_flutter/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
include: package:flutter_lints/flutter.yaml

linter:
rules:
- prefer_const_constructors
- prefer_const_declarations
- prefer_final_fields
- prefer_final_locals
- avoid_print
- require_trailing_commas
45 changes: 45 additions & 0 deletions packages/syncache_flutter/example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
/coverage/

# Symbolication related
app.*.symbols

# Obfuscation related
app.*.map.json

# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
Loading
Loading