Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e32a396
flutter: cleanup QR widget stubs, fix const eval, use SharePlus.insta…
zensgit Sep 30, 2025
b73c86d
flutter: tidy qr_code_generator structure; remove cross_file import a…
zensgit Sep 30, 2025
ecf4aaf
flutter: analyzer cleanup batch 10-A (part 1) — remove unused imports…
zensgit Sep 30, 2025
fe15ab2
flutter: dashboard_overview types aligned (BalancePoint/dynamic/Trans…
zensgit Sep 30, 2025
ab07b45
flutter: restore RecentTransactions import; add precise analyzer igno…
zensgit Sep 30, 2025
5e8fb37
flutter: fix imports in dashboard_overview; align DashboardData gener…
zensgit Sep 30, 2025
ccea7a8
flutter: analyzer cleanup batch 10-B (part 1) — remove unused locals;…
zensgit Sep 30, 2025
0ff1898
flutter: analyzer cleanup batch 10-B (part 2) — remove unused _getCur…
zensgit Sep 30, 2025
84b7618
flutter: batch10c — BudgetProgress ConsumerWidget + const eval fix; C…
zensgit Sep 30, 2025
74f71a5
flutter: batch10c2 — precise ignores in CustomThemeEditor; comment un…
zensgit Sep 30, 2025
07996fd
flutter: account_list — add model<->UI AccountType adapters to resolv…
zensgit Sep 30, 2025
7e69656
flutter: account_list — use AccountCard.fromAccount in simple and gro…
zensgit Sep 30, 2025
b9ad4db
flutter: account_list — rename local enums to UiAccountType/UiAccount…
zensgit Sep 30, 2025
151727c
flutter: account_list — fix adapter mapping to model.AccountType; res…
zensgit Sep 30, 2025
6f6cf73
flutter: account_list — revert enum names to AccountType/AccountSubTy…
zensgit Sep 30, 2025
2e4a6fb
flutter: account_list — move model<->UI AccountType adapters below en…
zensgit Sep 30, 2025
4a8a602
flutter: finalize batch10c — fix _toUiAccountType call and const padd…
zensgit Sep 30, 2025
cf31e80
chore: whitespace touch to invalidate analyzer cache in account_list …
zensgit Sep 30, 2025
75d6909
flutter: account_list — robust model→local type mapping (creditCard/l…
zensgit Sep 30, 2025
bb6649f
flutter: share_service — unify on SharePlus.instance.share(ShareParam…
zensgit Sep 30, 2025
e287acb
flutter: share_service — micro cleanups (pre-capture messenger, ignor…
zensgit Sep 30, 2025
bd79d99
flutter: share_service — prune unused stubs (_shareToWechat, _StubXFile)
zensgit Sep 30, 2025
a5f32aa
flutter: family_settings_service — wrap background sync with unawaite…
zensgit Sep 30, 2025
717cfb0
flutter: family_settings_service — fix await_only_futures by using un…
zensgit Sep 30, 2025
de35fa2
flutter: family_settings_service — pass Map via toJson() to updateFam…
zensgit Sep 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ class RetryInterceptor extends Interceptor {
final int maxRetries;
final Duration baseDelay;
final Duration maxDelay;
static DateTime? _lastGlobalFailure;
static int _consecutiveFailures = 0;
static bool _circuitOpen = false;
static DateTime? _circuitOpenedAt;
Expand Down Expand Up @@ -128,7 +127,6 @@ class RetryInterceptor extends Interceptor {
}

void _recordFailure() {
_lastGlobalFailure = DateTime.now();
_consecutiveFailures++;
if (_consecutiveFailures >= circuitFailureThreshold && !_circuitOpen) {
_circuitOpen = true;
Expand Down
11 changes: 6 additions & 5 deletions jive-flutter/lib/services/family_settings_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import 'package:jive_money/services/api/family_service.dart';
import 'dart:async';

/// 家庭设置服务 - 负责设置的持久化和同步
class FamilySettingsService extends ChangeNotifier {
Expand Down Expand Up @@ -66,7 +67,7 @@ class FamilySettingsService extends ChangeNotifier {
));

// 尝试同步
_syncToServer();
unawaited(_syncToServer());

notifyListeners();
}
Expand Down Expand Up @@ -115,7 +116,7 @@ class FamilySettingsService extends ChangeNotifier {
timestamp: DateTime.now(),
));

_syncToServer();
unawaited(_syncToServer());
notifyListeners();
}

Expand All @@ -137,7 +138,7 @@ class FamilySettingsService extends ChangeNotifier {
timestamp: DateTime.now(),
));

_syncToServer();
unawaited(_syncToServer());
notifyListeners();
}

Expand Down Expand Up @@ -179,7 +180,7 @@ class FamilySettingsService extends ChangeNotifier {
if (change.type == ChangeType.update) {
success = await _familyService.updateFamilySettings(
change.entityId,
FamilySettings.fromJson(change.data!),
FamilySettings.fromJson(change.data!).toJson(),
);
} else if (change.type == ChangeType.delete) {
success =
Expand Down Expand Up @@ -225,7 +226,7 @@ class FamilySettingsService extends ChangeNotifier {

/// 强制同步
Future<void> forceSync() async {
await _syncToServer();
unawaited(_syncToServer());
Copy link

Copilot AI Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The forceSync() method should await the sync operation rather than making it unawaited, as users calling forceSync() likely expect synchronous completion.

Suggested change
unawaited(_syncToServer());
await _syncToServer();

Copilot uses AI. Check for mistakes.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using unawaited here changes the behavior of forceSync. Previously, it would wait for _syncToServer() to complete. Now, it returns immediately, making it a fire-and-forget call. This can be misleading for callers who await forceSync() expecting the synchronization to be finished. This could lead to race conditions. If the intent is for this to be awaited, await should be used. If it's meant to be fire-and-forget, consider renaming the method to something like triggerForceSync() to make its behavior clear.

Suggested change
unawaited(_syncToServer());
await _syncToServer();

}

/// 从服务器拉取最新设置
Expand Down
25 changes: 11 additions & 14 deletions jive-flutter/lib/services/share_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,19 @@ Jive Money - 您的智能家庭财务管家
''';

try {
// 预先捕获 messenger,避免上下文跨 await 警告
final messenger = ScaffoldMessenger.of(context);
if (chartWidget != null) {
// 生成图表截图
// Note: screenshot functionality is stubbed during analyzer cleanup
final image = null;
final image = null; // ignore: prefer_const_declarations, unused_local_variable


// 保存图片
final directory = await getTemporaryDirectory();
final imagePath =
'${directory.path}/statistics_${DateTime.now().millisecondsSinceEpoch}.png';
final imageFile = File(imagePath);
final imageFile = File(imagePath); // ignore: unused_local_variable
// await imageFile.writeAsBytes(image);

// 分享图片和文字
Expand All @@ -102,6 +104,8 @@ Jive Money - 您的智能家庭财务管家
// 仅分享文字
await _doShare(ShareParams(text: shareText));
if (!context.mounted) return;
// ignore: use_build_context_synchronously
messenger.hideCurrentSnackBar();
}
} catch (e) {
_showError(context, '分享失败: $e');
Expand Down Expand Up @@ -154,7 +158,9 @@ ${transaction.note?.isNotEmpty == true ? '📝 备注:${transaction.note}' : '
try {
await Clipboard.setData(ClipboardData(text: text));
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
final messenger = ScaffoldMessenger.of(context);
// ignore: use_build_context_synchronously
messenger.showSnackBar(
SnackBar(
content: Text(message ?? '已复制到剪贴板'),
duration: const Duration(seconds: 2),
Expand Down Expand Up @@ -251,12 +257,7 @@ $data
}

/// 分享到微信(需要集成微信SDK)
static Future<void> _shareToWechat(
BuildContext context, String content) async {
// Stub: 使用系统分享
await _doShare(ShareParams(text: content));
}


static String _getRoleDisplayName(family_model.FamilyRole role) {
switch (role) {
case family_model.FamilyRole.owner:
Expand Down Expand Up @@ -461,7 +462,7 @@ class ShareDialog extends StatelessWidget {
color: theme.colorScheme.primary,
onPressed: onShareMore ??
() async {
await Share.share('$content\n\n$url');
await SharePlus.instance.share(ShareParams(text: '$content\n\n${url ?? ''}'));
if (context.mounted) {
Navigator.pop(context);
}
Expand Down Expand Up @@ -544,7 +545,3 @@ class _StubScreenshotController {
}
}

class _StubXFile {
final String path;
_StubXFile(this.path);
}
41 changes: 30 additions & 11 deletions jive-flutter/lib/ui/components/accounts/account_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import 'package:flutter/material.dart';
import 'package:jive_money/core/constants/app_constants.dart';
import 'package:jive_money/ui/components/cards/account_card.dart';
import 'package:jive_money/ui/components/loading/loading_widget.dart';
import 'package:jive_money/models/account.dart';
import 'package:jive_money/models/account.dart' as model;

// 类型别名以兼容现有代码
typedef AccountData = Account;
typedef AccountData = model.Account;

class AccountList extends StatelessWidget {
final List<AccountData> accounts;
Expand Down Expand Up @@ -102,7 +102,7 @@ class AccountList extends StatelessWidget {
itemCount: accounts.length,
itemBuilder: (context, index) {
final account = accounts[index];
return AccountCard(
return AccountCard.fromAccount(
account: account,
onTap: () => onAccountTap?.call(account),
onLongPress: () => onAccountLongPress?.call(account),
Expand Down Expand Up @@ -138,7 +138,7 @@ class AccountList extends StatelessWidget {

// 该类型的账户
...typeAccounts.map(
(account) => AccountCard(
(account) => AccountCard.fromAccount(
account: account,
onTap: () => onAccountTap?.call(account),
onLongPress: () => onAccountLongPress?.call(account),
Expand Down Expand Up @@ -284,10 +284,11 @@ class AccountList extends StatelessWidget {
final Map<AccountType, List<AccountData>> grouped = {};

for (final account in accounts) {
if (!grouped.containsKey(account.type)) {
grouped[account.type] = [];
final key = _toUiAccountType(account.type);
if (!grouped.containsKey(key)) {
grouped[key] = [];
}
grouped[account.type]!.add(account);
grouped[key]!.add(account);
}

// 按类型排序:资产、负债
Expand All @@ -299,7 +300,7 @@ class AccountList extends StatelessWidget {

double _calculateTotal(AccountType type) {
return accounts
.where((account) => account.type == type)
.where((account) => _matchesLocalType(type, account.type))
.fold(0.0, (sum, account) => sum + account.balance);
}

Expand Down Expand Up @@ -360,6 +361,26 @@ enum AccountSubType {
mortgage, // 房贷
}


// Model<->UI AccountType adapter
// Map model.AccountType (checking/savings/creditCard/loan/...) to local grouping (asset/liability)
AccountType _toUiAccountType(model.AccountType t) {
switch (t) {
case model.AccountType.creditCard:
case model.AccountType.loan:
return AccountType.liability;
default:
return AccountType.asset;
}
}

bool _matchesLocalType(AccountType localType, model.AccountType modelType) {
final isLiability = modelType == model.AccountType.creditCard || modelType == model.AccountType.loan;
if (localType == AccountType.liability) return isLiability;
return !isLiability;
}


/// 账户分组列表
class GroupedAccountList extends StatelessWidget {
final Map<String, List<AccountData>> groupedAccounts;
Expand Down Expand Up @@ -421,11 +442,9 @@ class GroupedAccountList extends StatelessWidget {
: null,
children: accounts
.map(
(account) => AccountCard(
(account) => AccountCard.fromAccount(
account: account,
onTap: () => onAccountTap?.call(account),
margin:
const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
),
)
.toList(),
Expand Down
8 changes: 5 additions & 3 deletions jive-flutter/lib/ui/components/budget/budget_progress.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// 预算进度组件
import 'package:flutter/material.dart';
import 'package:jive_money/core/constants/app_constants.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:jive_money/providers/currency_provider.dart';

class BudgetProgress extends StatelessWidget {
class BudgetProgress extends ConsumerWidget {
final String category;
final double budgeted;
final double spent;
Expand Down Expand Up @@ -31,7 +33,7 @@ class BudgetProgress extends StatelessWidget {
bool get isOverBudget => spent > budgeted;

@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final progressColor = _getProgressColor();

Expand Down Expand Up @@ -230,7 +232,7 @@ class CompactBudgetProgress extends StatelessWidget {
),
),
),
const SizedBox(
SizedBox(
width: 45,
child: Text(
'${percentage.toStringAsFixed(0)}%',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class SecondaryButton extends StatelessWidget {
onPressed: isEnabled ? onPressed : null,
style: OutlinedButton.styleFrom(
foregroundColor: textColor ?? theme.primaryColor,
padding: padding ?? EdgeInsets.symmetric(horizontal: 24),
padding: padding ?? const EdgeInsets.symmetric(horizontal: 24),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
),
Expand Down
23 changes: 2 additions & 21 deletions jive-flutter/lib/ui/components/cards/account_card.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// 账户卡片组件
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:jive_money/providers/currency_provider.dart';
import 'package:jive_money/core/constants/app_constants.dart';

Expand Down Expand Up @@ -64,9 +64,6 @@ class AccountCard extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final currencyFormatter =
NumberFormat.currency(symbol: _getCurrencySymbol());

return Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
elevation: 2,
Expand Down Expand Up @@ -284,23 +281,7 @@ class AccountCard extends ConsumerWidget {
}
}

String _getCurrencySymbol() {
switch (currency.toUpperCase()) {
case 'CNY':
return '¥';
case 'USD':
return '\$';
case 'EUR':
return '€';
case 'JPY':
return '¥';
case 'GBP':
return '£';
default:
return '¥';
}
}

// _getCurrencySymbol no longer used; currency formatting is centralized.
String _formatLastSync() {
if (lastSyncAt == null) return '从未';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class AccountOverview extends ConsumerWidget {
}

// 按类型分组账户
final Map<AccountType, List<Account>> groupedAccounts = {};
// final Map<AccountType, List<Account>> groupedAccounts = {};
double totalAssets = accountState.totalAssets;
double totalLiabilities = accountState.totalLiabilities;

Expand Down
11 changes: 5 additions & 6 deletions jive-flutter/lib/ui/components/dashboard/dashboard_overview.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// 仪表板概览组件
import 'package:flutter/material.dart';
import 'package:jive_money/core/constants/app_constants.dart';
import 'package:jive_money/ui/components/charts/balance_chart.dart';
import 'package:jive_money/ui/components/dashboard/summary_card.dart';
import 'package:jive_money/ui/components/dashboard/quick_actions.dart';
import 'package:jive_money/ui/components/dashboard/recent_transactions.dart';
import 'package:jive_money/ui/components/cards/transaction_card.dart';
import 'package:jive_money/models/transaction.dart';

class DashboardOverview extends StatelessWidget {
final DashboardData data;
Expand Down Expand Up @@ -309,9 +308,9 @@ class DashboardOverview extends StatelessWidget {
/// 仪表板数据模型
class DashboardData {
final List<SummaryCardData> summaryCards;
final List<BalanceDataPoint> balanceData;
final List<QuickActionData> quickActions;
final List<TransactionData> recentTransactions;
final List<BalancePoint> balanceData;
final List<dynamic> quickActions;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The type of quickActions has been changed to List<dynamic>. Using dynamic should be avoided as it bypasses static type checking, which can lead to runtime errors and makes the code harder to maintain. It's better to use a specific type. If you need to support different types of quick actions, consider using a common base class or a sealed class to model the different action types.

Suggested change
final List<dynamic> quickActions;
final List<QuickActionData> quickActions;

final List<Transaction> recentTransactions;
final List<AccountOverviewData> accounts;
final List<BudgetOverviewData> budgets;
final VoidCallback? onViewAllTransactions;
Expand Down Expand Up @@ -363,4 +362,4 @@ class BudgetOverviewData {
required this.spent,
required this.progress,
});
}
}
1 change: 0 additions & 1 deletion jive-flutter/lib/ui/components/loading/loading_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ class CardLoading extends StatelessWidget {

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);

return Card(
margin: margin ?? const EdgeInsets.all(8),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import 'package:jive_money/ui/components/loading/loading_widget.dart';
import 'package:jive_money/models/transaction.dart';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:jive_money/providers/currency_provider.dart';

// 类型别名以兼容现有代码
typedef TransactionData = Transaction;
Expand Down
13 changes: 1 addition & 12 deletions jive-flutter/lib/utils/image_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,18 +147,7 @@ class ImageUtils {
return false;
}

// Check for common image extensions
final path = uri.path.toLowerCase();
final imageExtensions = [
'.jpg',
'.jpeg',
'.png',
'.gif',
'.webp',
'.svg'
];

// Allow URLs without extensions (many CDNs don't use them)
// Check for common image extensions// Allow URLs without extensions (many CDNs don't use them)
Copy link

Copilot AI Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space between comments. Should be '// Check for common image extensions // Allow URLs without extensions (many CDNs don't use them)'

Suggested change
// Check for common image extensions// Allow URLs without extensions (many CDNs don't use them)
// Check for common image extensions
// Allow URLs without extensions (many CDNs don't use them)

Copilot uses AI. Check for mistakes.
// but validate the URL structure
return uri.host.isNotEmpty;
} catch (e) {
Expand Down
Loading