iws_version_manager 1.2.0
iws_version_manager: ^1.2.0 copied to clipboard
IWS Version Manager makes it easy to notify new app versions and block versions that are no longer supported.
IWS Version Manager #
IWS Version Manager makes it easy to notify users about new app versions, manage version compatibility, and block versions that are no longer supported. This package provides comprehensive version management with automatic notifications, device tracking, and flexible UI components.
Features #
- Automatic Version Checking: Automatically check for new versions on app startup
- Multiple Notification Types: Support for mandatory and optional updates
- Rich UI Components: Pre-built widgets for different notification styles
- Release Notes: Display formatted release notes with markdown support
- Device Tracking: Track device usage and user analytics
- Platform Support: Cross-platform support with web-specific features
- Custom Release Note Rendering: Customizable release note display
- Connection Error Handling: Graceful handling of network issues
- BLoC State Management: Built-in state management for version status
Getting Started #
1. Environment Setup #
Add the IWS API key and Version Code as DART variables at compile time:
flutter run --dart-define=IWS_API_KEY=your_api_key_here --dart-define=APP_VERSION_CODE=1
Or add them to your launch.json for VS Code:
{
"configurations": [
{
"name": "Flutter",
"type": "dart",
"request": "launch",
"program": "lib/main.dart",
"args": [
"--dart-define=IWS_API_KEY=your_api_key_here",
"--dart-define=APP_VERSION_CODE=1"
]
}
]
}
2. Initialize Version Manager #
Initialize the version checking in your app startup:
void main() {
runApp(MyApp());
// Initialize version manager
if (!IwsVersionManager().widgetsInitialized()) {
IwsVersionManager(
deviceId: 'unique-device-id',
userIdentifier: 'user-identifier'
).initializeWidgets();
}
}
3. Configure Web Reload Behavior (Optional) #
For Flutter Web apps, you can configure how the package forces app updates when a new version is detected:
IwsVersionManager(
webReloadConfig: const WebReloadConfig(
strategy: WebReloadStrategy.serviceWorkerSkipWaiting,
fallbackStrategy: WebReloadStrategy.hardReset,
serviceWorkerActivationTimeoutMs: 3000,
trigger: WebReloadTrigger.automaticMandatoryOnly,
),
).initializeWidgets();
Available strategies:
WebReloadStrategy.cacheBuster: reloads with a cache-busting query parameter.WebReloadStrategy.clearCaches: clears Cache Storage and reloads with cache-buster.WebReloadStrategy.hardReset: clears Cache Storage, unregisters Service Workers, and reloads with cache-buster.WebReloadStrategy.serviceWorkerSkipWaiting: tries to activate a waiting Service Worker and then reloads; if no waiting worker is available, it usesfallbackStrategy.
Available triggers:
WebReloadTrigger.manual: only reloads when user presses the web update button.WebReloadTrigger.automatic: auto-reloads when any update is detected.WebReloadTrigger.automaticMandatoryOnly: auto-reloads only for mandatory updates.
Note: aggressive strategies may affect offline behavior until the new app shell is cached again.
If your app uses a custom Service Worker, make sure it handles skip waiting messages:
self.addEventListener('message', (event) => {
if (event.data === 'skipWaiting' || event.data?.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
Usage Examples #
1. Automatic Scaffold Messages #
The easiest way to show version notifications is using IwsVersionScaffoldMessage:
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return IwsVersionScaffoldMessage(
showConnectionErrors: true, // Show connection error notifications
messageHeight: 120.0, // Customize notification height
child: Scaffold(
appBar: AppBar(title: Text('My App')),
body: YourContent(),
),
);
}
}
2. Version Icon Button #
Add a version status icon to your app bar:
class MyAppBar extends StatelessWidget implements PreferredSizeWidget {
@override
Widget build(BuildContext context) {
return AppBar(
title: Text('My App'),
actions: [
IwsVersionIconButton(
showConnectionError: true, // Show icon for connection errors
),
],
);
}
@override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
}
3. Custom Version Listener #
Handle version events with custom logic:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return IwsVersionListener(
onUpdateAvailable: (lastVersion, downloadLink) {
// Handle optional update
print('Update available: ${lastVersion?.versionName}');
// Show custom UI or trigger custom logic
},
onMandatoryUpdate: (lastVersion, downloadLink) {
// Handle mandatory update
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text('Update Required'),
content: Text('You must update to continue using the app.'),
actions: [
TextButton(
onPressed: () => _launchUpdate(downloadLink),
child: Text('Update Now'),
),
],
),
);
},
onConnectionError: () {
// Handle connection errors
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Connection error')),
);
},
onPlatformNotSupported: () {
// Handle unsupported platform
print('Platform not supported');
},
child: YourAppContent(),
);
}
void _launchUpdate(String? downloadLink) {
if (downloadLink != null) {
// Launch download URL
}
}
}
4. Custom Version Builder #
Build different UI based on version status:
class VersionAwareWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return IwsVersionBuilder(
updateAvailable: (lastVersion) {
return Card(
color: Colors.green.shade100,
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
Icon(Icons.new_releases, color: Colors.green),
Text('Version ${lastVersion?.versionName} available!'),
ElevatedButton(
onPressed: () => _downloadUpdate(),
child: Text('Download'),
),
],
),
),
);
},
mandatoryUpdate: (lastVersion) {
return Container(
color: Colors.orange.shade100,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.warning, size: 64, color: Colors.orange),
Text('Mandatory Update Required'),
Text('Version: ${lastVersion?.versionName}'),
ElevatedButton(
onPressed: () => _downloadUpdate(),
child: Text('Update Now'),
),
],
),
),
);
},
connectionError: () {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.wifi_off, size: 64, color: Colors.red),
Text('Connection Error'),
ElevatedButton(
onPressed: () => _retryConnection(),
child: Text('Retry'),
),
],
),
);
},
platformNotSupported: () {
return Center(
child: Text('Platform not supported'),
);
},
child: YourNormalContent(),
);
}
void _downloadUpdate() {
// Handle download logic
}
void _retryConnection() {
IwsVersionManager().versionBloc.add(CheckAppVersion());
}
}
5. Manual Version Checking #
Check for versions programmatically:
class VersionService {
static Future<void> checkForUpdates() async {
try {
final versionStatus = await IwsVersionManager(
deviceId: 'device-id',
userIdentifier: 'user-id',
).checkPlatformVersion();
if (versionStatus.newVersionAvailable) {
print('New version available: ${versionStatus.lastVersion?.versionName}');
if (versionStatus.mandatoryUpdate) {
print('This is a mandatory update!');
// Force user to update
} else {
print('Optional update available');
// Show optional update notification
}
// Access version details
final version = versionStatus.lastVersion;
if (version != null) {
print('Version Code: ${version.versionCode}');
print('Version Name: ${version.versionName}');
print('Download Link: ${version.downloadLink}');
print('Release Notes: ${version.rawReleaseNotes}');
print('Published At: ${version.publishedAt}');
print('Minimal Version: ${version.minimalVersion}');
}
}
if (!versionStatus.supportedPlatform) {
print('Current platform is not supported');
}
} catch (e) {
if (e is SdkNotConfigureException) {
print('SDK not configured: ${e.message}');
} else {
print('Error checking version: $e');
}
}
}
}
6. Custom Release Note Rendering #
Customize how release notes are displayed:
void setupCustomReleaseNotes() {
IwsVersionManager().releaseNoteBuilder = (String plainText, dynamic richText) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'What\'s New',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
child: MarkdownBody(data: plainText),
),
],
);
};
}
7. BLoC Integration #
Access the version BLoC directly for advanced use cases:
class CustomVersionWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<VersionBloc, VersionState>(
bloc: IwsVersionManager().versionBloc,
builder: (context, state) {
switch (state.status) {
case VersionStatus.starting:
return CircularProgressIndicator();
case VersionStatus.updated:
return Text('App is up to date');
case VersionStatus.updateAvailable:
return _buildUpdateAvailable(state);
case VersionStatus.mandatoryUpdate:
return _buildMandatoryUpdate(state);
case VersionStatus.connectionError:
return _buildConnectionError();
case VersionStatus.platformNotSupported:
return _buildPlatformNotSupported();
}
},
);
}
Widget _buildUpdateAvailable(VersionState state) {
return Card(
child: ListTile(
leading: Icon(Icons.new_releases, color: Colors.green),
title: Text('Update Available'),
subtitle: Text('Version ${state.lastVersion?.versionName}'),
trailing: TextButton(
onPressed: () => _downloadUpdate(state.downloadLink),
child: Text('Download'),
),
),
);
}
Widget _buildMandatoryUpdate(VersionState state) {
return Card(
color: Colors.orange.shade100,
child: ListTile(
leading: Icon(Icons.warning, color: Colors.orange),
title: Text('Mandatory Update'),
subtitle: Text('Update required to continue'),
trailing: ElevatedButton(
onPressed: () => _downloadUpdate(state.downloadLink),
child: Text('Update'),
),
),
);
}
Widget _buildConnectionError() {
return Card(
child: ListTile(
leading: Icon(Icons.error, color: Colors.red),
title: Text('Connection Error'),
trailing: IconButton(
onPressed: () => _retryCheck(),
icon: Icon(Icons.refresh),
),
),
);
}
Widget _buildPlatformNotSupported() {
return Card(
child: ListTile(
leading: Icon(Icons.block, color: Colors.grey),
title: Text('Platform Not Supported'),
subtitle: Text('This platform is not supported'),
),
);
}
void _downloadUpdate(String? downloadLink) {
// Implementation
}
void _retryCheck() {
IwsVersionManager().versionBloc.add(CheckAppVersion());
}
}
Configuration Options #
Version Status Types #
The package provides several version status types:
VersionStatus.starting: Initial state, checking in progressVersionStatus.updated: App is up to dateVersionStatus.updateAvailable: Optional update availableVersionStatus.mandatoryUpdate: Required update availableVersionStatus.connectionError: Network connection errorVersionStatus.platformNotSupported: Platform not supported
Widget Configuration #
Each widget supports various configuration options:
IwsVersionScaffoldMessage
showConnectionErrors: Show notifications for connection errorsmessageHeight: Height of notification messages
IwsVersionIconButton
showConnectionError: Show icon for connection errors
Error Handling #
The package includes built-in error handling:
try {
final status = await IwsVersionManager().checkPlatformVersion();
// Handle status
} on SdkNotConfigureException catch (e) {
// Handle configuration errors
print('Configuration error: ${e.message}');
} on FetchDataException catch (e) {
// Handle network errors
print('Network error: ${e.httpCode}');
} catch (e) {
// Handle other errors
print('Unknown error: $e');
}
Data Models #
ApplicationVersion #
class ApplicationVersion {
int versionCode; // Numeric version code
String versionName; // Human-readable version name
dynamic releaseNotes; // Rich text release notes
String? rawReleaseNotes; // Plain text release notes
String? downloadLink; // Direct download URL
DateTime? publishedAt; // Publication date
int minimalVersion; // Minimum supported version
String? platformDownloadLink; // Platform-specific download URL
}
AppVersionStatus #
class AppVersionStatus {
bool newVersionAvailable; // New version is available
bool mandatoryUpdate; // Update is mandatory
bool supportedPlatform; // Platform is supported
ApplicationVersion? lastVersion; // Latest version information
}
Web Support #
The package includes web-specific features:
- Cache Clearing: Automatically clears browser cache on web updates
- Page Reload: Forces page reload for web applications
- Conditional Rendering: Shows reload button only on web platform
Best Practices #
- Initialize Early: Call
initializeWidgets()as early as possible in your app lifecycle - Handle Errors: Always implement error handling for network issues
- Test Updates: Use the dry-run publish option to test your update flow
- User Experience: Provide clear messaging about update requirements
- Graceful Degradation: Ensure your app works even when version checking fails
Advanced Usage #
Custom BLoC Events #
Trigger version checks manually:
// Trigger a version check
IwsVersionManager().versionBloc.add(CheckAppVersion());
State Monitoring #
Monitor version state changes:
IwsVersionManager().versionBloc.stream.listen((state) {
print('Version status changed: ${state.status}');
});