800 lines
26 KiB
Dart
800 lines
26 KiB
Dart
/// ============================================================
|
||
/// 闲言APP — 软件信息页面(分区组件)
|
||
/// 创建时间: 2026-05-29
|
||
/// 更新时间: 2026-06-01
|
||
/// 作用: 技术栈、构建信息、设备信息、平台兼容、更新日志、备案信息等分区
|
||
/// 上次更新: IcpSection标题改为"APP ICP核准备案号"; 非中文时标题右侧增加info icon弹窗说明
|
||
/// ============================================================
|
||
|
||
import 'dart:ui';
|
||
|
||
import 'package:flutter/cupertino.dart';
|
||
import 'package:flutter/foundation.dart'
|
||
show defaultTargetPlatform, TargetPlatform, kIsWeb;
|
||
import 'package:flutter/services.dart';
|
||
import 'dart:io' show Platform;
|
||
|
||
import '../../../../core/theme/app_theme.dart';
|
||
import '../../../../core/theme/app_spacing.dart';
|
||
import '../../../../core/theme/app_typography.dart';
|
||
import '../../../../core/theme/app_radius.dart';
|
||
import '../../../../core/utils/platform/platform_utils.dart' as pu;
|
||
import '../../../../core/constants/app_constants.dart';
|
||
import '../../../../core/utils/platform/platform_utils.dart' show isOhos, isAndroid, isWindows, isIOS, isMacOS, isLinux, isWeb, isMobile, isDesktop, platformName;
|
||
import '../../../../shared/widgets/containers/glass_container.dart';
|
||
import '../../../../l10n/translations.dart';
|
||
import 'about_shared_widgets.dart';
|
||
import 'app_info_widgets.dart';
|
||
import 'learn_us_widgets.dart';
|
||
|
||
// ============================================================
|
||
// 技术栈卡片
|
||
// ============================================================
|
||
|
||
class TechStackSection extends StatelessWidget {
|
||
const TechStackSection({super.key, required this.ext, required this.t});
|
||
|
||
final AppThemeExtension ext;
|
||
final T t;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final items = [
|
||
TechItemData(
|
||
'Dart',
|
||
t.about.techLanguage,
|
||
CupertinoIcons.flame,
|
||
ext.iconTintYellow,
|
||
),
|
||
TechItemData(
|
||
'Riverpod',
|
||
t.about.techState,
|
||
CupertinoIcons.arrow_3_trianglepath,
|
||
ext.iconTintCyan,
|
||
),
|
||
TechItemData(
|
||
'GoRouter',
|
||
t.about.techRouter,
|
||
CupertinoIcons.map,
|
||
ext.iconTintPurple,
|
||
),
|
||
TechItemData(
|
||
'Dio',
|
||
t.about.techNetwork,
|
||
CupertinoIcons.wifi,
|
||
ext.iconTintBlue,
|
||
),
|
||
];
|
||
|
||
return GlassContainer(
|
||
depth: GlassDepth.elevated,
|
||
padding: EdgeInsets.zero,
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
AboutSectionTitle(
|
||
icon: CupertinoIcons.hammer,
|
||
title: t.about.techStack,
|
||
ext: ext,
|
||
),
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(
|
||
AppSpacing.md,
|
||
0,
|
||
AppSpacing.md,
|
||
AppSpacing.md,
|
||
),
|
||
child: LayoutBuilder(
|
||
builder: (context, constraints) {
|
||
final cardWidth = (constraints.maxWidth - AppSpacing.md) / 2;
|
||
return Wrap(
|
||
spacing: AppSpacing.md,
|
||
runSpacing: AppSpacing.md,
|
||
children: items
|
||
.map(
|
||
(item) => SizedBox(
|
||
width: cardWidth,
|
||
child: TechCard(item: item, ext: ext),
|
||
),
|
||
)
|
||
.toList(),
|
||
);
|
||
},
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 构建信息卡片
|
||
// ============================================================
|
||
|
||
class BuildInfoSection extends StatelessWidget {
|
||
const BuildInfoSection({super.key, required this.ext, required this.t});
|
||
|
||
final AppThemeExtension ext;
|
||
final T t;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return GlassContainer(
|
||
depth: GlassDepth.elevated,
|
||
padding: EdgeInsets.zero,
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
AboutSectionTitle(
|
||
icon: CupertinoIcons.wrench,
|
||
title: t.about.buildInfo,
|
||
ext: ext,
|
||
),
|
||
CopyableItem(
|
||
icon: CupertinoIcons.star,
|
||
title: t.about.version,
|
||
value: AppVersion.version,
|
||
ext: ext,
|
||
),
|
||
AboutDivider(ext: ext),
|
||
CopyableItem(
|
||
icon: CupertinoIcons.number,
|
||
title: t.about.buildNumber,
|
||
value: AppVersion.buildNumber.toString(),
|
||
ext: ext,
|
||
),
|
||
AboutDivider(ext: ext),
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(
|
||
horizontal: AppSpacing.md,
|
||
vertical: AppSpacing.md,
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Expanded(
|
||
child: Row(
|
||
children: [
|
||
Icon(
|
||
CupertinoIcons.calendar,
|
||
size: 18,
|
||
color: ext.textHint,
|
||
),
|
||
const SizedBox(width: AppSpacing.md),
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
t.about.buildTime,
|
||
style: AppTypography.body.copyWith(
|
||
fontWeight: FontWeight.w500,
|
||
color: ext.textPrimary,
|
||
),
|
||
),
|
||
const SizedBox(height: 2),
|
||
Text(
|
||
DateTime.now().toString().split(' ').first,
|
||
style: AppTypography.caption1.copyWith(
|
||
color: ext.textHint,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
const SizedBox(width: AppSpacing.md),
|
||
Expanded(
|
||
child: Row(
|
||
children: [
|
||
Icon(
|
||
CupertinoIcons.cube_box,
|
||
size: 18,
|
||
color: ext.textHint,
|
||
),
|
||
const SizedBox(width: AppSpacing.md),
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
'Build SDK',
|
||
style: AppTypography.body.copyWith(
|
||
fontWeight: FontWeight.w500,
|
||
color: ext.textPrimary,
|
||
),
|
||
),
|
||
const SizedBox(height: 2),
|
||
Text(
|
||
_getBuildSdk(),
|
||
style: AppTypography.caption1.copyWith(
|
||
color: ext.textHint,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
AboutDivider(ext: ext),
|
||
LicenseItem(ext: ext, t: t),
|
||
AboutDivider(ext: ext),
|
||
CheckUpdateItem(ext: ext, t: t),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
String _getBuildSdk() {
|
||
try {
|
||
if (isOhos) return 'Deveco API 23';
|
||
if (isAndroid) return 'Android Target 36';
|
||
if (isWindows) return 'Win10 SDK';
|
||
if (isIOS) return 'iOS 26';
|
||
if (isMacOS) return 'macOS 18';
|
||
if (isLinux) return 'Linux 20';
|
||
if (isWeb) return 'Web SDK';
|
||
return 'Unknown';
|
||
} catch (_) {
|
||
return 'Unknown';
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 设备信息卡片
|
||
// ============================================================
|
||
|
||
class DeviceInfoSection extends StatelessWidget {
|
||
const DeviceInfoSection({super.key, required this.ext, required this.t});
|
||
|
||
final AppThemeExtension ext;
|
||
final T t;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final currentPlatform = platformName;
|
||
String deviceType = t.about.deviceUnknown;
|
||
if (isWeb) {
|
||
deviceType = 'Web';
|
||
} else if (isMobile) {
|
||
deviceType = t.about.deviceMobile;
|
||
} else if (isDesktop) {
|
||
deviceType = t.about.deviceDesktop;
|
||
}
|
||
|
||
final size = MediaQuery.of(context).size;
|
||
final pixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||
|
||
return GlassContainer(
|
||
depth: GlassDepth.elevated,
|
||
padding: EdgeInsets.zero,
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
AboutSectionTitle(
|
||
icon: CupertinoIcons.device_phone_portrait,
|
||
title: t.about.deviceInfo,
|
||
ext: ext,
|
||
),
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(
|
||
AppSpacing.md,
|
||
0,
|
||
AppSpacing.md,
|
||
AppSpacing.md,
|
||
),
|
||
child: GridView.count(
|
||
shrinkWrap: true,
|
||
physics: const NeverScrollableScrollPhysics(),
|
||
crossAxisCount: 2,
|
||
mainAxisSpacing: AppSpacing.md,
|
||
crossAxisSpacing: AppSpacing.md,
|
||
childAspectRatio: 2.0,
|
||
children: [
|
||
GridInfoItem(
|
||
title: t.about.os,
|
||
value: currentPlatform,
|
||
icon: CupertinoIcons.device_desktop,
|
||
ext: ext,
|
||
),
|
||
GridInfoItem(
|
||
title: t.about.deviceType,
|
||
value: deviceType,
|
||
icon: CupertinoIcons.device_phone_portrait,
|
||
ext: ext,
|
||
),
|
||
GridInfoItem(
|
||
title: 'Dart',
|
||
value: _getDartVersion(),
|
||
icon: CupertinoIcons.bolt,
|
||
ext: ext,
|
||
),
|
||
GridInfoItem(
|
||
title: t.about.renderEngine,
|
||
value: _getRenderingEngine(),
|
||
icon: CupertinoIcons.paintbrush,
|
||
ext: ext,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||
child: Container(
|
||
width: double.infinity,
|
||
padding: const EdgeInsets.all(AppSpacing.md),
|
||
decoration: BoxDecoration(
|
||
color: ext.textHint.withValues(alpha: 0.06),
|
||
borderRadius: AppRadius.smBorder,
|
||
border: Border.all(color: ext.textHint.withValues(alpha: 0.1)),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Row(
|
||
children: [
|
||
Icon(
|
||
CupertinoIcons.info_circle,
|
||
size: 14,
|
||
color: ext.textHint,
|
||
),
|
||
const SizedBox(width: AppSpacing.xs),
|
||
Text(
|
||
t.about.screenDetail,
|
||
style: AppTypography.caption1.copyWith(
|
||
fontWeight: FontWeight.w500,
|
||
color: ext.textSecondary,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: AppSpacing.xs),
|
||
Text(
|
||
'${t.about.screenSize}: ${size.width.toStringAsFixed(0)} × ${size.height.toStringAsFixed(0)}',
|
||
style: AppTypography.caption1.copyWith(color: ext.textHint),
|
||
),
|
||
const SizedBox(height: AppSpacing.xs),
|
||
Text(
|
||
'${t.about.pixelRatio}: ${pixelRatio.toStringAsFixed(2)}',
|
||
style: AppTypography.caption1.copyWith(color: ext.textHint),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(height: AppSpacing.md),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
String _getDartVersion() {
|
||
try {
|
||
final fullVersion = Platform.version;
|
||
final match = RegExp(r'^(\d+\.\d+\.\d+)').firstMatch(fullVersion);
|
||
if (match != null) return '${match.group(1)} (VM)';
|
||
return fullVersion;
|
||
} catch (_) {
|
||
return 'Unknown';
|
||
}
|
||
}
|
||
|
||
String _getRenderingEngine() {
|
||
try {
|
||
final dynamic dispatcher = PlatformDispatcher.instance;
|
||
final bool? enabled = dispatcher.impellerEnabled as bool?;
|
||
if (enabled == true) return 'Impeller';
|
||
if (enabled == false) return 'Skia';
|
||
} catch (_) {}
|
||
|
||
try {
|
||
final dynamic view = PlatformDispatcher.instance.views.first;
|
||
final dynamic engine = view.renderingEngine;
|
||
if (engine != null) {
|
||
final String name = engine.name.toString();
|
||
return switch (name) {
|
||
'skia' => 'Skia',
|
||
'impeller' => 'Impeller',
|
||
'canvasKit' => 'CanvasKit',
|
||
'html' => 'HTML',
|
||
_ => name,
|
||
};
|
||
}
|
||
} catch (_) {}
|
||
|
||
if (kIsWeb) return 'CanvasKit/HTML';
|
||
|
||
if (defaultTargetPlatform == TargetPlatform.iOS ||
|
||
defaultTargetPlatform == TargetPlatform.android ||
|
||
defaultTargetPlatform == TargetPlatform.macOS) {
|
||
return 'Impeller';
|
||
}
|
||
|
||
if (defaultTargetPlatform == TargetPlatform.windows ||
|
||
defaultTargetPlatform == TargetPlatform.linux ||
|
||
defaultTargetPlatform == TargetPlatform.fuchsia) {
|
||
return 'Skia';
|
||
}
|
||
|
||
if (pu.isOhos) return 'Impeller';
|
||
|
||
return 'Skia';
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 平台兼容卡片
|
||
// ============================================================
|
||
|
||
class PlatformSection extends StatelessWidget {
|
||
const PlatformSection({super.key, required this.ext, required this.t});
|
||
|
||
final AppThemeExtension ext;
|
||
final T t;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final platforms = [
|
||
PlatformItemData('📱 iOS', true, isCurrent: isIOS),
|
||
PlatformItemData('🤖 Android', true, isCurrent: isAndroid),
|
||
PlatformItemData('🔷 HarmonyOS', true, isCurrent: isOhos),
|
||
PlatformItemData('💻 macOS', true, isCurrent: isMacOS),
|
||
PlatformItemData('🪟 Windows', true, isCurrent: isWindows),
|
||
PlatformItemData('🌐 Web', false, isCurrent: isWeb),
|
||
];
|
||
|
||
return GlassContainer(
|
||
depth: GlassDepth.elevated,
|
||
padding: EdgeInsets.zero,
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
AboutSectionTitle(
|
||
icon: CupertinoIcons.desktopcomputer,
|
||
title: t.about.platformCompat,
|
||
ext: ext,
|
||
),
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(
|
||
AppSpacing.md,
|
||
0,
|
||
AppSpacing.md,
|
||
AppSpacing.md,
|
||
),
|
||
child: Wrap(
|
||
spacing: AppSpacing.sm,
|
||
runSpacing: AppSpacing.sm,
|
||
children: platforms.map((p) {
|
||
final isCurrent = p.isCurrent;
|
||
return Container(
|
||
padding: const EdgeInsets.symmetric(
|
||
horizontal: AppSpacing.sm,
|
||
vertical: AppSpacing.xs,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: isCurrent
|
||
? ext.accent.withValues(alpha: 0.15)
|
||
: p.supported
|
||
? ext.successColor.withValues(alpha: 0.08)
|
||
: ext.textHint.withValues(alpha: 0.06),
|
||
borderRadius: AppRadius.pillBorder,
|
||
border: Border.all(
|
||
color: isCurrent
|
||
? ext.accent.withValues(alpha: 0.5)
|
||
: p.supported
|
||
? ext.successColor.withValues(alpha: 0.3)
|
||
: ext.textHint.withValues(alpha: 0.15),
|
||
),
|
||
),
|
||
child: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
if (isCurrent) ...[
|
||
Icon(
|
||
CupertinoIcons.checkmark_circle_fill,
|
||
size: 12,
|
||
color: ext.accent,
|
||
),
|
||
const SizedBox(width: AppSpacing.xs),
|
||
],
|
||
Text(
|
||
p.name,
|
||
style: AppTypography.caption1.copyWith(
|
||
fontWeight: isCurrent
|
||
? FontWeight.w700
|
||
: FontWeight.w600,
|
||
color: isCurrent
|
||
? ext.accent
|
||
: p.supported
|
||
? ext.successColor
|
||
: ext.textHint,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}).toList(),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 更新日志卡片
|
||
// ============================================================
|
||
|
||
class UpdateLogSection extends StatelessWidget {
|
||
const UpdateLogSection({super.key, required this.ext, required this.t});
|
||
|
||
final AppThemeExtension ext;
|
||
final T t;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return GlassContainer(
|
||
depth: GlassDepth.elevated,
|
||
padding: EdgeInsets.zero,
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
AboutSectionTitle(
|
||
icon: CupertinoIcons.doc_text,
|
||
title: t.about.updateLog,
|
||
ext: ext,
|
||
),
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||
child: UpdateItem(
|
||
version: '${t.about.version} ${AppVersion.version}',
|
||
date: '2026-05-29',
|
||
changes: [
|
||
t.about.updateLog1,
|
||
t.about.updateLog2,
|
||
t.about.updateLog3,
|
||
],
|
||
ext: ext,
|
||
),
|
||
),
|
||
const SizedBox(height: AppSpacing.md),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// ICP备案信息区域(从了解我们页面迁移)
|
||
// ============================================================
|
||
|
||
class IcpSection extends StatelessWidget {
|
||
const IcpSection({super.key, required this.ext, required this.t});
|
||
|
||
final AppThemeExtension ext;
|
||
final T t;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
const icpNumber = '滇ICP备2022000863号-18A';
|
||
const icpUrl = 'https://beian.miit.gov.cn/#/Integrated/index';
|
||
final isChinese = Localizations.localeOf(context).languageCode == 'zh';
|
||
|
||
return GlassContainer(
|
||
depth: GlassDepth.elevated,
|
||
padding: EdgeInsets.zero,
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
AboutSectionTitle(
|
||
icon: CupertinoIcons.doc_text,
|
||
title: t.about.icpInfo,
|
||
ext: ext,
|
||
trailing: isChinese
|
||
? null
|
||
: _IcpInfoIcon(ext: ext, t: t),
|
||
),
|
||
GestureDetector(
|
||
onTap: () {
|
||
Clipboard.setData(const ClipboardData(text: icpNumber));
|
||
showCupertinoDialog<void>(
|
||
context: context,
|
||
builder: (ctx) => CupertinoAlertDialog(
|
||
content: Text(t.about.copied),
|
||
actions: [
|
||
CupertinoDialogAction(
|
||
isDefaultAction: true,
|
||
onPressed: () => Navigator.pop(ctx),
|
||
child: const Text('OK'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
},
|
||
behavior: HitTestBehavior.opaque,
|
||
child: Padding(
|
||
padding: const EdgeInsets.symmetric(
|
||
horizontal: AppSpacing.md,
|
||
vertical: AppSpacing.md,
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
icpNumber,
|
||
style: AppTypography.body.copyWith(
|
||
color: ext.textSecondary,
|
||
decoration: TextDecoration.underline,
|
||
decorationColor: ext.textHint,
|
||
),
|
||
),
|
||
const SizedBox(height: 2),
|
||
Text(
|
||
t.about.icpDesc,
|
||
style: AppTypography.caption2.copyWith(
|
||
color: ext.textHint,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
Icon(
|
||
CupertinoIcons.doc_on_clipboard,
|
||
size: 16,
|
||
color: ext.textHint,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
AboutDivider(ext: ext, leftIndent: 0),
|
||
GestureDetector(
|
||
onTap: () => _showIcpLaunchDialog(context, icpUrl),
|
||
behavior: HitTestBehavior.opaque,
|
||
child: Padding(
|
||
padding: const EdgeInsets.symmetric(
|
||
horizontal: AppSpacing.md,
|
||
vertical: AppSpacing.md,
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Container(
|
||
width: 32,
|
||
height: 32,
|
||
decoration: BoxDecoration(
|
||
color: ext.accent.withValues(alpha: 0.1),
|
||
borderRadius: AppRadius.smBorder,
|
||
),
|
||
child: Icon(
|
||
CupertinoIcons.globe,
|
||
size: 18,
|
||
color: ext.accent,
|
||
),
|
||
),
|
||
const SizedBox(width: AppSpacing.md),
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
t.about.viewIcpDetail,
|
||
style: AppTypography.body.copyWith(
|
||
fontWeight: FontWeight.w500,
|
||
color: ext.textPrimary,
|
||
),
|
||
),
|
||
const SizedBox(height: 2),
|
||
Text(
|
||
'beian.miit.gov.cn',
|
||
style: AppTypography.caption2.copyWith(
|
||
color: ext.accent,
|
||
),
|
||
maxLines: 1,
|
||
overflow: TextOverflow.ellipsis,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
Icon(
|
||
CupertinoIcons.arrow_up_right_square,
|
||
size: 16,
|
||
color: ext.textHint,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
void _showIcpLaunchDialog(BuildContext context, String url) {
|
||
showCupertinoDialog<void>(
|
||
context: context,
|
||
builder: (ctx) => CupertinoAlertDialog(
|
||
title: Text(t.about.viewIcpDetail),
|
||
content: Text(t.about.icpLaunchConfirm),
|
||
actions: [
|
||
CupertinoDialogAction(
|
||
isDestructiveAction: true,
|
||
onPressed: () => Navigator.of(ctx).pop(),
|
||
child: Text(t.common.cancel),
|
||
),
|
||
CupertinoDialogAction(
|
||
isDefaultAction: true,
|
||
onPressed: () {
|
||
Navigator.of(ctx).pop();
|
||
launchAboutUrl(context, url);
|
||
},
|
||
child: Text(t.common.confirm),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
class _IcpInfoIcon extends StatelessWidget {
|
||
const _IcpInfoIcon({required this.ext, required this.t});
|
||
|
||
final AppThemeExtension ext;
|
||
final T t;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return GestureDetector(
|
||
onTap: () => _showIcpInfoHintDialog(context),
|
||
child: Icon(
|
||
CupertinoIcons.info_circle,
|
||
size: 18,
|
||
color: ext.textHint,
|
||
),
|
||
);
|
||
}
|
||
|
||
void _showIcpInfoHintDialog(BuildContext context) {
|
||
showCupertinoDialog<void>(
|
||
context: context,
|
||
builder: (ctx) => CupertinoAlertDialog(
|
||
title: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Icon(CupertinoIcons.info_circle, size: 18, color: ext.accent),
|
||
const SizedBox(width: AppSpacing.sm),
|
||
Flexible(child: Text(t.about.icpInfo)),
|
||
],
|
||
),
|
||
content: Padding(
|
||
padding: const EdgeInsets.only(top: AppSpacing.md),
|
||
child: Text(
|
||
t.about.icpInfoHint,
|
||
style: AppTypography.caption1.copyWith(
|
||
color: ext.textSecondary,
|
||
height: 1.5,
|
||
),
|
||
),
|
||
),
|
||
actions: [
|
||
CupertinoDialogAction(
|
||
isDefaultAction: true,
|
||
onPressed: () => Navigator.of(ctx).pop(),
|
||
child: Text(t.about.okButton),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|