installed_apps_manager 1.0.0
installed_apps_manager: ^1.0.0 copied to clipboard
A strictly focused, high-performance Flutter plugin for managing installed applications on Android devices with native pagination and intent handling.
import 'package:flutter/material.dart';
import 'package:installed_apps_manager/installed_apps_manager.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Installed Apps Manager',
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.blue),
home: const AppsListScreen(),
);
}
}
class AppsListScreen extends StatefulWidget {
const AppsListScreen({super.key});
@override
State<AppsListScreen> createState() => _AppsListScreenState();
}
class _AppsListScreenState extends State<AppsListScreen> {
final List<AppInfo> _apps = [];
bool _isLoading = false;
bool _hasMore = true;
int _offset = 0;
final int _limit = 50;
bool _includeSystemApps = false;
final TextEditingController _searchController = TextEditingController();
AppSortBy _sortBy = AppSortBy.appName;
SortOrder _sortOrder = SortOrder.ascending;
@override
void initState() {
super.initState();
_fetchNextPage();
}
Future<void> _fetchNextPage() async {
if (_isLoading || !_hasMore) return;
setState(() => _isLoading = true);
try {
final page = await InstalledAppsManager.getInstalledAppsPaginated(
includeSystemApps: _includeSystemApps,
limit: _limit,
offset: _offset,
searchQuery: _searchController.text,
sortBy: _sortBy,
sortOrder: _sortOrder,
);
setState(() {
_apps.addAll(page.items);
_hasMore = !page.isLastPage;
_offset += _limit;
_isLoading = false;
});
} catch (e) {
setState(() => _isLoading = false);
if (mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Error: $e')));
}
}
}
void _refreshList() {
setState(() {
_apps.clear();
_offset = 0;
_hasMore = true;
});
_fetchNextPage();
}
void _showError(String message) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message), backgroundColor: Colors.red),
);
}
}
void _showSuccess(String message) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message), backgroundColor: Colors.green),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Installed Apps'),
actions: [
IconButton(
icon: Icon(
_includeSystemApps ? Icons.android : Icons.phone_android,
),
tooltip: _includeSystemApps
? 'Hide System Apps'
: 'Show System Apps',
onPressed: () {
_includeSystemApps = !_includeSystemApps;
_refreshList();
},
),
PopupMenuButton<AppSortBy>(
icon: const Icon(Icons.sort),
onSelected: (AppSortBy result) {
if (_sortBy == result) {
_sortOrder = _sortOrder == SortOrder.ascending
? SortOrder.descending
: SortOrder.ascending;
} else {
_sortBy = result;
_sortOrder = SortOrder.ascending;
}
_refreshList();
},
itemBuilder: (BuildContext context) => <PopupMenuEntry<AppSortBy>>[
const PopupMenuItem<AppSortBy>(
value: AppSortBy.appName,
child: Text('Sort by Name'),
),
const PopupMenuItem<AppSortBy>(
value: AppSortBy.size,
child: Text('Sort by Size'),
),
const PopupMenuItem<AppSortBy>(
value: AppSortBy.installTime,
child: Text('Sort by Install Time'),
),
],
),
],
bottom: PreferredSize(
preferredSize: const Size.fromHeight(60),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
hintText: 'Search apps...',
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
),
contentPadding: EdgeInsets.zero,
filled: true,
fillColor: Theme.of(context).colorScheme.surface,
),
onSubmitted: (_) => _refreshList(),
),
),
),
),
body: _apps.isEmpty && _isLoading
? const Center(child: CircularProgressIndicator())
: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
if (!_isLoading &&
scrollInfo.metrics.pixels >=
scrollInfo.metrics.maxScrollExtent - 500) {
_fetchNextPage();
return true;
}
return false;
},
child: ListView.builder(
itemCount: _apps.length + (_hasMore ? 1 : 0),
itemBuilder: (context, index) {
if (index == _apps.length) {
return const Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: CircularProgressIndicator(),
),
);
}
final app = _apps[index];
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.transparent,
backgroundImage: AppIconImageProvider(app.packageName),
),
title: Text(app.appName),
subtitle: Text(
'${app.packageName} • ${(app.baseApkSize / 1024 / 1024).toStringAsFixed(1)} MB',
),
trailing: PopupMenuButton<String>(
onSelected: (action) async {
try {
switch (action) {
case 'open':
await InstalledAppsManager.openApp(
app.packageName,
);
break;
case 'settings':
await InstalledAppsManager.openSettingsScreen(
app.packageName,
);
break;
case 'extract':
final path =
await InstalledAppsManager.extractApk(
app.packageName,
app.appName,
);
_showSuccess('Saved to $path');
break;
case 'save_icon':
final path =
await InstalledAppsManager.saveAppIcon(
app.packageName,
app.appName,
);
_showSuccess('Saved to $path');
break;
case 'uninstall':
await InstalledAppsManager.uninstallApp(
app.packageName,
);
break;
}
} on InstalledAppsManagerException catch (e) {
_showError(e.message);
} catch (e) {
_showError(e.toString());
}
},
itemBuilder: (context) => [
const PopupMenuItem(
value: 'open',
child: Text('Open App'),
),
const PopupMenuItem(
value: 'settings',
child: Text('App Settings'),
),
const PopupMenuItem(
value: 'extract',
child: Text('Extract APK'),
),
const PopupMenuItem(
value: 'save_icon',
child: Text('Save Icon'),
),
const PopupMenuItem(
value: 'uninstall',
child: Text('Uninstall'),
),
],
),
);
},
),
),
);
}
}