appcare_flutter 1.0.0
appcare_flutter: ^1.0.0 copied to clipboard
An all-in-one Flutter utility package for app maintenance and user engagement using RAW CODE ONLY.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:appcare_flutter/appcare_flutter.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _appCare = AppCare();
bool _isConnected = true;
AppBaseInfo? _appInfo;
DeviceInfo? _deviceInfo;
GeoLocation? _location;
@override
void initState() {
super.initState();
_initConnectivityListener();
_getData();
}
void _initConnectivityListener() {
_appCare.checkConnectivity().then((value) {
if (mounted) setState(() => _isConnected = value);
});
_appCare.onConnectivityChanged.listen((status) {
if (mounted) setState(() => _isConnected = status);
// debugPrint("Connectivity changed: $status");
});
}
Future<void> _getData() async {
final appInfo = await _appCare.getAppBaseInfo();
final deviceInfo = await _appCare.getDeviceInfo();
if (mounted) {
setState(() {
_appInfo = appInfo;
_deviceInfo = deviceInfo;
});
}
}
Future<void> _getLocation() async {
final loc = await _appCare.getCurrentLocation();
if (mounted) {
setState(() {
_location = loc;
});
if (loc == null) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text("Location null/denied")));
}
}
}
Future<void> _requestReview() async {
// Force prompt by setting minDays to 0 for demo
final result = await _appCare.requestReview(minDaysBeforePrompt: 0);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
result
? 'Review flow launched'
: 'Review flow not launched (cooldown or unsupported)',
),
),
);
}
}
Future<void> _checkUpdate() async {
// Using a sample JSON struct for demo.
// Since we can't easily host a file, we expect this to likely fail or we can mock the `checkForUpdate` logic via a local test,
// but here we just call the real API.
// IF the URL is unreachable, it will return updateAvailable: false.
// For demo purpose, we CANNOT guarantee a working URL without setting one up.
// So we will just show the method call.
final info = await _appCare.checkForUpdate();
if (mounted) {
if (info.updateAvailable) {
showDialog(
context: context,
builder: (c) => AlertDialog(
title: const Text("Update Available"),
content: Text(
"New version: ${info.remoteVersion}\nStore URL: ${info.storeUrl}",
),
actions: [
TextButton(
onPressed: () => Navigator.pop(c),
child: const Text("Later"),
),
TextButton(
onPressed: () {
Navigator.pop(c);
_appCare.startUpdate(); // Auto handles flow
},
child: const Text("Update Now"),
),
],
),
);
} else {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text("No update available")));
}
}
}
Future<void> _openStore() async {
final result = await _appCare.openStore();
if (mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Open Store Status: $result')));
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('AppCare Flutter Demo')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
padding: const EdgeInsets.all(12),
color: _isConnected ? Colors.green[100] : Colors.red[100],
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
_isConnected ? Icons.wifi : Icons.wifi_off,
color: _isConnected ? Colors.green : Colors.red,
),
const SizedBox(width: 8),
Text(
'Internet: ${_isConnected ? "Online" : "Offline"}',
style: const TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
const SizedBox(height: 20),
_buildInfoCard("App Info", [
"Name: ${_appInfo?.appName ?? '...'}",
"Package: ${_appInfo?.packageName ?? '...'}",
"Version: ${_appInfo?.version ?? '...'}",
"Build: ${_appInfo?.buildNumber ?? '...'}",
"Platform: ${_appInfo?.platform ?? '...'}",
]),
const SizedBox(height: 10),
_buildInfoCard("Device Info", [
"OS: ${_deviceInfo?.osName} ${_deviceInfo?.osVersion}",
"Model: ${_deviceInfo?.model}",
"Manufacturer: ${_deviceInfo?.manufacturer}",
]),
const SizedBox(height: 10),
_buildInfoCard(
"Location",
[
_location != null ? "Lat: ${_location!.latitude}" : "Lat: -",
_location != null ? "Lng: ${_location!.longitude}" : "Lng: -",
],
trailing: IconButton(
icon: const Icon(Icons.my_location),
onPressed: _getLocation,
),
),
const SizedBox(height: 20),
const Divider(),
ElevatedButton(
onPressed: _requestReview,
child: const Text('Request Review (Force Mock)'),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: _checkUpdate,
child: const Text('Check for Update'),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: _openStore,
child: const Text('Open Store Page'),
),
],
),
),
),
);
}
Widget _buildInfoCard(String title, List<String> lines, {Widget? trailing}) {
return Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
if (trailing != null) trailing,
],
),
const Divider(),
...lines.map(
(l) => Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Align(alignment: Alignment.centerLeft, child: Text(l)),
),
),
],
),
),
);
}
}