mirror of
https://github.com/ZhuJHua/moodiary.git
synced 2026-04-05 16:39:01 +08:00
feat(about): Add easter eggs
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
import 'package:confetti/confetti.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:moodiary/router/app_routes.dart';
|
||||
import 'package:moodiary/utils/package_util.dart';
|
||||
import 'package:refreshed/refreshed.dart';
|
||||
@@ -6,15 +9,36 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'about_state.dart';
|
||||
|
||||
class AboutLogic extends GetxController {
|
||||
class AboutLogic extends GetxController with GetSingleTickerProviderStateMixin {
|
||||
final AboutState state = AboutState();
|
||||
|
||||
late final AnimationController _animationController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
)..repeat(reverse: true);
|
||||
late final animation = Tween<double>(begin: -0.06, end: 0.06).animate(
|
||||
CurvedAnimation(
|
||||
parent: _animationController,
|
||||
curve: Curves.easeInOut,
|
||||
),
|
||||
);
|
||||
|
||||
late final ConfettiController confettiController =
|
||||
ConfettiController(duration: const Duration(seconds: 2));
|
||||
|
||||
@override
|
||||
void onReady() async {
|
||||
await getInfo();
|
||||
super.onReady();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
_animationController.dispose();
|
||||
confettiController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> getInfo() async {
|
||||
var packageInfo = await PackageUtil.getPackageInfo();
|
||||
var deviceInfo = await PackageUtil.getInfo();
|
||||
@@ -51,6 +75,11 @@ class AboutLogic extends GetxController {
|
||||
await launchUrl(uri, mode: LaunchMode.platformDefault);
|
||||
}
|
||||
|
||||
void playConfetti() {
|
||||
HapticFeedback.selectionClick();
|
||||
confettiController.play();
|
||||
}
|
||||
|
||||
void toPrivacy() {
|
||||
Get.toNamed(AppRoutes.privacyPage);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:confetti/confetti.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:moodiary/components/base/button.dart';
|
||||
@@ -34,10 +37,22 @@ class AboutPage extends StatelessWidget {
|
||||
spacing: 16.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
state.appName,
|
||||
style: textStyle.titleLarge
|
||||
?.copyWith(color: colorScheme.onSurface),
|
||||
GestureDetector(
|
||||
onTap: logic.playConfetti,
|
||||
child: AnimatedBuilder(
|
||||
animation: logic.animation,
|
||||
builder: (context, child) {
|
||||
return Transform.rotate(
|
||||
angle: logic.animation.value,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
state.appName,
|
||||
style: textStyle.titleLarge
|
||||
?.copyWith(color: colorScheme.onSurface),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -72,114 +87,134 @@ class AboutPage extends StatelessWidget {
|
||||
leading: const PageBackButton(),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Stack(
|
||||
children: [
|
||||
GetBuilder<AboutLogic>(builder: (_) {
|
||||
return buildLogoTitle();
|
||||
}),
|
||||
const SizedBox(height: 16.0),
|
||||
Card.outlined(
|
||||
color: colorScheme.surfaceContainerLow,
|
||||
child: Column(
|
||||
children: [
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(Icons.update_rounded),
|
||||
title: Text(l10n.aboutUpdate),
|
||||
isFirst: true,
|
||||
trailing: const Icon(Icons.chevron_right_rounded),
|
||||
onTap: () async {
|
||||
await UpdateUtil.checkShouldUpdate(state.appVersion,
|
||||
handle: true);
|
||||
},
|
||||
),
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(Icons.source_rounded),
|
||||
title: Text(l10n.aboutSource),
|
||||
trailing: const Icon(Icons.chevron_right_rounded),
|
||||
onTap: () async {
|
||||
await logic.toSource();
|
||||
},
|
||||
),
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(Icons.file_copy_rounded),
|
||||
title: Text(l10n.aboutUserAgreement),
|
||||
trailing: const Icon(Icons.chevron_right_rounded),
|
||||
onTap: () {
|
||||
logic.toAgreement();
|
||||
},
|
||||
),
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(Icons.privacy_tip_rounded),
|
||||
title: Text(l10n.aboutPrivacyPolicy),
|
||||
trailing: const Icon(Icons.chevron_right_rounded),
|
||||
onTap: () {
|
||||
logic.toPrivacy();
|
||||
},
|
||||
),
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(Icons.bug_report_rounded),
|
||||
title: Text(l10n.aboutBugReport),
|
||||
trailing: const Icon(Icons.chevron_right_rounded),
|
||||
onTap: () async {
|
||||
await logic.toReportPage();
|
||||
},
|
||||
),
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(Icons.attach_money_rounded),
|
||||
title: Text(l10n.aboutDonate),
|
||||
isLast: true,
|
||||
trailing: const Icon(Icons.chevron_right_rounded),
|
||||
onTap: logic.toSponsor,
|
||||
ListView(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
physics: const ClampingScrollPhysics(),
|
||||
children: [
|
||||
GetBuilder<AboutLogic>(builder: (_) {
|
||||
return buildLogoTitle();
|
||||
}),
|
||||
const SizedBox(height: 16.0),
|
||||
Card.outlined(
|
||||
color: colorScheme.surfaceContainerLow,
|
||||
child: Column(
|
||||
children: [
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(Icons.update_rounded),
|
||||
title: Text(l10n.aboutUpdate),
|
||||
isFirst: true,
|
||||
trailing: const Icon(Icons.chevron_right_rounded),
|
||||
onTap: () async {
|
||||
await UpdateUtil.checkShouldUpdate(state.appVersion,
|
||||
handle: true);
|
||||
},
|
||||
),
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(Icons.source_rounded),
|
||||
title: Text(l10n.aboutSource),
|
||||
trailing: const Icon(Icons.chevron_right_rounded),
|
||||
onTap: () async {
|
||||
await logic.toSource();
|
||||
},
|
||||
),
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(Icons.file_copy_rounded),
|
||||
title: Text(l10n.aboutUserAgreement),
|
||||
trailing: const Icon(Icons.chevron_right_rounded),
|
||||
onTap: () {
|
||||
logic.toAgreement();
|
||||
},
|
||||
),
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(Icons.privacy_tip_rounded),
|
||||
title: Text(l10n.aboutPrivacyPolicy),
|
||||
trailing: const Icon(Icons.chevron_right_rounded),
|
||||
onTap: () {
|
||||
logic.toPrivacy();
|
||||
},
|
||||
),
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(Icons.bug_report_rounded),
|
||||
title: Text(l10n.aboutBugReport),
|
||||
trailing: const Icon(Icons.chevron_right_rounded),
|
||||
onTap: () async {
|
||||
await logic.toReportPage();
|
||||
},
|
||||
),
|
||||
AdaptiveListTile(
|
||||
leading: const Icon(Icons.attach_money_rounded),
|
||||
title: Text(l10n.aboutDonate),
|
||||
isLast: true,
|
||||
trailing: const Icon(Icons.chevron_right_rounded),
|
||||
onTap: logic.toSponsor,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4.0,
|
||||
children: [
|
||||
const FaIcon(
|
||||
FontAwesomeIcons.flutter,
|
||||
size: 16,
|
||||
color: Colors.lightBlue,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
child: VerticalDivider(
|
||||
thickness: 2,
|
||||
),
|
||||
),
|
||||
FaIcon(
|
||||
FontAwesomeIcons.dartLang,
|
||||
size: 16,
|
||||
color: colorScheme.onSurface.withValues(alpha: 0.8),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
child: VerticalDivider(
|
||||
thickness: 2,
|
||||
),
|
||||
),
|
||||
FaIcon(
|
||||
FontAwesomeIcons.rust,
|
||||
size: 16,
|
||||
color: colorScheme.onSurface.withValues(alpha: 0.8),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
child: VerticalDivider(
|
||||
thickness: 2,
|
||||
),
|
||||
),
|
||||
const FaIcon(
|
||||
FontAwesomeIcons.solidHeart,
|
||||
size: 16,
|
||||
color: Colors.pinkAccent,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ConfettiWidget(
|
||||
confettiController: logic.confettiController,
|
||||
blastDirectionality: BlastDirectionality.directional,
|
||||
blastDirection: pi / 2,
|
||||
emissionFrequency: 0.1,
|
||||
colors: const [
|
||||
Colors.green,
|
||||
Colors.blue,
|
||||
Colors.yellow,
|
||||
Colors.red,
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4.0,
|
||||
children: [
|
||||
const FaIcon(
|
||||
FontAwesomeIcons.flutter,
|
||||
size: 16,
|
||||
color: Colors.lightBlue,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
child: VerticalDivider(
|
||||
thickness: 2,
|
||||
),
|
||||
),
|
||||
FaIcon(
|
||||
FontAwesomeIcons.dartLang,
|
||||
size: 16,
|
||||
color: colorScheme.onSurface.withValues(alpha: 0.8),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
child: VerticalDivider(
|
||||
thickness: 2,
|
||||
),
|
||||
),
|
||||
FaIcon(
|
||||
FontAwesomeIcons.rust,
|
||||
size: 16,
|
||||
color: colorScheme.onSurface.withValues(alpha: 0.8),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
child: VerticalDivider(
|
||||
thickness: 2,
|
||||
),
|
||||
),
|
||||
const FaIcon(
|
||||
FontAwesomeIcons.solidHeart,
|
||||
size: 16,
|
||||
color: Colors.pinkAccent,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -5,12 +5,6 @@ class SponsorLogic extends GetxController {
|
||||
late final ConfettiController confettiController =
|
||||
ConfettiController(duration: const Duration(seconds: 2));
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
confettiController.play();
|
||||
super.onReady();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
confettiController.dispose();
|
||||
|
||||
Reference in New Issue
Block a user