@marcos_feitoza/personal-finance-frontend-core-ui 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ # [1.2.0](https://github.com/MarcosOps/personal-finance-frontend-core-ui/compare/v1.1.0...v1.2.0) (2026-01-29)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * new SnackBar ([7c519e0](https://github.com/MarcosOps/personal-finance-frontend-core-ui/commit/7c519e06b53884863a32170015080860ddf836be))
7
+ * update SnackBar ([b0c3b71](https://github.com/MarcosOps/personal-finance-frontend-core-ui/commit/b0c3b71efa0a7dd855fed3561c0c40bca33b23ec))
8
+
9
+
10
+ ### Features
11
+
12
+ * new AppTheme process ([7557114](https://github.com/MarcosOps/personal-finance-frontend-core-ui/commit/755711424e37a65f6891748c9d60940e1bbf2e80))
13
+
14
+ # [1.1.0](https://github.com/MarcosOps/personal-finance-frontend-core-ui/compare/v1.0.2...v1.1.0) (2025-11-28)
15
+
16
+
17
+ ### Features
18
+
19
+ * add confirmation dialog for deletion operations ([029b159](https://github.com/MarcosOps/personal-finance-frontend-core-ui/commit/029b159d370e2c68f71afeefb320dea7182b58bb))
20
+ * update readme.me ([7b1ac44](https://github.com/MarcosOps/personal-finance-frontend-core-ui/commit/7b1ac445a2b3183283779f710e46a0fed7238657))
21
+
1
22
  ## [1.0.2](https://github.com/MarcosOps/personal-finance-frontend-core-ui/compare/v1.0.1...v1.0.2) (2025-11-26)
2
23
 
3
24
 
package/README.md CHANGED
@@ -1,16 +1,94 @@
1
- # personal_finance_frontend_core_ui
1
+ # Core UI - Frontend (Flutter)
2
2
 
3
- A new Flutter project.
3
+ Este pacote contém os widgets e componentes de interface do usuário (UI) reutilizáveis para a aplicação Personal Finance Frontend.
4
4
 
5
- ## Getting Started
5
+ ## Propósito
6
6
 
7
- This project is a starting point for a Flutter application.
7
+ O objetivo deste pacote é centralizar e padronizar a aparência e o comportamento dos elementos de UI em todo o aplicativo. Ele fornece uma biblioteca de componentes que podem ser utilizados por outros pacotes de feature, garantindo consistência visual e reduzindo a duplicação de código.
8
8
 
9
- A few resources to get you started if this is your first Flutter project:
9
+ ## Conteúdo Principal
10
10
 
11
- - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
12
- - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
11
+ - **`widgets/`**: Contém diversos widgets reutilizáveis, como `AppFormCard`, `AppDropdown`, `TradeForm`, `DividendLogForm`, `CryptoTradeForm`, `RrspContributionForm`, etc.
12
+ - **`utils/app_dialogs.dart`**: Funções utilitárias para exibir diálogos padronizados (ex: confirmação de exclusão).
13
+ - **`utils/app_logger.dart`**: Implementação de um logger condicional que exibe logs apenas em modo de depuração.
14
+ - **`utils/currency_input_formatter.dart`**: Formatter para campos de entrada de moeda.
15
+ - **`utils/theme_notifier.dart`**: Provedor para gerenciar o tema da aplicação (claro/escuro).
13
16
 
14
- For help getting started with Flutter development, view the
15
- [online documentation](https://docs.flutter.dev/), which offers tutorials,
16
- samples, guidance on mobile development, and a full API reference.
17
+ ## Tema (Fase 1 centralização)
18
+
19
+ Para evitar que cada tela/feature package defina seus próprios estilos (cores, espaçamentos, bordas), o **tema global** foi centralizado no core-ui.
20
+
21
+ Arquivos:
22
+
23
+ - `lib/theme/app_theme.dart`
24
+ - Define `AppTheme.light` e `AppTheme.dark`.
25
+ - **O que controla:**
26
+ - `ColorScheme` (paleta de cores principal)
27
+ - `AppBarTheme` (cor do topo e cor do texto/ícones)
28
+ - `ElevatedButtonTheme` / `OutlinedButtonTheme` (tamanho e bordas padrão dos botões)
29
+ - `InputDecorationTheme` (bordas/padding padrão de `TextFormField`)
30
+ - `lib/theme/app_colors.dart`
31
+ - Tokens semânticos (ex.: `success`, `error`, `warning`) e `seed`.
32
+ - **O que controla:**
33
+ - cores usadas em feedback (SnackBars, highlights de sucesso/erro)
34
+ - seed do tema (mudança global do “tom” do app)
35
+ - `lib/theme/app_spacing.dart`
36
+ - Tokens de espaçamento (`xs/sm/md/lg/xl/...`).
37
+ - **O que controla:**
38
+ - valores de padding/margin padronizados
39
+
40
+ Uso no app shell (`personal-finance-frontend/lib/main.dart`):
41
+
42
+ ```dart
43
+ import 'package:personal_finance_frontend_core_ui/theme/app_theme.dart';
44
+
45
+ MaterialApp(
46
+ theme: AppTheme.light,
47
+ darkTheme: AppTheme.dark,
48
+ )
49
+ ```
50
+
51
+ Regras recomendadas:
52
+
53
+ - Em telas/features: preferir `Theme.of(context).colorScheme`.
54
+ - Para cores semânticas (ex.: erro/sucesso), usar `AppColors`.
55
+ - Para componentes, preferir `widgets/app_widgets.dart`.
56
+
57
+ ## SnackBars padronizados (Fase 2)
58
+
59
+ Para evitar SnackBars com cores/textos inconsistentes, use os helpers:
60
+
61
+ - `showErrorSnackBar(context, message)`
62
+ - `showSuccessSnackBar(context, message)`
63
+ - `showInfoSnackBar(context, message)`
64
+
65
+ Arquivo:
66
+ - `lib/utils/app_snackbars.dart`
67
+
68
+ Exemplo:
69
+
70
+ ```dart
71
+ import 'package:personal_finance_frontend_core_ui/utils/app_snackbars.dart';
72
+
73
+ showErrorSnackBar(context, 'Something went wrong');
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Como Usar (Instalação como Dependência)
79
+
80
+ Este pacote é uma dependência local para a aplicação principal (`personal-finance-frontend`) e outras features packages do frontend.
81
+
82
+ No `pubspec.yaml` do pacote consumidor, adicione a seguinte linha em `dependencies`:
83
+
84
+ ```yaml
85
+ personal_finance_frontend_core_ui:
86
+ path: ../personal-finance-frontend-core-ui
87
+ ```
88
+
89
+ ## Features
90
+
91
+ - **Widgets Reutilizáveis**: Componentes de UI com design e comportamento padronizados.
92
+ - **Diálogos Centralizados**: Funções para exibir diálogos consistentes em todo o aplicativo.
93
+ - **Logging Condicional**: Um logger que ajuda a manter o console limpo em produção, exibindo mensagens apenas em desenvolvimento.
94
+ - **Ferramentas de Formatação**: Utilitários para formatar entradas de usuário (ex: valores monetários).
@@ -1 +1,5 @@
1
- export 'widgets/app_widgets.dart';
1
+ export 'widgets/app_widgets.dart';
2
+ export 'theme/app_theme.dart';
3
+ export 'theme/app_colors.dart';
4
+ export 'theme/app_spacing.dart';
5
+ export 'utils/app_snackbars.dart';
@@ -0,0 +1,37 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ /// App-wide color tokens.
4
+ ///
5
+ /// Why this file exists:
6
+ /// - In a modular Flutter app (multiple packages/features), colors tend to be
7
+ /// duplicated across screens and drift over time.
8
+ /// - By keeping "semantic" colors here (success/error/warning + seed), we can
9
+ /// change the look globally without hunting through many screens.
10
+ ///
11
+ /// How to use:
12
+ /// - Prefer `Theme.of(context).colorScheme.*` for most UI.
13
+ /// - Use these tokens when the color is semantic (success/error), not arbitrary.
14
+ class AppColors {
15
+ AppColors._();
16
+
17
+ /// Seed used to generate the Material `ColorScheme`.
18
+ ///
19
+ /// Changing this will change the app's primary/secondary tones.
20
+ static const Color seed = Colors.blueGrey;
21
+
22
+ /// Background colors (used as overrides in ThemeData).
23
+ ///
24
+ /// Note: in the current setup, the light theme relies on Material defaults
25
+ /// (we don't override scaffoldBackgroundColor). If you want to set a custom
26
+ /// background, add a token here and wire it in `AppTheme.light`.
27
+ static const Color darkScaffoldBackground = Color(0xFF212121); // ~ Colors.grey[900]
28
+ static const Color darkCardBackground = Color(0xFF303030); // ~ Colors.grey[850]
29
+ static const Color darkTextFieldFill = Color(0xFF424242); // ~ Colors.grey[800]
30
+
31
+ /// Semantic colors.
32
+ ///
33
+ /// These should be used for meaning (success/error) instead of random `Colors.*`.
34
+ static const Color success = Colors.green;
35
+ static const Color error = Colors.red;
36
+ static const Color warning = Colors.orange;
37
+ }
@@ -0,0 +1,34 @@
1
+ /// App-wide spacing tokens.
2
+ ///
3
+ /// Why this file exists:
4
+ /// - Prevents each screen from inventing its own paddings/margins.
5
+ /// - Makes global layout adjustments easy.
6
+ ///
7
+ /// Usage:
8
+ /// ```dart
9
+ /// Padding(
10
+ /// padding: const EdgeInsets.all(AppSpacing.lg),
11
+ /// child: ...,
12
+ /// )
13
+ /// ```
14
+ class AppSpacing {
15
+ AppSpacing._();
16
+
17
+ /// 4px base spacing.
18
+ static const double xs = 4;
19
+
20
+ /// 8px spacing.
21
+ static const double sm = 8;
22
+
23
+ /// 12px spacing.
24
+ static const double md = 12;
25
+
26
+ /// 16px spacing.
27
+ static const double lg = 16;
28
+
29
+ /// 20px spacing.
30
+ static const double xl = 20;
31
+
32
+ /// 24px spacing.
33
+ static const double xxl = 24;
34
+ }
@@ -0,0 +1,112 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ import 'app_colors.dart';
4
+
5
+ /// Centralized ThemeData for the whole app.
6
+ ///
7
+ /// Why this file exists:
8
+ /// - The app is split into multiple Flutter packages (shell + feature packages).
9
+ /// - If each screen defines its own colors/fonts, UI drift happens.
10
+ /// - By centralizing ThemeData here, we keep a single source of truth.
11
+ ///
12
+ /// How to use:
13
+ /// - In the app shell (`personal-finance-frontend/lib/main.dart`), set:
14
+ /// - `theme: AppTheme.light`
15
+ /// - `darkTheme: AppTheme.dark`
16
+ /// - Inside widgets, prefer `Theme.of(context)` and `ColorScheme`.
17
+ class AppTheme {
18
+ AppTheme._();
19
+
20
+ /// Light theme.
21
+ ///
22
+ /// Key choices:
23
+ /// - `ColorScheme.fromSeed`: creates a consistent palette based on a single seed.
24
+ /// - `useMaterial3: true`: aligns the app to modern Material components.
25
+ static final ThemeData light = ThemeData(
26
+ colorScheme: ColorScheme.fromSeed(seedColor: AppColors.seed),
27
+ useMaterial3: true,
28
+
29
+ // AppBar (top bar)
30
+ // - backgroundColor: the bar background
31
+ // - foregroundColor: icon/text color on top of the bar
32
+ appBarTheme: const AppBarTheme(
33
+ centerTitle: true,
34
+ backgroundColor: AppColors.seed,
35
+ foregroundColor: Colors.white,
36
+ ),
37
+
38
+ // Default style for elevated buttons.
39
+ // Affects buttons created without custom style.
40
+ elevatedButtonTheme: ElevatedButtonThemeData(
41
+ style: ElevatedButton.styleFrom(
42
+ minimumSize: const Size.fromHeight(50),
43
+ shape: RoundedRectangleBorder(
44
+ borderRadius: BorderRadius.circular(8),
45
+ ),
46
+ ),
47
+ ),
48
+
49
+ // Default style for outlined buttons.
50
+ outlinedButtonTheme: OutlinedButtonThemeData(
51
+ style: OutlinedButton.styleFrom(
52
+ minimumSize: const Size.fromHeight(50),
53
+ shape: RoundedRectangleBorder(
54
+ borderRadius: BorderRadius.circular(8),
55
+ ),
56
+ ),
57
+ ),
58
+
59
+ // Default decoration for text fields.
60
+ // Controls border and padding used by TextFormField/InputDecoration.
61
+ inputDecorationTheme: const InputDecorationTheme(
62
+ border: OutlineInputBorder(),
63
+ contentPadding: EdgeInsets.symmetric(vertical: 16.0, horizontal: 12.0),
64
+ ),
65
+ );
66
+
67
+ /// Dark theme.
68
+ ///
69
+ /// Differences from light theme:
70
+ /// - `brightness: Brightness.dark` for proper Material defaults
71
+ /// - custom scaffold/card/text field background colors
72
+ static final ThemeData dark = ThemeData(
73
+ colorScheme: ColorScheme.fromSeed(
74
+ seedColor: AppColors.seed,
75
+ brightness: Brightness.dark,
76
+ ),
77
+ useMaterial3: true,
78
+ appBarTheme: const AppBarTheme(
79
+ centerTitle: true,
80
+ backgroundColor: Colors.black,
81
+ foregroundColor: Colors.white,
82
+ ),
83
+ elevatedButtonTheme: ElevatedButtonThemeData(
84
+ style: ElevatedButton.styleFrom(
85
+ minimumSize: const Size.fromHeight(50),
86
+ shape: RoundedRectangleBorder(
87
+ borderRadius: BorderRadius.circular(8),
88
+ ),
89
+ backgroundColor: Colors.blueGrey,
90
+ foregroundColor: Colors.white,
91
+ ),
92
+ ),
93
+ outlinedButtonTheme: OutlinedButtonThemeData(
94
+ style: OutlinedButton.styleFrom(
95
+ minimumSize: const Size.fromHeight(50),
96
+ shape: RoundedRectangleBorder(
97
+ borderRadius: BorderRadius.circular(8),
98
+ ),
99
+ backgroundColor: Colors.blueGrey,
100
+ foregroundColor: Colors.white,
101
+ ),
102
+ ),
103
+ inputDecorationTheme: const InputDecorationTheme(
104
+ border: OutlineInputBorder(),
105
+ contentPadding: EdgeInsets.symmetric(vertical: 16.0, horizontal: 12.0),
106
+ fillColor: AppColors.darkTextFieldFill,
107
+ filled: true,
108
+ ),
109
+ scaffoldBackgroundColor: AppColors.darkScaffoldBackground,
110
+ cardColor: AppColors.darkCardBackground,
111
+ );
112
+ }
@@ -0,0 +1,30 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ class AppDialogs {
4
+ /// Displays a confirmation dialog for deletion operations.
5
+ ///
6
+ /// Returns `true` if the user confirms deletion, `false` otherwise.
7
+ static Future<bool?> showDeleteConfirmationDialog(
8
+ BuildContext context, String itemName) async {
9
+ return await showDialog<bool>(
10
+ context: context,
11
+ builder: (BuildContext context) {
12
+ return AlertDialog(
13
+ title: const Text('Confirmar Exclusão'),
14
+ content: Text(
15
+ 'Você tem certeza que deseja excluir "$itemName"? Esta ação não pode ser desfeita.'),
16
+ actions: <Widget>[
17
+ TextButton(
18
+ onPressed: () => Navigator.of(context).pop(false),
19
+ child: const Text('Cancelar'),
20
+ ),
21
+ TextButton(
22
+ onPressed: () => Navigator.of(context).pop(true),
23
+ child: const Text('Excluir'),
24
+ ),
25
+ ],
26
+ );
27
+ },
28
+ );
29
+ }
30
+ }
@@ -0,0 +1,49 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ import '../theme/app_colors.dart';
4
+
5
+ /// Standard SnackBars for the whole app.
6
+ ///
7
+ /// Why this exists:
8
+ /// - Many screens create SnackBars with hardcoded colors.
9
+ /// - Over time, feedback UX becomes inconsistent.
10
+ /// - Centralizing here makes it easy to change styling globally.
11
+ ///
12
+ /// Usage:
13
+ /// ```dart
14
+ /// showErrorSnackBar(context, 'Login failed');
15
+ /// showSuccessSnackBar(context, 'Saved!');
16
+ /// ```
17
+ void showErrorSnackBar(BuildContext context, String message) {
18
+ ScaffoldMessenger.of(context).showSnackBar(
19
+ SnackBar(
20
+ content: Text(message),
21
+ backgroundColor: AppColors.error,
22
+ ),
23
+ );
24
+ }
25
+
26
+ void showSuccessSnackBar(BuildContext context, String message) {
27
+ ScaffoldMessenger.of(context).showSnackBar(
28
+ SnackBar(
29
+ content: Text(message),
30
+ backgroundColor: AppColors.success,
31
+ ),
32
+ );
33
+ }
34
+
35
+ void showInfoSnackBar(BuildContext context, String message) {
36
+ // Info uses the current theme's surface for readability.
37
+ final theme = Theme.of(context);
38
+ ScaffoldMessenger.of(context).showSnackBar(
39
+ SnackBar(
40
+ // SnackBar doesn't support a `textStyle` parameter in some Flutter versions,
41
+ // so we style the Text directly.
42
+ content: Text(
43
+ message,
44
+ style: TextStyle(color: theme.colorScheme.onInverseSurface),
45
+ ),
46
+ backgroundColor: theme.colorScheme.inverseSurface,
47
+ ),
48
+ );
49
+ }
@@ -1,6 +1,7 @@
1
1
  import 'package:flutter/material.dart';
2
2
  import 'package:intl/intl.dart';
3
3
  import 'package:personal_finance_frontend_core_services/services/crypto_service.dart';
4
+ import 'package:personal_finance_frontend_core_ui/utils/app_snackbars.dart';
4
5
  import 'package:personal_finance_frontend_core_services/services/transaction_service.dart';
5
6
  import 'package:personal_finance_frontend_core_ui/utils/currency_input_formatter.dart';
6
7
  import 'package:personal_finance_frontend_core_ui/widgets/app_widgets.dart';
@@ -156,9 +157,7 @@ class _CryptoTradeFormState extends State<CryptoTradeForm> {
156
157
  final validationResult = await _cryptoService.validateCryptoSymbol(symbol);
157
158
  if (validationResult == null) {
158
159
  if (mounted) {
159
- ScaffoldMessenger.of(context).showSnackBar(
160
- SnackBar(content: Text('Invalid crypto ID: "$symbol"'), backgroundColor: Colors.red),
161
- );
160
+ showErrorSnackBar(context, 'Invalid crypto ID: "$symbol"');
162
161
  }
163
162
  setState(() => _isSubmitting = false);
164
163
  return;
@@ -176,9 +175,7 @@ class _CryptoTradeFormState extends State<CryptoTradeForm> {
176
175
  } else { // Sell
177
176
  if (_livePriceForSell == null) {
178
177
  if (mounted) {
179
- ScaffoldMessenger.of(context).showSnackBar(
180
- const SnackBar(content: Text('Could not get live price. Please try again.'), backgroundColor: Colors.red),
181
- );
178
+ showErrorSnackBar(context, 'Could not get live price. Please try again.');
182
179
  }
183
180
  setState(() => _isSubmitting = false);
184
181
  return;
@@ -201,13 +198,9 @@ class _CryptoTradeFormState extends State<CryptoTradeForm> {
201
198
 
202
199
  if (mounted) {
203
200
  if (errorMessage != null) {
204
- ScaffoldMessenger.of(context).showSnackBar(
205
- SnackBar(content: Text(errorMessage), backgroundColor: Colors.red),
206
- );
201
+ showErrorSnackBar(context, errorMessage);
207
202
  } else {
208
- ScaffoldMessenger.of(context).showSnackBar(
209
- SnackBar(content: Text('Trade created successfully!'), backgroundColor: Colors.green),
210
- );
203
+ showSuccessSnackBar(context, 'Trade created successfully!');
211
204
  widget.onTradeCreated(tradeData);
212
205
  _formKey.currentState!.reset();
213
206
  _symbolController.clear();
@@ -1,6 +1,7 @@
1
1
  import 'package:flutter/material.dart';
2
2
  import 'package:intl/intl.dart';
3
3
  import 'package:personal_finance_frontend_core_services/services/transaction_service.dart';
4
+ import 'package:personal_finance_frontend_core_ui/utils/app_snackbars.dart';
4
5
  import 'package:personal_finance_frontend_core_ui/utils/currency_input_formatter.dart';
5
6
  import 'package:personal_finance_frontend_core_ui/widgets/app_widgets.dart';
6
7
 
@@ -75,13 +76,9 @@ class _DividendLogFormState extends State<DividendLogForm> {
75
76
 
76
77
  if (mounted) {
77
78
  if (errorMessage != null) {
78
- ScaffoldMessenger.of(context).showSnackBar(
79
- SnackBar(content: Text(errorMessage), backgroundColor: Colors.red),
80
- );
79
+ showErrorSnackBar(context, errorMessage);
81
80
  } else {
82
- ScaffoldMessenger.of(context).showSnackBar(
83
- const SnackBar(content: Text('Dividend logged successfully!'), backgroundColor: Colors.green),
84
- );
81
+ showSuccessSnackBar(context, 'Dividend logged successfully!');
85
82
  widget.onDividendLogged();
86
83
  _formKey.currentState!.reset();
87
84
  _amountController.clear();
@@ -0,0 +1,36 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ /// A reusable widget to display historical data in a standardized DataTable.
4
+ class HistoricalDataTable extends StatelessWidget {
5
+ final List<DataColumn> columns;
6
+ final List<DataRow> rows;
7
+ final String noDataFoundMessage;
8
+
9
+ const HistoricalDataTable({
10
+ Key? key,
11
+ required this.columns,
12
+ required this.rows,
13
+ this.noDataFoundMessage = 'No data found.',
14
+ }) : super(key: key);
15
+
16
+ @override
17
+ Widget build(BuildContext context) {
18
+ if (rows.isEmpty) {
19
+ return Center(
20
+ child: Padding(
21
+ padding: const EdgeInsets.all(16.0),
22
+ child: Text(noDataFoundMessage),
23
+ ),
24
+ );
25
+ }
26
+
27
+ return SingleChildScrollView(
28
+ scrollDirection: Axis.horizontal,
29
+ child: DataTable(
30
+ columnSpacing: 24.0,
31
+ columns: columns,
32
+ rows: rows,
33
+ ),
34
+ );
35
+ }
36
+ }
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
2
2
  import 'package:flutter/services.dart';
3
3
  import 'package:intl/intl.dart';
4
4
  import 'package:personal_finance_frontend_core_services/services/transaction_service.dart';
5
+ import 'package:personal_finance_frontend_core_ui/utils/app_snackbars.dart';
5
6
  import 'package:personal_finance_frontend_core_ui/utils/currency_input_formatter.dart';
6
7
  import 'package:personal_finance_frontend_core_ui/widgets/app_widgets.dart';
7
8
 
@@ -77,13 +78,9 @@ class _RrspContributionFormState extends State<RrspContributionForm> {
77
78
 
78
79
  if (mounted) {
79
80
  if (errorMessage != null) {
80
- ScaffoldMessenger.of(context).showSnackBar(
81
- SnackBar(content: Text(errorMessage), backgroundColor: Colors.red),
82
- );
81
+ showErrorSnackBar(context, errorMessage);
83
82
  } else {
84
- ScaffoldMessenger.of(context).showSnackBar(
85
- const SnackBar(content: Text('RRSP Contribution logged successfully!'), backgroundColor: Colors.green),
86
- );
83
+ showSuccessSnackBar(context, 'RRSP Contribution logged successfully!');
87
84
  widget.onContributionLogged(); // Notify parent to refresh
88
85
  _formKey.currentState!.reset();
89
86
  _rrspAmountController.clear();
@@ -154,4 +151,4 @@ class _RrspContributionFormState extends State<RrspContributionForm> {
154
151
  ),
155
152
  );
156
153
  }
157
- }
154
+ }
@@ -1,6 +1,7 @@
1
1
  import 'package:flutter/material.dart';
2
2
  import 'package:intl/intl.dart';
3
3
  import 'package:personal_finance_frontend_core_services/services/stock_service.dart';
4
+ import 'package:personal_finance_frontend_core_ui/utils/app_snackbars.dart';
4
5
  import 'package:personal_finance_frontend_core_services/services/transaction_service.dart';
5
6
  import 'package:personal_finance_frontend_core_ui/utils/currency_input_formatter.dart';
6
7
  import 'package:personal_finance_frontend_core_ui/widgets/app_widgets.dart';
@@ -147,11 +148,7 @@ class _TradeFormState extends State<TradeForm> {
147
148
  final isValid = await _stockService.validateStockSymbol(symbol);
148
149
  if (!isValid) {
149
150
  if (mounted) {
150
- ScaffoldMessenger.of(context).showSnackBar(
151
- SnackBar(
152
- content: Text('Invalid stock symbol: "$symbol"'),
153
- backgroundColor: Colors.red),
154
- );
151
+ showErrorSnackBar(context, 'Invalid stock symbol: "$symbol"');
155
152
  }
156
153
  setState(() => _isSubmitting = false);
157
154
  return; // Stop submission
@@ -163,11 +160,7 @@ class _TradeFormState extends State<TradeForm> {
163
160
  if (_tradeType == 'sell') {
164
161
  if (_livePriceForSell == null) {
165
162
  if (mounted) {
166
- ScaffoldMessenger.of(context).showSnackBar(
167
- const SnackBar(
168
- content: Text('Could not get live price. Please try again.'),
169
- backgroundColor: Colors.red),
170
- );
163
+ showErrorSnackBar(context, 'Could not get live price. Please try again.');
171
164
  }
172
165
  setState(() => _isSubmitting = false);
173
166
  return;
@@ -195,15 +188,9 @@ class _TradeFormState extends State<TradeForm> {
195
188
 
196
189
  if (mounted) {
197
190
  if (errorMessage != null) {
198
- ScaffoldMessenger.of(context).showSnackBar(
199
- SnackBar(content: Text(errorMessage), backgroundColor: Colors.red),
200
- );
191
+ showErrorSnackBar(context, errorMessage);
201
192
  } else {
202
- ScaffoldMessenger.of(context).showSnackBar(
203
- const SnackBar(
204
- content: Text('Trade created successfully!'),
205
- backgroundColor: Colors.green),
206
- );
193
+ showSuccessSnackBar(context, 'Trade created successfully!');
207
194
  widget.onTradeCreated(tradeData);
208
195
  _formKey.currentState!.reset();
209
196
  _symbolController.clear();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marcos_feitoza/personal-finance-frontend-core-ui",
3
- "version": "1.0.2",
3
+ "version": "1.2.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },