206 lines
6.7 KiB
Dart
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),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|