flutter_adaptive_assist 1.0.0
flutter_adaptive_assist: ^1.0.0 copied to clipboard
This package provides a cohesive, cross-platform API for developers to listen to, react to, and utilize high-level user-defined system settings and less-common device sensors to create truly adaptive UI/UX.
Flutter Adaptive Assist #
A Flutter plugin that provides unified access to platform-specific accessibility settings, enabling developers to create truly adaptive user interfaces that respect system accessibility preferences.
Features #
- ๐จ Monochrome Mode Detection - Detect when users have enabled grayscale/color correction
- ๐ฌ Reduce Motion Support - Respond to user preferences for reduced animations
- ๐ Bold Text Detection - Identify when users prefer bold text (iOS)
- ๐ High Contrast Mode - Detect high contrast preferences
- ๐ Text Scale Factor - Access the system's text size multiplier
- ๐ก Reactive Streams - Listen to real-time changes in accessibility settings
- ๐ Type-Safe - Full null-safety support
- ๐งช Testable - Built with testing in mind
Platform Support #
| Feature | Android | iOS |
|---|---|---|
| Monochrome Mode | โ (API 21+) | โ (iOS 13.0+) |
| Reduce Motion | โ (API 21+) | โ (iOS 13.0+) |
| Bold Text | โ | โ (iOS 13.0+) |
| High Contrast | โ (API 21+) | โ (iOS 13.0+) |
| Text Scale Factor | โ (API 21+) | โ (iOS 13.0+) |
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
flutter_adaptive_assist: ^1.0.0
Then run:
flutter pub get
Usage #
Basic Setup #
Initialize the plugin early in your app (e.g., in main()):
import 'package:flutter_adaptive_assist/adaptive_assist.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
AdaptiveAssist.ensureInitialized();
runApp(MyApp());
}
Check Current Settings #
// Check individual settings
final isMonochrome = await AdaptiveAssist.getMonochromeModeEnabled();
if (isMonochrome) {
// Apply monochrome-friendly color scheme
}
// Or get all settings at once (more efficient)
final config = await AdaptiveAssist.getConfig();
if (config.monochromeModeEnabled) {
// Apply monochrome-friendly colors
}
if (config.reduceMotionEnabled) {
// Disable or simplify animations
}
if (config.textScaleFactor > 1.5) {
// Adjust layout for larger text
}
Listen to Changes #
@override
void initState() {
super.initState();
// Listen to monochrome mode changes
AdaptiveAssist.monochromeModeEnabledStream.listen((isEnabled) {
setState(() {
_isMonochrome = isEnabled;
});
});
}
Complete Example #
import 'package:flutter/material.dart';
import 'package:flutter_adaptive_assist/adaptive_assist.dart';
class AdaptiveHomePage extends StatefulWidget {
@override
_AdaptiveHomePageState createState() => _AdaptiveHomePageState();
}
class _AdaptiveHomePageState extends State<AdaptiveHomePage> {
AdaptiveConfig _config = AdaptiveConfig();
@override
void initState() {
super.initState();
_loadConfig();
// Listen to changes
AdaptiveAssist.monochromeModeEnabledStream.listen((enabled) {
_loadConfig();
});
}
Future<void> _loadConfig() async {
final config = await AdaptiveAssist.getConfig();
setState(() {
_config = config;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Adaptive UI Demo'),
),
body: AnimatedContainer(
duration: _config.reduceMotionEnabled
? Duration.zero
: Duration(milliseconds: 300),
color: _config.monochromeModeEnabled
? Colors.grey[300]
: Colors.blue[100],
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Accessibility Status',
style: TextStyle(
fontSize: 20 * _config.textScaleFactor,
fontWeight: _config.boldTextEnabled
? FontWeight.bold
: FontWeight.normal,
),
),
SizedBox(height: 20),
_buildStatusCard('Monochrome Mode', _config.monochromeModeEnabled),
_buildStatusCard('Reduce Motion', _config.reduceMotionEnabled),
_buildStatusCard('Bold Text', _config.boldTextEnabled),
_buildStatusCard('High Contrast', _config.highContrastEnabled),
_buildStatusCard('Text Scale', '${_config.textScaleFactor.toStringAsFixed(2)}x'),
],
),
),
),
);
}
Widget _buildStatusCard(String label, dynamic value) {
return Card(
margin: EdgeInsets.symmetric(horizontal: 20, vertical: 8),
child: Padding(
padding: EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label),
Text(
value.toString(),
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
);
}
@override
void dispose() {
// Optional: dispose if you want to clean up
// AdaptiveAssist.dispose();
super.dispose();
}
}
API Reference #
AdaptiveAssist #
Methods
static void ensureInitialized()- Initialize the plugin (safe to call multiple times)static Future<bool> getMonochromeModeEnabled()- Check if monochrome mode is enabledstatic Future<AdaptiveConfig> getConfig()- Get all accessibility settings at oncestatic void clearCache()- Force fresh platform queries on next callstatic void dispose()- Clean up resources (typically called on app disposal)
Streams
static Stream<bool> monochromeModeEnabledStream- Stream of monochrome mode changes
AdaptiveConfig #
A data class containing all accessibility settings:
class AdaptiveConfig {
final bool reduceMotionEnabled;
final bool monochromeModeEnabled;
final bool boldTextEnabled;
final bool highContrastEnabled;
final double textScaleFactor;
}
Testing #
The plugin is designed to be testable. Use the provided test channel for unit tests:
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_adaptive_assist/adaptive_assist.dart';
void main() {
setUp(() {
AdaptiveAssist.reset();
// Set up mock channel
final testChannel = MethodChannel('flutter_adaptive_assist');
AdaptiveAssist.testChannel = testChannel;
// Mock responses
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(testChannel, (call) async {
if (call.method == 'getMonochromeModeEnabled') {
return true;
}
return null;
});
});
test('getMonochromeModeEnabled returns mocked value', () async {
final result = await AdaptiveAssist.getMonochromeModeEnabled();
expect(result, true);
});
}
Platform-Specific Notes #
Android #
- Monochrome Mode: Checks both Color Correction (Daltonizer) and Color Inversion settings
- Reduce Motion: Checks if animation scales are set to very low values (< 0.1)
- Bold Text: Not available system-wide on Android (returns
false) - High Contrast: Available on Android 5.0+ (API 21)
- Minimum SDK: API 21 (Android 5.0 Lollipop)
iOS #
- Monochrome Mode: Uses
UIAccessibility.isGrayscaleEnabled - Reduce Motion: Uses
UIAccessibility.isReduceMotionEnabled - Bold Text: Uses
UIAccessibility.isBoldTextEnabled - High Contrast: Uses
UIAccessibility.isDarkerSystemColorsEnabled(iOS 13.0+) - Text Scale Factor: Maps
UIApplication.shared.preferredContentSizeCategoryto numeric values - Minimum Version: iOS 13.0
Performance Considerations #
- The plugin caches values to minimize platform calls
- Cache is automatically updated when settings change
- Use
getConfig()instead of multiple individual calls when you need several values - Streams are broadcast streams - safe to have multiple listeners
Troubleshooting #
Stream not emitting values #
Make sure you call AdaptiveAssist.ensureInitialized() before subscribing to streams.
Values not updating on Android #
Check that your app has the necessary permissions and that ContentObserver registration succeeded (check logs).
iOS notifications not working #
Ensure you're testing on a real device, as some accessibility features may not work correctly in the simulator.
Contributing #
Contributions are welcome! Please read the contributing guidelines before submitting PRs.
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Credits #
Developed with โค๏ธ for the Flutter community.