marco.pms.mobileapp/lib/view/mandatory_update_screen.dart

292 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_in_store_app_version_checker/flutter_in_store_app_version_checker.dart';
import 'package:on_field_work/helpers/services/app_logger.dart';
import 'package:on_field_work/helpers/utils/mixins/ui_mixin.dart';
import 'package:on_field_work/images.dart';
import 'package:on_field_work/helpers/widgets/wave_background.dart';
class MandatoryUpdateScreen extends StatefulWidget {
final String newVersion;
final InStoreAppVersionCheckerResult? updateResult;
const MandatoryUpdateScreen({
super.key,
required this.newVersion,
this.updateResult,
});
@override
State<MandatoryUpdateScreen> createState() => _MandatoryUpdateScreenState();
}
class _MandatoryUpdateScreenState extends State<MandatoryUpdateScreen>
with SingleTickerProviderStateMixin, UIMixin {
late AnimationController _controller;
late Animation<double> _logoAnimation;
static const double _kMaxContentWidth = 480.0;
Color get _primaryColor => contentTheme.primary;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 800),
);
_logoAnimation = CurvedAnimation(
parent: _controller,
curve: Curves.elasticOut,
);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
Future<void> _launchStoreUrl() async {
final url = widget.updateResult?.appURL;
if (url != null && url.isNotEmpty) {
final uri = Uri.parse(url);
try {
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
} else {
logSafe("Could not launch store URL: $url");
}
} catch (e, stack) {
logSafe(
"Error launching store URL: $url",
error: e,
stackTrace: stack,
level: LogLevel.error,
);
}
}
}
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.ltr,
child: Scaffold(
body: Stack(
children: [
RedWaveBackground(brandRed: _primaryColor),
SafeArea(
child: Center(
child: ConstrainedBox(
constraints:
const BoxConstraints(maxWidth: _kMaxContentWidth),
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 32),
ScaleTransition(
scale: _logoAnimation,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 8),
),
],
),
padding: const EdgeInsets.all(20),
child: Image.asset(Images.logoDark),
),
),
const SizedBox(height: 32),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
padding: const EdgeInsets.all(32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Title
Text(
"Update Required",
textAlign: TextAlign.center,
style: Theme.of(context)
.textTheme
.headlineMedium
?.copyWith(
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
const SizedBox(height: 12),
// Subtitle/Message
Text(
"A mandatory update (version ${widget.newVersion}) is available to continue using the application. Please update now for uninterrupted access.",
textAlign: TextAlign.center,
style: Theme.of(context)
.textTheme
.bodyLarge
?.copyWith(
color: Colors.black54,
height: 1.4,
),
),
const SizedBox(height: 32),
// Prominent Action Button
ElevatedButton.icon(
onPressed: _launchStoreUrl,
icon: const Icon(
Icons.system_update_alt,
color: Colors.white,
),
label: Text(
"UPDATE NOW",
style: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(
fontWeight: FontWeight.w700,
color: Colors.white,
fontSize: 16,
),
),
style: ElevatedButton.styleFrom(
padding:
const EdgeInsets.symmetric(vertical: 18),
backgroundColor: _primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
elevation: 5,
),
),
const SizedBox(height: 32),
// Why Update Section
Text(
"Why updating is important:",
textAlign: TextAlign.start,
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
const SizedBox(height: 12),
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
BulletPoint(
text:
"Access new features and improvements"),
BulletPoint(
text:
"Fix critical bugs and security issues"),
BulletPoint(
text:
"Ensure smooth app performance and stability"),
BulletPoint(
text:
"Stay compatible with latest operating system and services"),
],
),
const SizedBox(height: 12),
Text(
"Thank you for keeping your app up to date!",
textAlign: TextAlign.center,
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(
color: Colors.black45,
),
),
],
),
),
],
),
),
),
),
),
],
),
),
);
}
}
class BulletPoint extends StatelessWidget {
final String text;
final Color bulletColor;
const BulletPoint({
super.key,
required this.text,
this.bulletColor = const Color(0xFF555555),
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(right: 8, top: 4),
child: Icon(
Icons.circle,
size: 6,
color: bulletColor,
),
),
Expanded(
child: Text(
text,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.black87,
height: 1.4,
),
),
),
],
),
);
}
}