flutter_android_enterprise 0.1.1
flutter_android_enterprise: ^0.1.1 copied to clipboard
Android Enterprise and managed-device APIs for Flutter — managed configuration, kiosk mode, compliance reporting, and profile awareness.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_android_enterprise/flutter_android_enterprise.dart';
void main() {
runApp(const EnterpriseExampleApp());
}
class EnterpriseExampleApp extends StatelessWidget {
const EnterpriseExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Android Enterprise',
theme: ThemeData(colorSchemeSeed: Colors.blue, useMaterial3: true),
home: const EnterpriseDashboard(),
);
}
}
class EnterpriseDashboard extends StatefulWidget {
const EnterpriseDashboard({super.key});
@override
State<EnterpriseDashboard> createState() => _EnterpriseDashboardState();
}
class _EnterpriseDashboardState extends State<EnterpriseDashboard> {
final EnterpriseManager _enterprise = EnterpriseManager();
StreamSubscription<Map<String, String>>? _restrictionsSubscription;
String _managedConfigurations = 'Loading...';
String _lastRestrictionsEvent = 'Waiting for managed config updates...';
String _status = 'Ready';
String _userProfiles = 'Not loaded';
bool _deviceManaged = false;
bool _managedProfile = false;
bool _deviceOwnerActive = false;
@override
void initState() {
super.initState();
_restrictionsSubscription = _enterprise.restrictionsChanged.listen((
config,
) {
setState(() {
_lastRestrictionsEvent = config.isEmpty
? 'No managed configuration received yet.'
: config.toString();
});
});
_loadTier1State();
}
Future<void> _loadTier1State() async {
try {
final deviceManaged = await _enterprise.isDeviceManaged();
final managedProfile = await _enterprise.isManagedProfile();
final deviceOwnerActive = await _enterprise.isDeviceOwnerActive();
final configurations = await _enterprise.getManagedConfig();
if (!mounted) {
return;
}
setState(() {
_deviceManaged = deviceManaged;
_managedProfile = managedProfile;
_deviceOwnerActive = deviceOwnerActive;
_managedConfigurations = configurations.raw.isEmpty
? 'No app restrictions are set.'
: configurations.raw.toString();
_status = 'Tier 1 checks completed.';
});
} on EnterpriseChannelException catch (error) {
if (!mounted) {
return;
}
setState(() {
_status = 'Enterprise channel error (${error.code}): ${error.message}';
});
} on PlatformException catch (error) {
if (!mounted) {
return;
}
setState(() {
_status =
'Failed to load enterprise state: ${error.message ?? error.code}';
});
}
}
Future<void> _reportCompliance() async {
await _enterprise.reportComplianceState(
ComplianceState.compliant,
'All local checks passed in the example app.',
data: const <String, String>{'source': 'example'},
);
if (!mounted) {
return;
}
setState(() {
_status = 'Reported compliant state to the EMM channel.';
});
}
Future<void> _enterKioskMode() async {
await _enterprise.startKioskMode(
allowedPackages: const <String>[
'com.example.flutter_android_enterprise_example',
],
);
if (!mounted) {
return;
}
setState(() {
_status = 'Kiosk mode requested.';
});
}
Future<void> _leaveKioskMode() async {
await _enterprise.stopKioskMode();
if (!mounted) {
return;
}
setState(() {
_status = 'Kiosk mode stopped.';
});
}
Future<void> _checkPrivateSpacePolicy() async {
try {
final restricted = await _enterprise.isPrivateSpaceRestricted();
if (!mounted) {
return;
}
setState(() {
_status =
'Private Space creation is ${restricted ? 'blocked' : 'allowed'}.';
});
} on RequiresDeviceOwnerException {
if (!mounted) {
return;
}
setState(() {
_status = 'Private Space policy requires Device Owner provisioning.';
});
} on NotSupportedBelowApiException catch (error) {
if (!mounted) {
return;
}
setState(() {
_status =
'Private Space policy requires Android API ${error.requiredApi}+.';
});
}
}
Future<void> _loadUserProfiles() async {
try {
final profiles = await _enterprise.getUserProfiles();
if (!mounted) {
return;
}
setState(() {
_userProfiles = profiles.isEmpty
? 'No profiles returned.'
: profiles
.map(
(profile) =>
'${profile.userType} (running=${profile.isRunning})',
)
.join(', ');
_status = 'Loaded ${profiles.length} user profile(s).';
});
} on RequiresDeviceOwnerException catch (error) {
if (!mounted) {
return;
}
setState(() {
_status = error.message;
});
} on EnterpriseChannelException catch (error) {
if (!mounted) {
return;
}
setState(() {
_status = 'Failed to load profiles: ${error.message ?? error.code}';
});
}
}
Future<void> _blockPrivateSpace() async {
await _setPrivateSpaceAllowed(false);
}
Future<void> _allowPrivateSpace() async {
await _setPrivateSpaceAllowed(true);
}
Future<void> _setPrivateSpaceAllowed(bool allowed) async {
try {
await _enterprise.setPrivateSpaceAllowed(allowed);
if (!mounted) {
return;
}
setState(() {
_status =
'Private Space creation is now ${allowed ? 'allowed' : 'blocked'}.';
});
} on RequiresDeviceOwnerException catch (error) {
if (!mounted) {
return;
}
setState(() {
_status = error.message;
});
} on NotSupportedBelowApiException catch (error) {
if (!mounted) {
return;
}
setState(() {
_status =
'Private Space policy requires Android API ${error.requiredApi}+.';
});
} on EnterpriseChannelException catch (error) {
if (!mounted) {
return;
}
setState(() {
_status = 'Private Space policy failed: ${error.message ?? error.code}';
});
}
}
@override
void dispose() {
_restrictionsSubscription?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Flutter Android Enterprise')),
body: ListView(
padding: const EdgeInsets.all(16),
children: <Widget>[
const Text(
'Works without provisioning',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
_InfoTile(label: 'Device managed', value: _deviceManaged.toString()),
_InfoTile(
label: 'Managed profile',
value: _managedProfile.toString(),
),
_InfoTile(
label: 'Device owner active',
value: _deviceOwnerActive.toString(),
),
_InfoTile(label: 'Managed config', value: _managedConfigurations),
_InfoTile(
label: 'Restrictions stream',
value: _lastRestrictionsEvent,
),
const SizedBox(height: 16),
Wrap(
spacing: 12,
runSpacing: 12,
children: <Widget>[
FilledButton(
onPressed: _loadTier1State,
child: const Text('Refresh'),
),
FilledButton(
onPressed: _reportCompliance,
child: const Text('Report compliance'),
),
],
),
const SizedBox(height: 24),
const Text(
'Requires Device Owner',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
const Text(
'Provision with adb shell dpm set-device-owner '
'com.example.flutter_android_enterprise_example/'
'com.example.flutter_android_enterprise.DeviceAdminReceiverImpl',
),
const SizedBox(height: 12),
_InfoTile(label: 'User profiles', value: _userProfiles),
const SizedBox(height: 12),
Wrap(
spacing: 12,
runSpacing: 12,
children: <Widget>[
FilledButton(
onPressed: _enterKioskMode,
child: const Text('Start kiosk mode'),
),
FilledButton(
onPressed: _leaveKioskMode,
child: const Text('Stop kiosk mode'),
),
FilledButton(
onPressed: _checkPrivateSpacePolicy,
child: const Text('Check Private Space policy'),
),
FilledButton(
onPressed: _loadUserProfiles,
child: const Text('List user profiles'),
),
FilledButton(
onPressed: _blockPrivateSpace,
child: const Text('Block Private Space'),
),
FilledButton(
onPressed: _allowPrivateSpace,
child: const Text('Allow Private Space'),
),
],
),
const SizedBox(height: 24),
Text(_status, style: Theme.of(context).textTheme.bodyLarge),
],
),
);
}
}
class _InfoTile extends StatelessWidget {
const _InfoTile({required this.label, required this.value});
final String label;
final String value;
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(title: Text(label), subtitle: Text(value)),
);
}
}