bring_app_to_foreground 0.1.1
bring_app_to_foreground: ^0.1.1 copied to clipboard
Bring a Flutter app to the foreground from the background on Android (incoming VoIP/dispatch calls, alarms) via the Display-over-other-apps permission.
import 'dart:async';
import 'package:bring_app_to_foreground/bring_app_to_foreground.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const ExampleApp());
}
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'bring_app_to_foreground',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
bool _hasPermission = false;
int _countdown = 0;
Timer? _timer;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_refreshPermission();
}
@override
void dispose() {
_timer?.cancel();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
// Re-check the permission when returning from the system settings page
// (or from being backgrounded), since the user may have just granted it.
if (state == AppLifecycleState.resumed) {
_refreshPermission();
}
}
Future<void> _refreshPermission() async {
final bool granted = await BringAppToForeground.hasPermission();
if (!mounted) return;
setState(() => _hasPermission = granted);
}
Future<void> _requestPermission() async {
await BringAppToForeground.requestPermission();
// The real status arrives on resume via didChangeAppLifecycleState; refresh
// now as well in case the page returned synchronously.
await _refreshPermission();
}
void _startCountdown() {
_timer?.cancel();
setState(() => _countdown = 5);
_timer = Timer.periodic(const Duration(seconds: 1), (Timer timer) async {
if (!mounted) return;
if (_countdown <= 1) {
timer.cancel();
setState(() => _countdown = 0);
await BringAppToForeground.bringToFront();
} else {
setState(() => _countdown -= 1);
}
});
}
@override
Widget build(BuildContext context) {
final bool counting = _countdown > 0;
return Scaffold(
appBar: AppBar(title: const Text('bring_app_to_foreground')),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
_hasPermission ? Icons.check_circle : Icons.cancel,
color: _hasPermission ? Colors.green : Colors.red,
),
const SizedBox(width: 8),
Text(
_hasPermission
? '"Display over other apps" granted'
: 'Permission not granted',
style: Theme.of(context).textTheme.titleMedium,
),
],
),
const SizedBox(height: 32),
FilledButton.tonal(
onPressed: _refreshPermission,
child: const Text('Check permission'),
),
const SizedBox(height: 12),
FilledButton(
onPressed: _hasPermission ? null : _requestPermission,
child: const Text('Grant permission'),
),
const SizedBox(height: 12),
FilledButton(
onPressed: counting ? null : _startCountdown,
child: Text(
counting
? 'Press Home now... $_countdown'
: 'Bring to front in 5s',
),
),
const SizedBox(height: 24),
Text(
'Tap "Bring to front in 5s", press the Home button, and the app '
'should pop back to the foreground after the countdown.',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
);
}
}