Files
OnBudget/lib/src/features/accounts/presentation/screens/accounts_screen.dart
T
2026-05-28 17:27:06 +03:00

206 lines
6.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../../../app/l10n/l10n.dart';
import '../../../../app/router/app_routes.dart';
import '../../../../app/theme/app_colors.dart';
import '../../../home/presentation/widgets/money_text.dart';
import '../../../user/application/active_user_controller.dart';
import '../../application/accounts_controller.dart';
import '../../domain/entities/account.dart';
import '../widgets/account_icon.dart';
class AccountsScreen extends ConsumerWidget {
const AccountsScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final p = context.palette;
final activeUser = ref.watch(activeUserControllerProvider);
return Scaffold(
backgroundColor: p.paper,
body: SafeArea(
child: activeUser.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (e, _) => Center(child: Text('$e')),
data: (user) {
if (user == null) return const SizedBox.shrink();
return _AccountsList(userId: user.id);
},
),
),
floatingActionButton: FloatingActionButton(
backgroundColor: p.accent,
foregroundColor: p.paper,
onPressed: () => context.push(AppRoutes.accountNew),
child: const Icon(Icons.add),
),
);
}
}
class _AccountsList extends ConsumerWidget {
const _AccountsList({required this.userId});
final String userId;
@override
Widget build(BuildContext context, WidgetRef ref) {
final p = context.palette;
final l10n = context.l10n;
final accountsAsync = ref.watch(accountsStreamProvider(userId));
final defaultAsync = ref.watch(defaultAccountProvider(userId));
final defaultId = defaultAsync.value?.id;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16, 14, 16, 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
l10n.accountsSubtitle.toUpperCase(),
style: TextStyle(
fontSize: 11,
color: p.ink2,
letterSpacing: 0.6,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 2),
Text(
l10n.navAccounts,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: p.ink,
),
),
],
),
),
Expanded(
child: accountsAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (e, _) => Center(child: Text('$e')),
data: (accounts) {
if (accounts.isEmpty) {
return Center(
child: Text(
l10n.accountsEmpty,
style: TextStyle(fontSize: 14, color: p.ink2),
textAlign: TextAlign.center,
),
);
}
return ListView.builder(
padding: const EdgeInsets.fromLTRB(12, 4, 12, 80),
itemCount: accounts.length,
itemBuilder: (context, i) => _AccountTile(
account: accounts[i],
isDefault: accounts[i].id == defaultId,
onTap: () => context.push(
AppRoutes.accountEdit(accounts[i].id),
),
),
);
},
),
),
],
);
}
}
class _AccountTile extends ConsumerWidget {
const _AccountTile({
required this.account,
required this.isDefault,
required this.onTap,
});
final Account account;
final bool isDefault;
final VoidCallback onTap;
@override
Widget build(BuildContext context, WidgetRef ref) {
final p = context.palette;
final balance = ref.watch(accountBalanceProvider(account.id)).value ?? 0;
final color = Color(account.colorValue ?? 0xFFB8B5AC);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 4),
child: Material(
color: p.paper2,
borderRadius: BorderRadius.circular(14),
child: InkWell(
borderRadius: BorderRadius.circular(14),
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
child: Row(
children: [
Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: color.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(12),
),
child: Icon(iconForAccount(account), size: 22, color: color),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Flexible(
child: Text(
account.name,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: p.ink,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
if (isDefault) ...[
const SizedBox(width: 6),
Icon(Icons.star_rounded,
size: 14, color: p.accent),
],
],
),
const SizedBox(height: 2),
Text(
shortAccountLabel(account),
style: TextStyle(fontSize: 12, color: p.ink2),
),
],
),
),
MoneyText(
balance,
color: p.ink,
fontSize: 14,
fontWeight: FontWeight.w600,
),
const SizedBox(width: 4),
Icon(Icons.chevron_right, size: 16, color: p.ink2),
],
),
),
),
),
);
}
}