feat(divination): 重构手动起卦教程,支持三硬币交互选择
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 336 KiB |
|
Before Width: | Height: | Size: 409 KiB |
|
Before Width: | Height: | Size: 331 KiB |
|
Before Width: | Height: | Size: 344 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 799 KiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 3.0 MiB |
@@ -77,7 +77,6 @@ class DivinationApi {
|
||||
final aggregate = DivinationRunAggregate(
|
||||
derived: derived,
|
||||
signLevel: _asString(agentOutputRaw['sign_level']),
|
||||
summary: _asString(agentOutputRaw['summary']),
|
||||
conclusion: _asStringList(agentOutputRaw['conclusion']),
|
||||
focusPoints: _asStringList(agentOutputRaw['focus_points']),
|
||||
advice: _asStringList(agentOutputRaw['advice']),
|
||||
|
||||
@@ -54,7 +54,6 @@ class DivinationRunAggregate {
|
||||
const DivinationRunAggregate({
|
||||
required this.derived,
|
||||
required this.signLevel,
|
||||
required this.summary,
|
||||
required this.conclusion,
|
||||
required this.focusPoints,
|
||||
required this.advice,
|
||||
@@ -64,7 +63,6 @@ class DivinationRunAggregate {
|
||||
|
||||
final DerivedDivinationData derived;
|
||||
final String signLevel;
|
||||
final String summary;
|
||||
final List<String> conclusion;
|
||||
final List<String> focusPoints;
|
||||
final List<String> advice;
|
||||
@@ -82,8 +80,9 @@ class DivinationRunAggregate {
|
||||
lowerName: derived.lowerName,
|
||||
signType: signLevel,
|
||||
keywords: keywords.join('、'),
|
||||
focusPoints: focusPoints,
|
||||
conclusion: _asBullet(conclusion),
|
||||
analysis: summary.isEmpty ? answer : '$summary\n\n$answer',
|
||||
analysis: answer,
|
||||
suggestion: _asBullet(advice),
|
||||
ganzhi: GanzhiData(
|
||||
yearGanZhi: derived.ganzhi.yearGanZhi,
|
||||
|
||||
@@ -22,6 +22,7 @@ class DivinationParams {
|
||||
required this.divinationTime,
|
||||
required this.coinBalance,
|
||||
required this.userId,
|
||||
this.allowVibration = true,
|
||||
});
|
||||
|
||||
final DivinationMethod method;
|
||||
@@ -30,6 +31,7 @@ class DivinationParams {
|
||||
final DateTime divinationTime;
|
||||
final int coinBalance;
|
||||
final String userId;
|
||||
final bool allowVibration;
|
||||
|
||||
DivinationParams copyWith({
|
||||
DivinationMethod? method,
|
||||
@@ -38,6 +40,7 @@ class DivinationParams {
|
||||
DateTime? divinationTime,
|
||||
int? coinBalance,
|
||||
String? userId,
|
||||
bool? allowVibration,
|
||||
}) {
|
||||
return DivinationParams(
|
||||
method: method ?? this.method,
|
||||
@@ -46,6 +49,7 @@ class DivinationParams {
|
||||
divinationTime: divinationTime ?? this.divinationTime,
|
||||
coinBalance: coinBalance ?? this.coinBalance,
|
||||
userId: userId ?? this.userId,
|
||||
allowVibration: allowVibration ?? this.allowVibration,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ class DivinationResultData {
|
||||
required this.lowerName,
|
||||
required this.signType,
|
||||
required this.keywords,
|
||||
required this.focusPoints,
|
||||
required this.conclusion,
|
||||
required this.analysis,
|
||||
required this.suggestion,
|
||||
@@ -29,6 +30,7 @@ class DivinationResultData {
|
||||
final String lowerName;
|
||||
final String signType;
|
||||
final String keywords;
|
||||
final List<String> focusPoints;
|
||||
final String conclusion;
|
||||
final String analysis;
|
||||
final String suggestion;
|
||||
@@ -50,6 +52,7 @@ class DivinationResultData {
|
||||
'lowerName': lowerName,
|
||||
'signType': signType,
|
||||
'keywords': keywords,
|
||||
'focusPoints': focusPoints,
|
||||
'conclusion': conclusion,
|
||||
'analysis': analysis,
|
||||
'suggestion': suggestion,
|
||||
@@ -86,6 +89,7 @@ class DivinationResultData {
|
||||
lowerName: _requiredString(json, 'lowerName'),
|
||||
signType: _requiredString(json, 'signType'),
|
||||
keywords: _requiredString(json, 'keywords'),
|
||||
focusPoints: _requiredStringList(json, 'focusPoints'),
|
||||
conclusion: _requiredString(json, 'conclusion'),
|
||||
analysis: _requiredString(json, 'analysis'),
|
||||
suggestion: _requiredString(json, 'suggestion'),
|
||||
@@ -113,6 +117,21 @@ class DivinationResultData {
|
||||
}
|
||||
}
|
||||
|
||||
List<String> _requiredStringList(Map<String, dynamic> json, String key) {
|
||||
final raw = json[key];
|
||||
if (raw is! List<dynamic>) {
|
||||
throw FormatException('Invalid $key payload');
|
||||
}
|
||||
return raw
|
||||
.map((item) {
|
||||
if (item is! String) {
|
||||
throw FormatException('Invalid $key item payload');
|
||||
}
|
||||
return item;
|
||||
})
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
class GanzhiData {
|
||||
const GanzhiData({
|
||||
required this.yearGanZhi,
|
||||
|
||||
@@ -33,7 +33,6 @@ class DivinationRunService {
|
||||
|
||||
DerivedDivinationData? derived;
|
||||
String signLevel = '';
|
||||
String summary = '';
|
||||
List<String> conclusion = const <String>[];
|
||||
List<String> focusPoints = const <String>[];
|
||||
List<String> advice = const <String>[];
|
||||
@@ -64,7 +63,6 @@ class DivinationRunService {
|
||||
}
|
||||
if (type == 'TEXT_MESSAGE_END') {
|
||||
signLevel = _requiredString(event, 'sign_level');
|
||||
summary = _requiredString(event, 'summary');
|
||||
conclusion = _requiredStringList(event, 'conclusion');
|
||||
focusPoints = _requiredStringList(event, 'focus_points');
|
||||
advice = _requiredStringList(event, 'advice');
|
||||
@@ -107,7 +105,6 @@ class DivinationRunService {
|
||||
return DivinationRunAggregate(
|
||||
derived: derived,
|
||||
signLevel: signLevel,
|
||||
summary: summary,
|
||||
conclusion: conclusion,
|
||||
focusPoints: focusPoints,
|
||||
advice: advice,
|
||||
|
||||
@@ -300,6 +300,9 @@ class _AutoDivinationScreenState extends State<AutoDivinationScreen>
|
||||
}
|
||||
|
||||
Future<void> _vibrateStrong() async {
|
||||
if (!widget.params.allowVibration) {
|
||||
return;
|
||||
}
|
||||
final hasVibrator = await Vibration.hasVibrator();
|
||||
if (hasVibrator == true) {
|
||||
await Vibration.vibrate(duration: 280, amplitude: 255);
|
||||
@@ -313,15 +316,17 @@ class _AutoDivinationScreenState extends State<AutoDivinationScreen>
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DivinationGuideDialog(
|
||||
title: l10n.autoGuideTitle,
|
||||
title: l10n.divinationManualGuideTitle,
|
||||
guideImages: const [
|
||||
'assets/images/qigua/lc1.jpg',
|
||||
'assets/images/qigua/lc2.jpg',
|
||||
'assets/images/qigua/lc3.jpg',
|
||||
'assets/images/qigua/lc4.jpg',
|
||||
'assets/images/qigua/lc5.jpg',
|
||||
['assets/images/tutorial/tutorial_1.png'],
|
||||
['assets/images/tutorial/tutorial_2.png'],
|
||||
['assets/images/tutorial/tutorial_3.png'],
|
||||
],
|
||||
instructions: [
|
||||
l10n.divinationManualGuideStep1,
|
||||
l10n.divinationManualGuideStep2,
|
||||
l10n.divinationManualGuideStep3,
|
||||
],
|
||||
instructionText: l10n.autoGuideInstruction,
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -527,7 +532,7 @@ class _CoinColumn extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
Text(
|
||||
DivinationTerms.yinYang[isYang] ?? '',
|
||||
DivinationTerms.ziHua[isYang] ?? '',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: colors.onSurface,
|
||||
fontWeight: FontWeight.w700,
|
||||
@@ -562,8 +567,8 @@ class _CoinFace extends StatelessWidget {
|
||||
: (isYang ? 0 : 180);
|
||||
final showingYang = isSpinning ? rotationY < 90 : isYang;
|
||||
final image = showingYang
|
||||
? 'assets/images/qigua/yangmian.jpg'
|
||||
: 'assets/images/qigua/yinmian.jpg';
|
||||
? 'assets/images/qigua/hua.jpg'
|
||||
: 'assets/images/qigua/zi.jpg';
|
||||
return Transform(
|
||||
alignment: Alignment.center,
|
||||
transform: Matrix4.identity()
|
||||
|
||||
@@ -244,7 +244,7 @@ class _DivinationProcessingScreenState extends State<DivinationProcessingScreen>
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'I Ching',
|
||||
l10n.iChingTitle,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelSmall
|
||||
|
||||
@@ -175,6 +175,8 @@ class _DivinationResultScreenState extends State<DivinationResultScreen> {
|
||||
content: widget.data.conclusion,
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_FocusPointsCard(points: widget.data.focusPoints),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_AnalysisCard(
|
||||
title: l10n.resultAnalysis,
|
||||
content: widget.data.analysis,
|
||||
@@ -426,6 +428,71 @@ class _KeywordCard extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _FocusPointsCard extends StatelessWidget {
|
||||
const _FocusPointsCard({required this.points});
|
||||
|
||||
final List<String> points;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
final languageCode = Localizations.localeOf(context).languageCode;
|
||||
final title = languageCode == 'en' ? 'Focus Points' : '断卦要点';
|
||||
if (points.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Card(
|
||||
margin: EdgeInsets.zero,
|
||||
color: colors.surface,
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
color: colors.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
...List<Widget>.generate(points.length, (index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: AppSpacing.xs),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${index + 1}. ',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: colors.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
points[index],
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.bodyMedium?.copyWith(height: 1.55),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AnalysisCard extends StatelessWidget {
|
||||
const _AnalysisCard({required this.title, required this.content});
|
||||
|
||||
@@ -715,8 +782,11 @@ class _HexagramDetailCard extends StatelessWidget {
|
||||
for (int idx = 5; idx >= 0; idx--)
|
||||
_YaoDetailRow(
|
||||
line: data.yaoLines[idx],
|
||||
target: data.targetYaoLines[idx],
|
||||
showTarget: data.hasChangingYao,
|
||||
target: idx < data.targetYaoLines.length
|
||||
? data.targetYaoLines[idx]
|
||||
: data.yaoLines[idx],
|
||||
showTarget:
|
||||
data.hasChangingYao && idx < data.targetYaoLines.length,
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
const Align(
|
||||
|
||||
@@ -24,12 +24,14 @@ class DivinationScreen extends StatefulWidget {
|
||||
required this.userId,
|
||||
required this.onCompleted,
|
||||
this.runServiceOverride,
|
||||
this.allowVibration = true,
|
||||
});
|
||||
|
||||
final SessionStore sessionStore;
|
||||
final String userId;
|
||||
final Future<void> Function(DivinationResultData result) onCompleted;
|
||||
final DivinationRunService? runServiceOverride;
|
||||
final bool allowVibration;
|
||||
|
||||
@override
|
||||
State<DivinationScreen> createState() => _DivinationScreenState();
|
||||
@@ -51,7 +53,7 @@ class _DivinationScreenState extends State<DivinationScreen> {
|
||||
widget.runServiceOverride ??
|
||||
DivinationRunService(api: DivinationApi(apiClient: apiClient));
|
||||
_params = DivinationParams(
|
||||
method: DivinationMethod.manual,
|
||||
method: DivinationMethod.auto,
|
||||
questionType: QuestionType.career,
|
||||
question: '',
|
||||
divinationTime: DateTime.now(),
|
||||
@@ -169,7 +171,10 @@ class _DivinationScreenState extends State<DivinationScreen> {
|
||||
return;
|
||||
}
|
||||
|
||||
final nextParams = _params.copyWith(divinationTime: DateTime.now());
|
||||
final nextParams = _params.copyWith(
|
||||
divinationTime: DateTime.now(),
|
||||
allowVibration: widget.allowVibration,
|
||||
);
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => AutoDivinationScreen(
|
||||
@@ -406,13 +411,15 @@ Future<void> _showGuide(BuildContext context, AppLocalizations l10n) {
|
||||
return DivinationGuideDialog(
|
||||
title: l10n.divinationManualGuideTitle,
|
||||
guideImages: const [
|
||||
'assets/images/qigua/lc1.jpg',
|
||||
'assets/images/qigua/lc2.jpg',
|
||||
'assets/images/qigua/lc3.jpg',
|
||||
'assets/images/qigua/lc4.jpg',
|
||||
'assets/images/qigua/lc5.jpg',
|
||||
['assets/images/tutorial/tutorial_1.png'],
|
||||
['assets/images/tutorial/tutorial_2.png'],
|
||||
['assets/images/tutorial/tutorial_3.png'],
|
||||
],
|
||||
instructions: [
|
||||
l10n.divinationManualGuideStep1,
|
||||
l10n.divinationManualGuideStep2,
|
||||
l10n.divinationManualGuideStep3,
|
||||
],
|
||||
instructionText: l10n.divinationManualGuideInstruction,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
@@ -130,8 +132,16 @@ class _ManualDivinationScreenState extends State<ManualDivinationScreen>
|
||||
builder: (_) {
|
||||
return DivinationGuideDialog(
|
||||
title: l10n.manualSelectYaoTitle,
|
||||
guideImages: const ['lc2.jpg', 'lc3.jpg', 'lc4.jpg', 'lc5.jpg'],
|
||||
instructionText: l10n.manualYaoTipContent,
|
||||
guideImages: const [
|
||||
['assets/images/tutorial/tutorial_1.png'],
|
||||
['assets/images/tutorial/tutorial_2.png'],
|
||||
['assets/images/tutorial/tutorial_3.png'],
|
||||
],
|
||||
instructions: [
|
||||
l10n.divinationManualGuideStep1,
|
||||
l10n.divinationManualGuideStep2,
|
||||
l10n.divinationManualGuideStep3,
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -409,208 +419,199 @@ class _YaoSelectionCard extends StatelessWidget {
|
||||
int yaoIndex,
|
||||
void Function(int, YaoType) onSelect,
|
||||
) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
final options = <(String, YaoType, String)>[
|
||||
(
|
||||
'${DivinationTerms.yaoTypeLabels[YaoTypeLabel.youngYang]}(${DivinationTerms.youngYangSymbol})',
|
||||
YaoType.youngYang,
|
||||
'字字字',
|
||||
),
|
||||
(
|
||||
'${DivinationTerms.yaoTypeLabels[YaoTypeLabel.youngYin]}(${DivinationTerms.youngYinSymbol})',
|
||||
YaoType.youngYin,
|
||||
'花花花',
|
||||
),
|
||||
(
|
||||
'${DivinationTerms.yaoTypeLabels[YaoTypeLabel.oldYang]}(${DivinationTerms.oldYangSymbol})',
|
||||
YaoType.oldYang,
|
||||
'字字字',
|
||||
),
|
||||
(
|
||||
'${DivinationTerms.yaoTypeLabels[YaoTypeLabel.oldYin]}(${DivinationTerms.oldYinSymbol})',
|
||||
YaoType.oldYin,
|
||||
'花花花',
|
||||
),
|
||||
];
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.lg),
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
l10n.manualSelectYaoTitle,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: colors.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
icon: const Icon(Icons.close),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
...options.map((option) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: AppSpacing.sm),
|
||||
child: _YaoOptionCard(
|
||||
label: option.$1,
|
||||
pattern: option.$3,
|
||||
isSelected: false,
|
||||
onTap: () {
|
||||
onSelect(yaoIndex, option.$2);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: colors.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
l10n.coinFaceGuideTitle,
|
||||
style: Theme.of(context).textTheme.titleSmall
|
||||
?.copyWith(
|
||||
color: colors.onSurface,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
child: Image.asset(
|
||||
'assets/images/qigua/zihua.jpg',
|
||||
width: double.infinity,
|
||||
height: 120,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (context, error, stackTrace) =>
|
||||
Container(
|
||||
height: 120,
|
||||
color: colors.errorContainer,
|
||||
child: Center(
|
||||
child: Text(
|
||||
l10n.divinationClose,
|
||||
style: TextStyle(
|
||||
color: colors.onErrorContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
Text(
|
||||
l10n.coinFaceGuideDescription,
|
||||
style: Theme.of(context).textTheme.bodySmall
|
||||
?.copyWith(color: colors.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
return _ThreeCoinSelectorDialog(
|
||||
onConfirm: (yaoType) {
|
||||
onSelect(yaoIndex, yaoType);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _YaoOptionCard extends StatelessWidget {
|
||||
const _YaoOptionCard({
|
||||
required this.label,
|
||||
required this.pattern,
|
||||
required this.isSelected,
|
||||
required this.onTap,
|
||||
});
|
||||
class _ThreeCoinSelectorDialog extends StatefulWidget {
|
||||
const _ThreeCoinSelectorDialog({required this.onConfirm});
|
||||
|
||||
final String label;
|
||||
final String pattern;
|
||||
final bool isSelected;
|
||||
final VoidCallback onTap;
|
||||
final void Function(YaoType) onConfirm;
|
||||
|
||||
@override
|
||||
State<_ThreeCoinSelectorDialog> createState() =>
|
||||
_ThreeCoinSelectorDialogState();
|
||||
}
|
||||
|
||||
class _ThreeCoinSelectorDialogState extends State<_ThreeCoinSelectorDialog> {
|
||||
final List<bool> _coinStates = [false, false, false];
|
||||
|
||||
YaoType get _currentYaoType {
|
||||
final huaCount = _coinStates.where((isHua) => isHua).length;
|
||||
return switch (huaCount) {
|
||||
0 => YaoType.oldYin,
|
||||
1 => YaoType.youngYang,
|
||||
2 => YaoType.youngYin,
|
||||
3 => YaoType.oldYang,
|
||||
_ => YaoType.undetermined,
|
||||
};
|
||||
}
|
||||
|
||||
void _toggleCoin(int index) {
|
||||
setState(() {
|
||||
_coinStates[index] = !_coinStates[index];
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
return Material(
|
||||
color: isSelected ? colors.primaryContainer : colors.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm + 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
border: Border.all(
|
||||
color: isSelected ? colors.primary : colors.outlineVariant,
|
||||
width: isSelected ? 2 : 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: isSelected
|
||||
? colors.onPrimaryContainer
|
||||
: colors.onSurface,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
pattern,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: isSelected
|
||||
? colors.onPrimaryContainer.withValues(alpha: 0.7)
|
||||
: colors.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.lg),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
l10n.manualSelectYaoTitle,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: colors.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
icon: const Icon(Icons.close),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: List.generate(3, (index) {
|
||||
return _FlippingCoin(
|
||||
isHua: _coinStates[index],
|
||||
onTap: () => _toggleCoin(index),
|
||||
);
|
||||
}),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: colors.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
),
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
color: isSelected
|
||||
? colors.onPrimaryContainer
|
||||
: colors.onSurfaceVariant,
|
||||
child: Text(
|
||||
l10n.manualCoinSelectHint,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.bodySmall?.copyWith(color: colors.onSurfaceVariant),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: FilledButton(
|
||||
onPressed: () => widget.onConfirm(_currentYaoType),
|
||||
child: Text(l10n.confirm),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FlippingCoin extends StatefulWidget {
|
||||
const _FlippingCoin({required this.isHua, required this.onTap});
|
||||
|
||||
final bool isHua;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
State<_FlippingCoin> createState() => _FlippingCoinState();
|
||||
}
|
||||
|
||||
class _FlippingCoinState extends State<_FlippingCoin>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
late Animation<double> _animation;
|
||||
bool _showHua = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_showHua = widget.isHua;
|
||||
_controller = AnimationController(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
vsync: this,
|
||||
);
|
||||
_animation = Tween<double>(
|
||||
begin: 0,
|
||||
end: 1,
|
||||
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut));
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(_FlippingCoin oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.isHua != widget.isHua) {
|
||||
if (widget.isHua != _showHua) {
|
||||
_controller.forward().then((_) {
|
||||
setState(() {
|
||||
_showHua = widget.isHua;
|
||||
});
|
||||
_controller.reverse();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: widget.onTap,
|
||||
child: AnimatedBuilder(
|
||||
animation: _animation,
|
||||
builder: (context, child) {
|
||||
final angle = _animation.value * pi;
|
||||
return Transform(
|
||||
alignment: Alignment.center,
|
||||
transform: Matrix4.identity()
|
||||
..setEntry(3, 2, 0.001)
|
||||
..rotateY(angle),
|
||||
child: ClipOval(
|
||||
child: Image.asset(
|
||||
_showHua
|
||||
? 'assets/images/qigua/hua.jpg'
|
||||
: 'assets/images/qigua/zi.jpg',
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,11 +80,15 @@
|
||||
"signBad": "Inauspicious",
|
||||
"language": "Language",
|
||||
"settingsTitle": "Settings",
|
||||
"settingsSectionGeneral": "General",
|
||||
"settingsSectionGeneral": "Preferences",
|
||||
"settingsSectionQuickAccess": "Primary Menu",
|
||||
"settingsSectionAccount": "Account",
|
||||
"settingsSectionPrivacy": "Privacy",
|
||||
"settingsSectionNotification": "Notifications",
|
||||
"settingsInterfaceLanguage": "Interface Language",
|
||||
"settingsAiLanguage": "AI Response Language",
|
||||
"settingsNotificationAllow": "Allow Notifications",
|
||||
"settingsNotificationVibration": "Allow Vibration",
|
||||
"settingsSectionAbout": "About",
|
||||
"settingsGeneralTitle": "General Settings",
|
||||
"settingsGeneralSubtitle": "Language: {currentLanguage}. Other fields are reserved to match profiles.settings.",
|
||||
@@ -166,14 +170,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"settingsCoinCenterDescription": "Payment is not connected yet. The UI now shows packages and the recharge entry.",
|
||||
"settingsCoinCenterDescription": "",
|
||||
"settingsCoinRechargeSection": "Recharge Packages",
|
||||
"settingsCoinPackBasic": "Starter Pack",
|
||||
"settingsCoinPackPopular": "Popular Pack",
|
||||
"settingsCoinPackPremium": "Premium Pack",
|
||||
"settingsCoinPackPopularBadge": "Popular",
|
||||
"settingsPurchaseButton": "Pay Now",
|
||||
"settingsPurchasePending": "Payment is not connected yet",
|
||||
"settingsPurchasePending": "",
|
||||
"settingsCoinAmount": "{amount} credits",
|
||||
"@settingsCoinAmount": {
|
||||
"placeholders": {
|
||||
@@ -226,7 +230,9 @@
|
||||
"divinationMethodTipManual": "Manual: Prepare three identical coins.",
|
||||
"divinationMethodTipRecommend": "Manual casting provides higher accuracy.",
|
||||
"divinationManualGuideTitle": "Manual Casting Tutorial",
|
||||
"divinationManualGuideInstruction": "Prepare three identical coins and cast six times following the guide.",
|
||||
"divinationManualGuideStep1": "Left: Pattern side. Right: Inscription side. Prepare three identical coins or similar tokens.",
|
||||
"divinationManualGuideStep2": "Hold the coins in both hands, think about your question, then toss them onto a table. Record how many inscription sides and pattern sides appear.",
|
||||
"divinationManualGuideStep3": "Record each result by whether the inscription side or pattern side faces up. Repeat 6 times, recording from bottom to top.",
|
||||
"divinationIAcknowledge": "I Understand",
|
||||
"divinationClose": "Close",
|
||||
"divinationModify": "Modify",
|
||||
@@ -287,6 +293,7 @@
|
||||
"transitionPreparing": "Deriving...",
|
||||
"transitionDeriving": "Analyzing...",
|
||||
"transitionDone": "Complete\nTap to view",
|
||||
"iChingTitle": "I Ching",
|
||||
"processingCardQianTitle": "Qian • The Creative",
|
||||
"processingCardQianQuote": "The movement of Heaven is full of power; thus the noble one makes himself strong and tireless.",
|
||||
"processingCardDuiTitle": "Dui • The Joyous",
|
||||
@@ -321,6 +328,7 @@
|
||||
"manualYaoInstruction": "Tap to view casting method and coin combination guide",
|
||||
"manualYaoTipTitle": "Tip",
|
||||
"manualYaoTipContent": "Select from bottom to top, not top to bottom.\n\nCast three coins together, select once each time, six times total.",
|
||||
"manualCoinSelectHint": "Tap coins to flip between inscription and pattern. Record your casting result.",
|
||||
"autoScreenTitle": "Auto Casting",
|
||||
"autoSelectTime": "Select time",
|
||||
"autoCoinDivination": "Coin Casting",
|
||||
@@ -365,5 +373,33 @@
|
||||
"cancel": "Cancel",
|
||||
"autoSelectTime": "Select Time",
|
||||
"coinFaceGuideTitle": "Coin Face Guide",
|
||||
"coinFaceGuideDescription": "字 = side with inscription\n花 = side with pattern"
|
||||
"coinFaceGuideDescription": "Left: Pattern side. Right: Inscription side.",
|
||||
"settingsInviteTitle": "My Invitation",
|
||||
"settingsInviteSubtitle": "Invite friends, earn rewards together",
|
||||
"settingsInviteMyCode": "My Invite Code",
|
||||
"settingsInviteCopySuccess": "Invite code copied",
|
||||
"settingsInviteCopied": "Copied",
|
||||
"settingsInviteCopy": "Copy",
|
||||
"settingsInviteStats": "Invited: {count} friend(s)",
|
||||
"@settingsInviteStats": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"settingsInviteBindCode": "Bind Invite Code",
|
||||
"settingsInviteBindHint": "Enter your friend's invite code",
|
||||
"settingsInviteBindPlaceholder": "6-digit code",
|
||||
"settingsInviteBindButton": "Bind",
|
||||
"settingsInviteBindSuccess": "Invite code bound successfully",
|
||||
"settingsInviteBindFailed": "Failed to bind invite code",
|
||||
"settingsInviteGenerateTitle": "Generate My Invite Code",
|
||||
"settingsInviteGenerateButton": "Generate My Invite Code",
|
||||
"settingsInviteGenerateSuccess": "Invite code generated",
|
||||
"settingsInviteEmptyTitle": "Invite Friends, Earn Rewards",
|
||||
"settingsInviteEmptyDescription": "Each friend who registers using your invite code gives you bonus credits",
|
||||
"settingsInviteInputLabel": "Enter invite code (optional)",
|
||||
"settingsInviteInputHint": "Enter code to bind your inviter",
|
||||
"settingsInviteInvalidCode": "Please enter a valid 6-character invite code"
|
||||
}
|
||||
|
||||
@@ -497,7 +497,7 @@ abstract class AppLocalizations {
|
||||
/// No description provided for @settingsSectionGeneral.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'通用设置'**
|
||||
/// **'偏好设置'**
|
||||
String get settingsSectionGeneral;
|
||||
|
||||
/// No description provided for @settingsSectionQuickAccess.
|
||||
@@ -524,6 +524,30 @@ abstract class AppLocalizations {
|
||||
/// **'通知设置'**
|
||||
String get settingsSectionNotification;
|
||||
|
||||
/// No description provided for @settingsInterfaceLanguage.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'界面语言'**
|
||||
String get settingsInterfaceLanguage;
|
||||
|
||||
/// No description provided for @settingsAiLanguage.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'AI 回复语言'**
|
||||
String get settingsAiLanguage;
|
||||
|
||||
/// No description provided for @settingsNotificationAllow.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'允许通知'**
|
||||
String get settingsNotificationAllow;
|
||||
|
||||
/// No description provided for @settingsNotificationVibration.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'允许振动'**
|
||||
String get settingsNotificationVibration;
|
||||
|
||||
/// No description provided for @settingsSectionAbout.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
@@ -584,12 +608,6 @@ abstract class AppLocalizations {
|
||||
/// **'点数可用于后续起卦与相关服务消费'**
|
||||
String get settingsCoinHeroSubtitle;
|
||||
|
||||
/// No description provided for @settingsAiLanguage.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'AI 回复语言'**
|
||||
String get settingsAiLanguage;
|
||||
|
||||
/// No description provided for @settingsAiLanguageHint.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
@@ -845,7 +863,7 @@ abstract class AppLocalizations {
|
||||
/// No description provided for @settingsCoinCenterDescription.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'充值入口暂未接入支付逻辑,先展示套餐与购买流程入口。'**
|
||||
/// **''**
|
||||
String get settingsCoinCenterDescription;
|
||||
|
||||
/// No description provided for @settingsCoinRechargeSection.
|
||||
@@ -887,7 +905,7 @@ abstract class AppLocalizations {
|
||||
/// No description provided for @settingsPurchasePending.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'支付能力暂未接入'**
|
||||
/// **''**
|
||||
String get settingsPurchasePending;
|
||||
|
||||
/// No description provided for @settingsCoinAmount.
|
||||
@@ -1118,11 +1136,23 @@ abstract class AppLocalizations {
|
||||
/// **'手动起卦教程'**
|
||||
String get divinationManualGuideTitle;
|
||||
|
||||
/// No description provided for @divinationManualGuideInstruction.
|
||||
/// No description provided for @divinationManualGuideStep1.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'准备三枚同样铜钱,按页面引导连续完成六次摇卦。'**
|
||||
String get divinationManualGuideInstruction;
|
||||
/// **'左侧为花面,右侧为字面。准备三枚相同的钱币,任何类似款式均可。'**
|
||||
String get divinationManualGuideStep1;
|
||||
|
||||
/// No description provided for @divinationManualGuideStep2.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'双手捧起钱币,闭目心中默念所问之事,然后抛掷钱币于桌面,记录字面和花面出现的次数。'**
|
||||
String get divinationManualGuideStep2;
|
||||
|
||||
/// No description provided for @divinationManualGuideStep3.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'记录每次结果,按照「字面在上还是花面在上」记录。重复六次,从下往上记录。'**
|
||||
String get divinationManualGuideStep3;
|
||||
|
||||
/// No description provided for @divinationIAcknowledge.
|
||||
///
|
||||
@@ -1382,100 +1412,106 @@ abstract class AppLocalizations {
|
||||
/// **'解卦完成\n点击查看'**
|
||||
String get transitionDone;
|
||||
|
||||
/// No description provided for @iChingTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'周易'**
|
||||
String get iChingTitle;
|
||||
|
||||
/// No description provided for @processingCardQianTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'Qian • The Creative'**
|
||||
/// **'乾 • 元亨利贞'**
|
||||
String get processingCardQianTitle;
|
||||
|
||||
/// No description provided for @processingCardQianQuote.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'The movement of Heaven is full of power; thus the noble one makes himself strong and tireless.'**
|
||||
/// **'天行健,君子以自强不息。'**
|
||||
String get processingCardQianQuote;
|
||||
|
||||
/// No description provided for @processingCardDuiTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'Dui • The Joyous'**
|
||||
/// **'兑 • 亨利贞'**
|
||||
String get processingCardDuiTitle;
|
||||
|
||||
/// No description provided for @processingCardDuiQuote.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'Joy grounded in integrity brings openness, harmony, and right expression.'**
|
||||
/// **'丽泽兑,君子以朋友讲习。'**
|
||||
String get processingCardDuiQuote;
|
||||
|
||||
/// No description provided for @processingCardLiTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'Li • The Clinging Fire'**
|
||||
/// **'离 • 明两作亨利贞'**
|
||||
String get processingCardLiTitle;
|
||||
|
||||
/// No description provided for @processingCardLiQuote.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'With clear brilliance, the great one illumines all directions.'**
|
||||
/// **'大人以继明照于四方。'**
|
||||
String get processingCardLiQuote;
|
||||
|
||||
/// No description provided for @processingCardZhenTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'Zhen • The Arousing Thunder'**
|
||||
/// **'震 • 亨震来虩虩,笑言哑哑'**
|
||||
String get processingCardZhenTitle;
|
||||
|
||||
/// No description provided for @processingCardZhenQuote.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'Shock awakens the heart; composure turns fear into growth.'**
|
||||
/// **'震惊百里,惊远而惧迩也。'**
|
||||
String get processingCardZhenQuote;
|
||||
|
||||
/// No description provided for @processingCardXunTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'Xun • The Gentle Wind'**
|
||||
/// **'巽 • 小亨利贞'**
|
||||
String get processingCardXunTitle;
|
||||
|
||||
/// No description provided for @processingCardXunQuote.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'Gentle penetration furthers progress and helps one meet the right people.'**
|
||||
/// **'随风,君子以申命行事。'**
|
||||
String get processingCardXunQuote;
|
||||
|
||||
/// No description provided for @processingCardKanTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'Kan • The Abysmal Water'**
|
||||
/// **'坎 • 习坎有孚维心亨'**
|
||||
String get processingCardKanTitle;
|
||||
|
||||
/// No description provided for @processingCardKanQuote.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'In danger, sincerity and disciplined action carry one through.'**
|
||||
/// **'水流而不盈,行险而不失其信。'**
|
||||
String get processingCardKanQuote;
|
||||
|
||||
/// No description provided for @processingCardGenTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'Gen • Keeping Still Mountain'**
|
||||
/// **'艮 • 艮其背不获其身'**
|
||||
String get processingCardGenTitle;
|
||||
|
||||
/// No description provided for @processingCardGenQuote.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'Stillness at the proper time keeps one centered and steady in place.'**
|
||||
/// **'时止则止,时行则行,动静不失其时。'**
|
||||
String get processingCardGenQuote;
|
||||
|
||||
/// No description provided for @processingCardKunTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'Kun • The Receptive Earth'**
|
||||
/// **'坤 • 元亨利牝马之贞'**
|
||||
String get processingCardKunTitle;
|
||||
|
||||
/// No description provided for @processingCardKunQuote.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'The Earth\'s condition is devoted receptivity; the noble one carries all with broad virtue.'**
|
||||
/// **'地势坤,君子以厚德载物。'**
|
||||
String get processingCardKunQuote;
|
||||
|
||||
/// No description provided for @ganZhiInfo.
|
||||
@@ -1586,6 +1622,12 @@ abstract class AppLocalizations {
|
||||
/// **'请从下往上选,不是从上往下选。\n\n三枚铜钱一起摇,摇完一次选一次,一共摇六次。'**
|
||||
String get manualYaoTipContent;
|
||||
|
||||
/// No description provided for @manualCoinSelectHint.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'点击硬币可翻转,调整字面和花面。记录摇卦结果。'**
|
||||
String get manualCoinSelectHint;
|
||||
|
||||
/// No description provided for @autoScreenTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
@@ -1715,14 +1757,140 @@ abstract class AppLocalizations {
|
||||
/// No description provided for @coinFaceGuideTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'字花图片说明'**
|
||||
/// **'字花对照说明'**
|
||||
String get coinFaceGuideTitle;
|
||||
|
||||
/// No description provided for @coinFaceGuideDescription.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'字=铜钱有字的一面\n花=铜钱有花纹的一面'**
|
||||
/// **'左侧为花面,右侧为字面。'**
|
||||
String get coinFaceGuideDescription;
|
||||
|
||||
/// No description provided for @settingsInviteTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'我的邀请'**
|
||||
String get settingsInviteTitle;
|
||||
|
||||
/// No description provided for @settingsInviteSubtitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'邀请好友,共同获得奖励'**
|
||||
String get settingsInviteSubtitle;
|
||||
|
||||
/// No description provided for @settingsInviteMyCode.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'我的邀请码'**
|
||||
String get settingsInviteMyCode;
|
||||
|
||||
/// No description provided for @settingsInviteCopySuccess.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'邀请码已复制'**
|
||||
String get settingsInviteCopySuccess;
|
||||
|
||||
/// No description provided for @settingsInviteCopied.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'已复制'**
|
||||
String get settingsInviteCopied;
|
||||
|
||||
/// No description provided for @settingsInviteCopy.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'复制'**
|
||||
String get settingsInviteCopy;
|
||||
|
||||
/// No description provided for @settingsInviteStats.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'已邀请:{count} 位好友'**
|
||||
String settingsInviteStats(int count);
|
||||
|
||||
/// No description provided for @settingsInviteBindCode.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'绑定邀请码'**
|
||||
String get settingsInviteBindCode;
|
||||
|
||||
/// No description provided for @settingsInviteBindHint.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'输入好友的邀请码'**
|
||||
String get settingsInviteBindHint;
|
||||
|
||||
/// No description provided for @settingsInviteBindPlaceholder.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'6位邀请码'**
|
||||
String get settingsInviteBindPlaceholder;
|
||||
|
||||
/// No description provided for @settingsInviteBindButton.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'绑定'**
|
||||
String get settingsInviteBindButton;
|
||||
|
||||
/// No description provided for @settingsInviteBindSuccess.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'邀请码绑定成功'**
|
||||
String get settingsInviteBindSuccess;
|
||||
|
||||
/// No description provided for @settingsInviteBindFailed.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'邀请码绑定失败'**
|
||||
String get settingsInviteBindFailed;
|
||||
|
||||
/// No description provided for @settingsInviteGenerateTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'生成我的邀请码'**
|
||||
String get settingsInviteGenerateTitle;
|
||||
|
||||
/// No description provided for @settingsInviteGenerateButton.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'生成我的邀请码'**
|
||||
String get settingsInviteGenerateButton;
|
||||
|
||||
/// No description provided for @settingsInviteGenerateSuccess.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'邀请码生成成功'**
|
||||
String get settingsInviteGenerateSuccess;
|
||||
|
||||
/// No description provided for @settingsInviteEmptyTitle.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'邀请好友,获得奖励'**
|
||||
String get settingsInviteEmptyTitle;
|
||||
|
||||
/// No description provided for @settingsInviteEmptyDescription.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'每成功邀请一位好友注册,您将获得积分奖励'**
|
||||
String get settingsInviteEmptyDescription;
|
||||
|
||||
/// No description provided for @settingsInviteInputLabel.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'输入邀请码(选填)'**
|
||||
String get settingsInviteInputLabel;
|
||||
|
||||
/// No description provided for @settingsInviteInputHint.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'输入邀请码绑定您的邀请人'**
|
||||
String get settingsInviteInputHint;
|
||||
|
||||
/// No description provided for @settingsInviteInvalidCode.
|
||||
///
|
||||
/// In zh, this message translates to:
|
||||
/// **'请输入有效的6位邀请码'**
|
||||
String get settingsInviteInvalidCode;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
||||
@@ -217,7 +217,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get settingsTitle => 'Settings';
|
||||
|
||||
@override
|
||||
String get settingsSectionGeneral => 'General';
|
||||
String get settingsSectionGeneral => 'Preferences';
|
||||
|
||||
@override
|
||||
String get settingsSectionQuickAccess => 'Primary Menu';
|
||||
@@ -231,6 +231,18 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get settingsSectionNotification => 'Notifications';
|
||||
|
||||
@override
|
||||
String get settingsInterfaceLanguage => 'Interface Language';
|
||||
|
||||
@override
|
||||
String get settingsAiLanguage => 'AI Response Language';
|
||||
|
||||
@override
|
||||
String get settingsNotificationAllow => 'Allow Notifications';
|
||||
|
||||
@override
|
||||
String get settingsNotificationVibration => 'Allow Vibration';
|
||||
|
||||
@override
|
||||
String get settingsSectionAbout => 'About';
|
||||
|
||||
@@ -268,9 +280,6 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get settingsCoinHeroSubtitle =>
|
||||
'Credits will be used for casting and related services later.';
|
||||
|
||||
@override
|
||||
String get settingsAiLanguage => 'AI Response Language';
|
||||
|
||||
@override
|
||||
String get settingsAiLanguageHint =>
|
||||
'This field will align with profiles.settings.preferences.ai_language once the real preference flow is connected.';
|
||||
@@ -411,8 +420,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
}
|
||||
|
||||
@override
|
||||
String get settingsCoinCenterDescription =>
|
||||
'Payment is not connected yet. The UI now shows packages and the recharge entry.';
|
||||
String get settingsCoinCenterDescription => '';
|
||||
|
||||
@override
|
||||
String get settingsCoinRechargeSection => 'Recharge Packages';
|
||||
@@ -433,7 +441,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get settingsPurchaseButton => 'Pay Now';
|
||||
|
||||
@override
|
||||
String get settingsPurchasePending => 'Payment is not connected yet';
|
||||
String get settingsPurchasePending => '';
|
||||
|
||||
@override
|
||||
String settingsCoinAmount(int amount) {
|
||||
@@ -566,8 +574,16 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get divinationManualGuideTitle => 'Manual Casting Tutorial';
|
||||
|
||||
@override
|
||||
String get divinationManualGuideInstruction =>
|
||||
'Prepare three identical coins and cast six times following the guide.';
|
||||
String get divinationManualGuideStep1 =>
|
||||
'Left: Pattern side. Right: Inscription side. Prepare three identical coins or similar tokens.';
|
||||
|
||||
@override
|
||||
String get divinationManualGuideStep2 =>
|
||||
'Hold the coins in both hands, think about your question, then toss them onto a table. Record how many inscription sides and pattern sides appear.';
|
||||
|
||||
@override
|
||||
String get divinationManualGuideStep3 =>
|
||||
'Record each result by whether the inscription side or pattern side faces up. Repeat 6 times, recording from bottom to top.';
|
||||
|
||||
@override
|
||||
String get divinationIAcknowledge => 'I Understand';
|
||||
@@ -703,6 +719,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get transitionDone => 'Complete\nTap to view';
|
||||
|
||||
@override
|
||||
String get iChingTitle => 'I Ching';
|
||||
|
||||
@override
|
||||
String get processingCardQianTitle => 'Qian • The Creative';
|
||||
|
||||
@@ -815,6 +834,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get manualYaoTipContent =>
|
||||
'Select from bottom to top, not top to bottom.\n\nCast three coins together, select once each time, six times total.';
|
||||
|
||||
@override
|
||||
String get manualCoinSelectHint =>
|
||||
'Tap coins to flip between inscription and pattern. Record your casting result.';
|
||||
|
||||
@override
|
||||
String get autoScreenTitle => 'Auto Casting';
|
||||
|
||||
@@ -890,5 +913,72 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get coinFaceGuideDescription =>
|
||||
'字 = side with inscription\n花 = side with pattern';
|
||||
'Left: Pattern side. Right: Inscription side.';
|
||||
|
||||
@override
|
||||
String get settingsInviteTitle => 'My Invitation';
|
||||
|
||||
@override
|
||||
String get settingsInviteSubtitle => 'Invite friends, earn rewards together';
|
||||
|
||||
@override
|
||||
String get settingsInviteMyCode => 'My Invite Code';
|
||||
|
||||
@override
|
||||
String get settingsInviteCopySuccess => 'Invite code copied';
|
||||
|
||||
@override
|
||||
String get settingsInviteCopied => 'Copied';
|
||||
|
||||
@override
|
||||
String get settingsInviteCopy => 'Copy';
|
||||
|
||||
@override
|
||||
String settingsInviteStats(int count) {
|
||||
return 'Invited: $count friend(s)';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settingsInviteBindCode => 'Bind Invite Code';
|
||||
|
||||
@override
|
||||
String get settingsInviteBindHint => 'Enter your friend\'s invite code';
|
||||
|
||||
@override
|
||||
String get settingsInviteBindPlaceholder => '6-digit code';
|
||||
|
||||
@override
|
||||
String get settingsInviteBindButton => 'Bind';
|
||||
|
||||
@override
|
||||
String get settingsInviteBindSuccess => 'Invite code bound successfully';
|
||||
|
||||
@override
|
||||
String get settingsInviteBindFailed => 'Failed to bind invite code';
|
||||
|
||||
@override
|
||||
String get settingsInviteGenerateTitle => 'Generate My Invite Code';
|
||||
|
||||
@override
|
||||
String get settingsInviteGenerateButton => 'Generate My Invite Code';
|
||||
|
||||
@override
|
||||
String get settingsInviteGenerateSuccess => 'Invite code generated';
|
||||
|
||||
@override
|
||||
String get settingsInviteEmptyTitle => 'Invite Friends, Earn Rewards';
|
||||
|
||||
@override
|
||||
String get settingsInviteEmptyDescription =>
|
||||
'Each friend who registers using your invite code gives you bonus credits';
|
||||
|
||||
@override
|
||||
String get settingsInviteInputLabel => 'Enter invite code (optional)';
|
||||
|
||||
@override
|
||||
String get settingsInviteInputHint => 'Enter code to bind your inviter';
|
||||
|
||||
@override
|
||||
String get settingsInviteInvalidCode =>
|
||||
'Please enter a valid 6-character invite code';
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
String get settingsTitle => '设置';
|
||||
|
||||
@override
|
||||
String get settingsSectionGeneral => '通用设置';
|
||||
String get settingsSectionGeneral => '偏好设置';
|
||||
|
||||
@override
|
||||
String get settingsSectionQuickAccess => '一级菜单';
|
||||
@@ -229,6 +229,18 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
@override
|
||||
String get settingsSectionNotification => '通知设置';
|
||||
|
||||
@override
|
||||
String get settingsInterfaceLanguage => '界面语言';
|
||||
|
||||
@override
|
||||
String get settingsAiLanguage => 'AI 回复语言';
|
||||
|
||||
@override
|
||||
String get settingsNotificationAllow => '允许通知';
|
||||
|
||||
@override
|
||||
String get settingsNotificationVibration => '允许振动';
|
||||
|
||||
@override
|
||||
String get settingsSectionAbout => '关于';
|
||||
|
||||
@@ -264,9 +276,6 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
@override
|
||||
String get settingsCoinHeroSubtitle => '点数可用于后续起卦与相关服务消费';
|
||||
|
||||
@override
|
||||
String get settingsAiLanguage => 'AI 回复语言';
|
||||
|
||||
@override
|
||||
String get settingsAiLanguageHint =>
|
||||
'该字段将对齐 profiles.settings.preferences.ai_language,后续接入真实偏好设置。';
|
||||
@@ -403,7 +412,7 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
}
|
||||
|
||||
@override
|
||||
String get settingsCoinCenterDescription => '充值入口暂未接入支付逻辑,先展示套餐与购买流程入口。';
|
||||
String get settingsCoinCenterDescription => '';
|
||||
|
||||
@override
|
||||
String get settingsCoinRechargeSection => '充值套餐';
|
||||
@@ -424,7 +433,7 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
String get settingsPurchaseButton => '立即支付';
|
||||
|
||||
@override
|
||||
String get settingsPurchasePending => '支付能力暂未接入';
|
||||
String get settingsPurchasePending => '';
|
||||
|
||||
@override
|
||||
String settingsCoinAmount(int amount) {
|
||||
@@ -549,7 +558,15 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
String get divinationManualGuideTitle => '手动起卦教程';
|
||||
|
||||
@override
|
||||
String get divinationManualGuideInstruction => '准备三枚同样铜钱,按页面引导连续完成六次摇卦。';
|
||||
String get divinationManualGuideStep1 => '左侧为花面,右侧为字面。准备三枚相同的钱币,任何类似款式均可。';
|
||||
|
||||
@override
|
||||
String get divinationManualGuideStep2 =>
|
||||
'双手捧起钱币,闭目心中默念所问之事,然后抛掷钱币于桌面,记录字面和花面出现的次数。';
|
||||
|
||||
@override
|
||||
String get divinationManualGuideStep3 =>
|
||||
'记录每次结果,按照「字面在上还是花面在上」记录。重复六次,从下往上记录。';
|
||||
|
||||
@override
|
||||
String get divinationIAcknowledge => '我知道了';
|
||||
@@ -686,60 +703,55 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
String get transitionDone => '解卦完成\n点击查看';
|
||||
|
||||
@override
|
||||
String get processingCardQianTitle => 'Qian • The Creative';
|
||||
String get iChingTitle => '周易';
|
||||
|
||||
@override
|
||||
String get processingCardQianQuote =>
|
||||
'The movement of Heaven is full of power; thus the noble one makes himself strong and tireless.';
|
||||
String get processingCardQianTitle => '乾 • 元亨利贞';
|
||||
|
||||
@override
|
||||
String get processingCardDuiTitle => 'Dui • The Joyous';
|
||||
String get processingCardQianQuote => '天行健,君子以自强不息。';
|
||||
|
||||
@override
|
||||
String get processingCardDuiQuote =>
|
||||
'Joy grounded in integrity brings openness, harmony, and right expression.';
|
||||
String get processingCardDuiTitle => '兑 • 亨利贞';
|
||||
|
||||
@override
|
||||
String get processingCardLiTitle => 'Li • The Clinging Fire';
|
||||
String get processingCardDuiQuote => '丽泽兑,君子以朋友讲习。';
|
||||
|
||||
@override
|
||||
String get processingCardLiQuote =>
|
||||
'With clear brilliance, the great one illumines all directions.';
|
||||
String get processingCardLiTitle => '离 • 明两作亨利贞';
|
||||
|
||||
@override
|
||||
String get processingCardZhenTitle => 'Zhen • The Arousing Thunder';
|
||||
String get processingCardLiQuote => '大人以继明照于四方。';
|
||||
|
||||
@override
|
||||
String get processingCardZhenQuote =>
|
||||
'Shock awakens the heart; composure turns fear into growth.';
|
||||
String get processingCardZhenTitle => '震 • 亨震来虩虩,笑言哑哑';
|
||||
|
||||
@override
|
||||
String get processingCardXunTitle => 'Xun • The Gentle Wind';
|
||||
String get processingCardZhenQuote => '震惊百里,惊远而惧迩也。';
|
||||
|
||||
@override
|
||||
String get processingCardXunQuote =>
|
||||
'Gentle penetration furthers progress and helps one meet the right people.';
|
||||
String get processingCardXunTitle => '巽 • 小亨利贞';
|
||||
|
||||
@override
|
||||
String get processingCardKanTitle => 'Kan • The Abysmal Water';
|
||||
String get processingCardXunQuote => '随风,君子以申命行事。';
|
||||
|
||||
@override
|
||||
String get processingCardKanQuote =>
|
||||
'In danger, sincerity and disciplined action carry one through.';
|
||||
String get processingCardKanTitle => '坎 • 习坎有孚维心亨';
|
||||
|
||||
@override
|
||||
String get processingCardGenTitle => 'Gen • Keeping Still Mountain';
|
||||
String get processingCardKanQuote => '水流而不盈,行险而不失其信。';
|
||||
|
||||
@override
|
||||
String get processingCardGenQuote =>
|
||||
'Stillness at the proper time keeps one centered and steady in place.';
|
||||
String get processingCardGenTitle => '艮 • 艮其背不获其身';
|
||||
|
||||
@override
|
||||
String get processingCardKunTitle => 'Kun • The Receptive Earth';
|
||||
String get processingCardGenQuote => '时止则止,时行则行,动静不失其时。';
|
||||
|
||||
@override
|
||||
String get processingCardKunQuote =>
|
||||
'The Earth\'s condition is devoted receptivity; the noble one carries all with broad virtue.';
|
||||
String get processingCardKunTitle => '坤 • 元亨利牝马之贞';
|
||||
|
||||
@override
|
||||
String get processingCardKunQuote => '地势坤,君子以厚德载物。';
|
||||
|
||||
@override
|
||||
String get ganZhiInfo => '干支信息';
|
||||
@@ -795,6 +807,9 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
@override
|
||||
String get manualYaoTipContent => '请从下往上选,不是从上往下选。\n\n三枚铜钱一起摇,摇完一次选一次,一共摇六次。';
|
||||
|
||||
@override
|
||||
String get manualCoinSelectHint => '点击硬币可翻转,调整字面和花面。记录摇卦结果。';
|
||||
|
||||
@override
|
||||
String get autoScreenTitle => '自动起卦';
|
||||
|
||||
@@ -865,8 +880,73 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
String get cancel => '取消';
|
||||
|
||||
@override
|
||||
String get coinFaceGuideTitle => '字花图片说明';
|
||||
String get coinFaceGuideTitle => '字花对照说明';
|
||||
|
||||
@override
|
||||
String get coinFaceGuideDescription => '字=铜钱有字的一面\n花=铜钱有花纹的一面';
|
||||
String get coinFaceGuideDescription => '左侧为花面,右侧为字面。';
|
||||
|
||||
@override
|
||||
String get settingsInviteTitle => '我的邀请';
|
||||
|
||||
@override
|
||||
String get settingsInviteSubtitle => '邀请好友,共同获得奖励';
|
||||
|
||||
@override
|
||||
String get settingsInviteMyCode => '我的邀请码';
|
||||
|
||||
@override
|
||||
String get settingsInviteCopySuccess => '邀请码已复制';
|
||||
|
||||
@override
|
||||
String get settingsInviteCopied => '已复制';
|
||||
|
||||
@override
|
||||
String get settingsInviteCopy => '复制';
|
||||
|
||||
@override
|
||||
String settingsInviteStats(int count) {
|
||||
return '已邀请:$count 位好友';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settingsInviteBindCode => '绑定邀请码';
|
||||
|
||||
@override
|
||||
String get settingsInviteBindHint => '输入好友的邀请码';
|
||||
|
||||
@override
|
||||
String get settingsInviteBindPlaceholder => '6位邀请码';
|
||||
|
||||
@override
|
||||
String get settingsInviteBindButton => '绑定';
|
||||
|
||||
@override
|
||||
String get settingsInviteBindSuccess => '邀请码绑定成功';
|
||||
|
||||
@override
|
||||
String get settingsInviteBindFailed => '邀请码绑定失败';
|
||||
|
||||
@override
|
||||
String get settingsInviteGenerateTitle => '生成我的邀请码';
|
||||
|
||||
@override
|
||||
String get settingsInviteGenerateButton => '生成我的邀请码';
|
||||
|
||||
@override
|
||||
String get settingsInviteGenerateSuccess => '邀请码生成成功';
|
||||
|
||||
@override
|
||||
String get settingsInviteEmptyTitle => '邀请好友,获得奖励';
|
||||
|
||||
@override
|
||||
String get settingsInviteEmptyDescription => '每成功邀请一位好友注册,您将获得积分奖励';
|
||||
|
||||
@override
|
||||
String get settingsInviteInputLabel => '输入邀请码(选填)';
|
||||
|
||||
@override
|
||||
String get settingsInviteInputHint => '输入邀请码绑定您的邀请人';
|
||||
|
||||
@override
|
||||
String get settingsInviteInvalidCode => '请输入有效的6位邀请码';
|
||||
}
|
||||
|
||||
@@ -80,11 +80,15 @@
|
||||
"signBad": "下下签",
|
||||
"language": "语言",
|
||||
"settingsTitle": "设置",
|
||||
"settingsSectionGeneral": "通用设置",
|
||||
"settingsSectionGeneral": "偏好设置",
|
||||
"settingsSectionQuickAccess": "一级菜单",
|
||||
"settingsSectionAccount": "账户操作",
|
||||
"settingsSectionPrivacy": "隐私设置",
|
||||
"settingsSectionNotification": "通知设置",
|
||||
"settingsInterfaceLanguage": "界面语言",
|
||||
"settingsAiLanguage": "AI回复语言",
|
||||
"settingsNotificationAllow": "允许通知",
|
||||
"settingsNotificationVibration": "允许振动",
|
||||
"settingsSectionAbout": "关于",
|
||||
"settingsGeneralTitle": "通用设置",
|
||||
"settingsGeneralSubtitle": "语言:{currentLanguage},其余字段按 profiles.settings 结构预留",
|
||||
@@ -166,14 +170,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"settingsCoinCenterDescription": "充值入口暂未接入支付逻辑,先展示套餐与购买流程入口。",
|
||||
"settingsCoinCenterDescription": "",
|
||||
"settingsCoinRechargeSection": "充值套餐",
|
||||
"settingsCoinPackBasic": "入门补充包",
|
||||
"settingsCoinPackPopular": "常用加量包",
|
||||
"settingsCoinPackPremium": "高频进阶包",
|
||||
"settingsCoinPackPopularBadge": "推荐",
|
||||
"settingsPurchaseButton": "立即支付",
|
||||
"settingsPurchasePending": "支付能力暂未接入",
|
||||
"settingsPurchasePending": "",
|
||||
"settingsCoinAmount": "{amount} 点数",
|
||||
"@settingsCoinAmount": {
|
||||
"placeholders": {
|
||||
@@ -226,7 +230,9 @@
|
||||
"divinationMethodTipManual": "手动起卦:需要准备三枚同样的铜钱或硬币。",
|
||||
"divinationMethodTipRecommend": "推荐使用手动起卦,卦象解读准确概率更高。",
|
||||
"divinationManualGuideTitle": "手动起卦教程",
|
||||
"divinationManualGuideInstruction": "准备三枚同样铜钱,按页面引导连续完成六次摇卦。",
|
||||
"divinationManualGuideStep1": "左侧为花面,右侧为字面。准备三枚相同的钱币,任何类似款式均可。",
|
||||
"divinationManualGuideStep2": "双手捧起钱币,闭目心中默念所问之事,然后抛掷钱币于桌面,记录字面和花面出现的次数。",
|
||||
"divinationManualGuideStep3": "记录每次结果,按照「字面在上还是花面在上」记录。重复六次,从下往上记录。",
|
||||
"divinationIAcknowledge": "我知道了",
|
||||
"divinationClose": "关闭",
|
||||
"divinationModify": "修改",
|
||||
@@ -287,22 +293,23 @@
|
||||
"transitionPreparing": "天机推演中",
|
||||
"transitionDeriving": "正在解卦",
|
||||
"transitionDone": "解卦完成\n点击查看",
|
||||
"processingCardQianTitle": "Qian • The Creative",
|
||||
"processingCardQianQuote": "The movement of Heaven is full of power; thus the noble one makes himself strong and tireless.",
|
||||
"processingCardDuiTitle": "Dui • The Joyous",
|
||||
"processingCardDuiQuote": "Joy grounded in integrity brings openness, harmony, and right expression.",
|
||||
"processingCardLiTitle": "Li • The Clinging Fire",
|
||||
"processingCardLiQuote": "With clear brilliance, the great one illumines all directions.",
|
||||
"processingCardZhenTitle": "Zhen • The Arousing Thunder",
|
||||
"processingCardZhenQuote": "Shock awakens the heart; composure turns fear into growth.",
|
||||
"processingCardXunTitle": "Xun • The Gentle Wind",
|
||||
"processingCardXunQuote": "Gentle penetration furthers progress and helps one meet the right people.",
|
||||
"processingCardKanTitle": "Kan • The Abysmal Water",
|
||||
"processingCardKanQuote": "In danger, sincerity and disciplined action carry one through.",
|
||||
"processingCardGenTitle": "Gen • Keeping Still Mountain",
|
||||
"processingCardGenQuote": "Stillness at the proper time keeps one centered and steady in place.",
|
||||
"processingCardKunTitle": "Kun • The Receptive Earth",
|
||||
"processingCardKunQuote": "The Earth's condition is devoted receptivity; the noble one carries all with broad virtue.",
|
||||
"iChingTitle": "周易",
|
||||
"processingCardQianTitle": "乾 • 元亨利贞",
|
||||
"processingCardQianQuote": "天行健,君子以自强不息。",
|
||||
"processingCardDuiTitle": "兑 • 亨利贞",
|
||||
"processingCardDuiQuote": "丽泽兑,君子以朋友讲习。",
|
||||
"processingCardLiTitle": "离 • 明两作亨利贞",
|
||||
"processingCardLiQuote": "大人以继明照于四方。",
|
||||
"processingCardZhenTitle": "震 • 亨震来虩虩,笑言哑哑",
|
||||
"processingCardZhenQuote": "震惊百里,惊远而惧迩也。",
|
||||
"processingCardXunTitle": "巽 • 小亨利贞",
|
||||
"processingCardXunQuote": "随风,君子以申命行事。",
|
||||
"processingCardKanTitle": "坎 • 习坎有孚维心亨",
|
||||
"processingCardKanQuote": "水流而不盈,行险而不失其信。",
|
||||
"processingCardGenTitle": "艮 • 艮其背不获其身",
|
||||
"processingCardGenQuote": "时止则止,时行则行,动静不失其时。",
|
||||
"processingCardKunTitle": "坤 • 元亨利牝马之贞",
|
||||
"processingCardKunQuote": "地势坤,君子以厚德载物。",
|
||||
"ganZhiInfo": "干支信息",
|
||||
"wuXingWangShuai": "五行旺衰",
|
||||
"ganZhiKongWang": "空亡信息",
|
||||
@@ -321,6 +328,7 @@
|
||||
"manualYaoInstruction": "点击查看起卦方法与铜钱字花组合说明",
|
||||
"manualYaoTipTitle": "提示",
|
||||
"manualYaoTipContent": "请从下往上选,不是从上往下选。\n\n三枚铜钱一起摇,摇完一次选一次,一共摇六次。",
|
||||
"manualCoinSelectHint": "点击硬币可翻转,调整字面和花面。记录摇卦结果。",
|
||||
"autoScreenTitle": "自动起卦",
|
||||
"autoSelectTime": "选择起卦时间",
|
||||
"autoCoinDivination": "铜钱摇卦",
|
||||
@@ -364,6 +372,34 @@
|
||||
"confirm": "确认",
|
||||
"cancel": "取消",
|
||||
"autoSelectTime": "选择时间",
|
||||
"coinFaceGuideTitle": "字花图片说明",
|
||||
"coinFaceGuideDescription": "字=铜钱有字的一面\n花=铜钱有花纹的一面"
|
||||
"coinFaceGuideTitle": "字花对照说明",
|
||||
"coinFaceGuideDescription": "左侧为花面,右侧为字面。",
|
||||
"settingsInviteTitle": "我的邀请",
|
||||
"settingsInviteSubtitle": "邀请好友,共同获得奖励",
|
||||
"settingsInviteMyCode": "我的邀请码",
|
||||
"settingsInviteCopySuccess": "邀请码已复制",
|
||||
"settingsInviteCopied": "已复制",
|
||||
"settingsInviteCopy": "复制",
|
||||
"settingsInviteStats": "已邀请:{count} 位好友",
|
||||
"@settingsInviteStats": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"settingsInviteBindCode": "绑定邀请码",
|
||||
"settingsInviteBindHint": "输入好友的邀请码",
|
||||
"settingsInviteBindPlaceholder": "6位邀请码",
|
||||
"settingsInviteBindButton": "绑定",
|
||||
"settingsInviteBindSuccess": "邀请码绑定成功",
|
||||
"settingsInviteBindFailed": "邀请码绑定失败",
|
||||
"settingsInviteGenerateTitle": "生成我的邀请码",
|
||||
"settingsInviteGenerateButton": "生成我的邀请码",
|
||||
"settingsInviteGenerateSuccess": "邀请码生成成功",
|
||||
"settingsInviteEmptyTitle": "邀请好友,获得奖励",
|
||||
"settingsInviteEmptyDescription": "每成功邀请一位好友注册,您将获得积分奖励",
|
||||
"settingsInviteInputLabel": "输入邀请码(选填)",
|
||||
"settingsInviteInputHint": "输入邀请码绑定您的邀请人",
|
||||
"settingsInviteInvalidCode": "请输入有效的6位邀请码"
|
||||
}
|
||||
|
||||
@@ -5,15 +5,24 @@ import '../../theme/app_color_palette.dart';
|
||||
import '../../theme/design_tokens.dart';
|
||||
|
||||
class DivinationGuideImage extends StatelessWidget {
|
||||
const DivinationGuideImage({super.key, required this.path});
|
||||
const DivinationGuideImage({super.key, required this.paths});
|
||||
|
||||
final String path;
|
||||
final List<String> paths;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg),
|
||||
child: Image.asset(path, fit: BoxFit.contain),
|
||||
child: paths.length == 1
|
||||
? Image.asset(paths.first, fit: BoxFit.contain)
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(child: Image.asset(paths[0], fit: BoxFit.contain)),
|
||||
const SizedBox(width: AppSpacing.md),
|
||||
Expanded(child: Image.asset(paths[1], fit: BoxFit.contain)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -67,17 +76,31 @@ class DivinationInstructionCard extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class DivinationGuideDialog extends StatelessWidget {
|
||||
class DivinationGuideDialog extends StatefulWidget {
|
||||
const DivinationGuideDialog({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.guideImages,
|
||||
required this.instructionText,
|
||||
required this.instructions,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final List<String> guideImages;
|
||||
final String instructionText;
|
||||
final List<List<String>> guideImages;
|
||||
final List<String> instructions;
|
||||
|
||||
@override
|
||||
State<DivinationGuideDialog> createState() => _DivinationGuideDialogState();
|
||||
}
|
||||
|
||||
class _DivinationGuideDialogState extends State<DivinationGuideDialog> {
|
||||
final PageController _pageController = PageController();
|
||||
int _currentPage = 0;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -90,7 +113,7 @@ class DivinationGuideDialog extends StatelessWidget {
|
||||
children: [
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
Text(
|
||||
title,
|
||||
widget.title,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
@@ -98,15 +121,25 @@ class DivinationGuideDialog extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Expanded(
|
||||
child: PageView(
|
||||
children: guideImages
|
||||
.map((path) => DivinationGuideImage(path: path))
|
||||
.toList(),
|
||||
child: PageView.builder(
|
||||
controller: _pageController,
|
||||
onPageChanged: (index) {
|
||||
setState(() {
|
||||
_currentPage = index;
|
||||
});
|
||||
},
|
||||
itemCount: widget.guideImages.length,
|
||||
itemBuilder: (context, index) {
|
||||
return DivinationGuideImage(paths: widget.guideImages[index]);
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||
child: Text(instructionText),
|
||||
child: Text(
|
||||
widget.instructions[_currentPage],
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
|
||||
@@ -12,6 +12,8 @@ abstract final class DivinationTerms {
|
||||
|
||||
static const yinYang = {true: '阳', false: '阴'};
|
||||
|
||||
static const ziHua = {true: '字', false: '花'};
|
||||
|
||||
static const wuXing = ['木', '火', '土', '金', '水'];
|
||||
|
||||
static const yuanZhi = '元';
|
||||
|
||||
@@ -80,6 +80,7 @@ flutter:
|
||||
assets:
|
||||
- assets/images/logo.png
|
||||
- assets/images/qigua/
|
||||
- assets/images/tutorial/
|
||||
- assets/legal/en/
|
||||
- assets/legal/zh/
|
||||
|
||||
|
||||