diff --git a/frontend/lib/features/app/app_tabs.dart b/frontend/lib/features/app/app_tabs.dart index 11ddb18a..a870bf79 100644 --- a/frontend/lib/features/app/app_tabs.dart +++ b/frontend/lib/features/app/app_tabs.dart @@ -24,7 +24,7 @@ class AppTabs extends HookConsumerWidget { _TabItem( 'Home', const Icon(Icons.home_outlined), - HomePage(), + const HomePage(), ), _TabItem( 'Send', diff --git a/frontend/lib/features/home/home_page.dart b/frontend/lib/features/home/home_page.dart index 8de51c71..7035f4f0 100644 --- a/frontend/lib/features/home/home_page.dart +++ b/frontend/lib/features/home/home_page.dart @@ -1,17 +1,19 @@ import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_starter/features/deposit/deposit_page.dart'; +import 'package:flutter_starter/features/home/transaction_details_page.dart'; import 'package:flutter_starter/features/withdraw/withdraw_page.dart'; import 'package:flutter_starter/l10n/app_localizations.dart'; import 'package:flutter_starter/shared/grid.dart'; +import 'package:flutter_starter/shared/transaction.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; -class HomePage extends HookWidget { - HomePage({super.key}); - - final List txns = []; +class HomePage extends HookConsumerWidget { + const HomePage({super.key}); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + final txns = ref.watch(transactionsProvider); + return Scaffold( appBar: AppBar(title: Text(Loc.of(context).home)), body: SafeArea( @@ -30,7 +32,7 @@ class HomePage extends HookWidget { Expanded( child: txns.isEmpty ? _buildEmptyState(context) - : _buildTransactionsList(context), + : _buildTransactionsList(context, txns), ), ], ), @@ -122,19 +124,19 @@ class HomePage extends HookWidget { ); } - Widget _buildTransactionsList(BuildContext context) { + Widget _buildTransactionsList(BuildContext context, List txns) { return ListView( children: txns.map((txn) { return ListTile( + // TODO: display name for payments and type for deposits/withdrawals title: Text( - txn.title, + txn.type, style: Theme.of(context).textTheme.titleSmall?.copyWith( fontWeight: FontWeight.bold, ), ), - // TODO: display status from txn subtitle: Text( - 'status', + txn.status, style: Theme.of(context).textTheme.labelMedium?.copyWith( fontWeight: FontWeight.w300, ), @@ -152,16 +154,17 @@ class HomePage extends HookWidget { child: Text('\$'), ), ), + onTap: () => Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return TransactionDetailsPage( + txn: txn, + ); + }, + ), + ), ); }).toList(), ); } } - -// Will be replaced with FTL-generated types later -class Transaction { - final String title; - final double amount; - - Transaction({required this.title, required this.amount}); -} diff --git a/frontend/lib/features/home/transaction_details_page.dart b/frontend/lib/features/home/transaction_details_page.dart new file mode 100644 index 00000000..a1ea3081 --- /dev/null +++ b/frontend/lib/features/home/transaction_details_page.dart @@ -0,0 +1,244 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_starter/l10n/app_localizations.dart'; +import 'package:flutter_starter/shared/grid.dart'; +import 'package:flutter_starter/shared/transaction.dart'; + +class TransactionDetailsPage extends HookWidget { + final Transaction txn; + const TransactionDetailsPage({required this.txn, super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text(Loc.of(context).transactionDetails)), + body: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Column( + children: [ + _buildHeader(context), + _buildAmount(context), + _buildStatus(context), + if (txn.status != Status.failed) _buildDetails(context) + ], + ), + ), + ), + txn.status == Status.quoted + ? _buildResponseButtons(context) + : Padding( + padding: const EdgeInsets.symmetric(horizontal: Grid.side), + child: FilledButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text(Loc.of(context).done), + ), + ), + ], + ), + ), + ); + } + + Widget _buildHeader(BuildContext context) { + return Column( + children: [ + const SizedBox(height: Grid.md), + ExcludeSemantics( + child: Center( + child: Container( + width: Grid.xxl, + height: Grid.xxl, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceVariant, + shape: BoxShape.circle, + ), + child: Center( + // TODO: use $ or first letter of name based on txn type + child: Text( + '\$', + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + const SizedBox(height: Grid.xxs), + Text( + txn.type, + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ], + ); + } + + Widget _buildAmount(BuildContext context) { + return Column( + children: [ + const SizedBox(height: Grid.xxl), + Text( + '${txn.amount} USD', + style: Theme.of(context).textTheme.displayMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + // TODO: replace with createdAt time + const Text('Mar 1 at 10:00 am'), + ], + ); + } + + Widget _buildStatus(BuildContext context) { + return Column( + children: [ + const SizedBox(height: Grid.lg), + Icon(_getStatusIcon(txn.status), + size: Grid.md, color: _getStatusColor(context, txn.status)), + const SizedBox(height: Grid.xxs), + Text( + txn.status, + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ], + ); + } + + IconData _getStatusIcon(String status) { + switch (status) { + case Status.quoted: + return Icons.pending; + case Status.failed: + return Icons.error; + case Status.completed: + return Icons.check_circle; + default: + return Icons.help; + } + } + + Color _getStatusColor(BuildContext context, String status) { + var colorScheme = Theme.of(context).colorScheme; + switch (status) { + case Status.quoted: + return colorScheme.secondary; + case Status.failed: + return colorScheme.error; + case Status.completed: + return colorScheme.primary; + default: + return colorScheme.outlineVariant; + } + } + + Widget _buildDetails(BuildContext context) { + final paymentLabel = txn.status == Status.quoted + ? Loc.of(context).youPay + : txn.type == Type.deposit + ? Loc.of(context).youPaid + : Loc.of(context).youReceived; + + final balanceLabel = txn.status == Status.quoted + ? Loc.of(context).txnTypeQuote(txn.type) + : Loc.of(context).accountBalance; + + final amount = txn.status == Status.completed + ? '${txn.type == Type.deposit ? '+' : '-'}${txn.amount}' + : txn.amount.toString(); + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: Grid.side), + child: Column( + children: [ + const SizedBox(height: Grid.xxl), + Padding( + padding: const EdgeInsets.symmetric(vertical: Grid.xxs), + child: Row( + children: [ + Expanded( + flex: 1, + child: Text( + paymentLabel, + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + textAlign: TextAlign.left, + ), + ), + Expanded( + flex: 2, + child: Text( + '${txn.amount} USD', + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + textAlign: TextAlign.right, + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: Grid.xxs), + child: Row( + children: [ + Expanded( + child: Text( + balanceLabel, + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + textAlign: TextAlign.left, + ), + ), + Expanded( + child: Text( + '$amount USD', + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + textAlign: TextAlign.right, + ), + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildResponseButtons(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: Grid.sm), + child: Row( + children: [ + Expanded( + child: FilledButton( + onPressed: () {}, + child: Text(Loc.of(context).reject), + ), + ), + const SizedBox(width: Grid.sm), + Expanded( + child: FilledButton( + onPressed: () {}, + child: Text(Loc.of(context).accept), + ), + ), + ], + ), + ); + } +} diff --git a/frontend/lib/l10n/app_en.arb b/frontend/lib/l10n/app_en.arb index 273da179..ae00a98a 100644 --- a/frontend/lib/l10n/app_en.arb +++ b/frontend/lib/l10n/app_en.arb @@ -23,5 +23,20 @@ "usd": "USD", "activity": "Activity", "noTransactionsYet": "No transactions yet", - "startByAdding": "Start by adding funds to your account!" + "startByAdding": "Start by adding funds to your account!", + "transactionDetails": "Transaction details", + "youPay": "You pay", + "youPaid": "You paid", + "youReceived": "You received", + "txnTypeQuote": "{txnType} quote", + "@txnTypeQuote": { + "placeholders": { + "txnType": { + "type": "String" + } + } + }, + "accountBalance": "Account balance", + "accept": "Accept", + "reject": "Reject" } diff --git a/frontend/lib/l10n/app_localizations.dart b/frontend/lib/l10n/app_localizations.dart index 89e62fee..e81ded2b 100644 --- a/frontend/lib/l10n/app_localizations.dart +++ b/frontend/lib/l10n/app_localizations.dart @@ -240,6 +240,48 @@ abstract class Loc { /// In en, this message translates to: /// **'Start by adding funds to your account!'** String get startByAdding; + + /// No description provided for @transactionDetails. + /// + /// In en, this message translates to: + /// **'Transaction details'** + String get transactionDetails; + + /// No description provided for @youPay. + /// + /// In en, this message translates to: + /// **'You pay'** + String get youPay; + + /// No description provided for @youPaid. + /// + /// In en, this message translates to: + /// **'You paid'** + String get youPaid; + + /// No description provided for @youReceived. + /// + /// In en, this message translates to: + /// **'You received'** + String get youReceived; + + /// No description provided for @txnTypeQuote. + /// + /// In en, this message translates to: + /// **'{txnType} quote'** + String txnTypeQuote(String txnType); + + /// No description provided for @accept. + /// + /// In en, this message translates to: + /// **'Accept'** + String get accept; + + /// No description provided for @reject. + /// + /// In en, this message translates to: + /// **'Reject'** + String get reject; } class _LocDelegate extends LocalizationsDelegate { diff --git a/frontend/lib/l10n/app_localizations_en.dart b/frontend/lib/l10n/app_localizations_en.dart index 855046b8..02888721 100644 --- a/frontend/lib/l10n/app_localizations_en.dart +++ b/frontend/lib/l10n/app_localizations_en.dart @@ -78,4 +78,27 @@ class LocEn extends Loc { @override String get startByAdding => 'Start by adding funds to your account!'; + + @override + String get transactionDetails => 'Transaction details'; + + @override + String get youPay => 'You pay'; + + @override + String get youPaid => 'You paid'; + + @override + String get youReceived => 'You received'; + + @override + String txnTypeQuote(String txnType) { + return '$txnType quote'; + } + + @override + String get accept => 'Accept'; + + @override + String get reject => 'Reject'; } diff --git a/frontend/lib/shared/transaction.dart b/frontend/lib/shared/transaction.dart new file mode 100644 index 00000000..0f317016 --- /dev/null +++ b/frontend/lib/shared/transaction.dart @@ -0,0 +1,37 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +// TODO: remove this file when FTL generated types are available +class Transaction { + final String type; + final String status; + final double amount; + + Transaction({ + required this.type, + required this.status, + required this.amount, + }); +} + +final _defaultList = [ + Transaction(type: Type.deposit, status: Status.quoted, amount: 4.61), + Transaction(type: Type.withdrawal, status: Status.quoted, amount: 20.85), + Transaction(type: Type.deposit, status: Status.completed, amount: 10.99), + Transaction(type: Type.withdrawal, status: Status.completed, amount: 7.03), + Transaction(type: Type.withdrawal, status: Status.failed, amount: 5.42), +]; + +final transactionsProvider = StateProvider>((ref) { + return _defaultList; +}); + +class Status { + static const String failed = 'Failed'; + static const String quoted = 'Quoted'; + static const String completed = 'Completed'; +} + +class Type { + static const String deposit = 'Deposit'; + static const String withdrawal = 'Withdrawal'; +} diff --git a/frontend/test/features/deposit/deposit_page_test.dart b/frontend/test/features/deposit/deposit_page_test.dart index 2637f72e..6ea279c0 100644 --- a/frontend/test/features/deposit/deposit_page_test.dart +++ b/frontend/test/features/deposit/deposit_page_test.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:flutter_starter/features/deposit/deposit_page.dart'; import 'package:flutter_starter/shared/currency_converter.dart'; import 'package:flutter_starter/shared/fee_details.dart'; @@ -28,7 +29,7 @@ void main() { WidgetHelpers.testableWidget(child: const DepositPage()), ); - expect(find.text('Next'), findsOneWidget); + expect(find.widgetWithText(FilledButton, 'Next'), findsOneWidget); }); testWidgets('should change deposit input amount after number pad press', diff --git a/frontend/test/features/home/home_page_test.dart b/frontend/test/features/home/home_page_test.dart index aff1b0bb..827e5da3 100644 --- a/frontend/test/features/home/home_page_test.dart +++ b/frontend/test/features/home/home_page_test.dart @@ -1,6 +1,8 @@ +import 'package:flutter/material.dart'; import 'package:flutter_starter/features/deposit/deposit_page.dart'; import 'package:flutter_starter/features/home/home_page.dart'; import 'package:flutter_starter/features/withdraw/withdraw_page.dart'; +import 'package:flutter_starter/shared/transaction.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../helpers/widget_helpers.dart'; @@ -9,7 +11,7 @@ void main() { group('HomePage', () { testWidgets('should show account balance', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: HomePage()), + WidgetHelpers.testableWidget(child: const HomePage()), ); expect(find.text('Account balance'), findsOneWidget); @@ -17,7 +19,7 @@ void main() { testWidgets('should show valid account balance amount', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: HomePage()), + WidgetHelpers.testableWidget(child: const HomePage()), ); final dollarAmountPattern = RegExp(r'\$[0-9]+\.[0-9]{2}$'); @@ -27,19 +29,19 @@ void main() { testWidgets('should show deposit button', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: HomePage()), + WidgetHelpers.testableWidget(child: const HomePage()), ); - expect(find.text('Deposit'), findsOneWidget); + expect(find.widgetWithText(FilledButton, 'Deposit'), findsOneWidget); }); testWidgets('should navigate to DepositPage on tap of deposit button', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: HomePage()), + WidgetHelpers.testableWidget(child: const HomePage()), ); - await tester.tap(find.text('Deposit')); + await tester.tap(find.widgetWithText(FilledButton, 'Deposit')); await tester.pumpAndSettle(); expect(find.byType(DepositPage), findsOneWidget); @@ -47,16 +49,16 @@ void main() { testWidgets('should show withdraw button', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: HomePage()), + WidgetHelpers.testableWidget(child: const HomePage()), ); - expect(find.text('Withdraw'), findsOneWidget); + expect(find.widgetWithText(FilledButton, 'Withdraw'), findsOneWidget); }); testWidgets('should navigate to WithdrawPage on tap of withdraw button', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: HomePage()), + WidgetHelpers.testableWidget(child: const HomePage()), ); await tester.tap(find.text('Withdraw')); @@ -67,7 +69,12 @@ void main() { testWidgets('should show empty state when no transactions', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: HomePage()), + WidgetHelpers.testableWidget( + child: const HomePage(), + overrides: [ + transactionsProvider.overrideWith((ref) => []), + ], + ), ); expect(find.text('No transactions yet'), findsOneWidget); @@ -79,7 +86,12 @@ void main() { testWidgets('should navigate to DepositPage on tap of get started button', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: HomePage()), + WidgetHelpers.testableWidget( + child: const HomePage(), + overrides: [ + transactionsProvider.overrideWith((ref) => []), + ], + ), ); await tester.tap(find.text('Get started')); diff --git a/frontend/test/features/home/transaction_details_page_test.dart b/frontend/test/features/home/transaction_details_page_test.dart new file mode 100644 index 00000000..bef7832d --- /dev/null +++ b/frontend/test/features/home/transaction_details_page_test.dart @@ -0,0 +1,172 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_starter/features/home/transaction_details_page.dart'; +import 'package:flutter_starter/shared/transaction.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../helpers/widget_helpers.dart'; + +void main() { + group('TransactionDetailsPage', () { + testWidgets('should show deposit header', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: TransactionDetailsPage( + txn: Transaction(type: 'Deposit', status: '', amount: 0), + ), + ), + ); + + expect(find.text('Deposit'), findsOneWidget); + }); + + testWidgets('should show withdrawal header', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: TransactionDetailsPage( + txn: Transaction(type: 'Withdrawal', status: '', amount: 0), + ), + ), + ); + + expect(find.text('Withdrawal'), findsOneWidget); + }); + + testWidgets('should show transaction amount', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: TransactionDetailsPage( + txn: Transaction(type: '', status: '', amount: 123.45), + ), + ), + ); + + expect(find.text('123.45 USD'), findsExactly(3)); + }); + + testWidgets('should show quoted transaction status', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: TransactionDetailsPage( + txn: Transaction(type: '', status: 'Quoted', amount: 0), + ), + ), + ); + + expect(find.text('Quoted'), findsOneWidget); + expect(find.byIcon(Icons.pending), findsOneWidget); + }); + + testWidgets('should show completed transaction status', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: TransactionDetailsPage( + txn: Transaction(type: '', status: 'Completed', amount: 0), + ), + ), + ); + + expect(find.text('Completed'), findsOneWidget); + expect(find.byIcon(Icons.check_circle), findsOneWidget); + }); + + testWidgets('should show failed transaction status', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: TransactionDetailsPage( + txn: Transaction(type: '', status: 'Failed', amount: 0), + ), + ), + ); + + expect(find.text('Failed'), findsOneWidget); + expect(find.byIcon(Icons.error), findsOneWidget); + }); + + testWidgets('should show unknown transaction status', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: TransactionDetailsPage( + txn: Transaction(type: '', status: 'Unknown', amount: 0), + ), + ), + ); + + expect(find.text('Unknown'), findsOneWidget); + expect(find.byIcon(Icons.help), findsOneWidget); + }); + + testWidgets('should show you pay label', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: TransactionDetailsPage( + txn: Transaction(type: '', status: 'Quoted', amount: 0), + ), + ), + ); + + expect(find.text('You pay'), findsOneWidget); + }); + + testWidgets('should show you paid label', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: TransactionDetailsPage( + txn: Transaction(type: 'Deposit', status: 'Completed', amount: 0), + ), + ), + ); + + expect(find.text('You paid'), findsOneWidget); + }); + + testWidgets('should show you received label', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: TransactionDetailsPage( + txn: + Transaction(type: 'Withdrawal', status: 'Completed', amount: 0), + ), + ), + ); + + expect(find.text('You received'), findsOneWidget); + }); + + testWidgets('should show account balance label', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: TransactionDetailsPage( + txn: Transaction(type: '', status: 'Completed', amount: 0), + ), + ), + ); + + expect(find.text('Account balance'), findsOneWidget); + }); + + testWidgets('should show reject and accept', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: TransactionDetailsPage( + txn: Transaction(type: '', status: 'Quoted', amount: 0), + ), + ), + ); + + expect(find.text('Reject'), findsOneWidget); + expect(find.text('Accept'), findsOneWidget); + }); + + testWidgets('should show done', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: TransactionDetailsPage( + txn: Transaction(type: '', status: 'Completed', amount: 0), + ), + ), + ); + + expect(find.text('Done'), findsOneWidget); + }); + }); +} diff --git a/frontend/test/features/send/send_did_page_test.dart b/frontend/test/features/send/send_did_page_test.dart index ff53ad7a..85109adf 100644 --- a/frontend/test/features/send/send_did_page_test.dart +++ b/frontend/test/features/send/send_did_page_test.dart @@ -48,7 +48,7 @@ void main() { child: const SendDidPage(sendAmount: '25')), ); - expect(find.text('Pay \$25'), findsOneWidget); + expect(find.widgetWithText(FilledButton, 'Pay \$25'), findsOneWidget); }); }); } diff --git a/frontend/test/features/send/send_page_test.dart b/frontend/test/features/send/send_page_test.dart index fcdd41e2..16ab8702 100644 --- a/frontend/test/features/send/send_page_test.dart +++ b/frontend/test/features/send/send_page_test.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:flutter_starter/features/send/send_did_page.dart'; import 'package:flutter_starter/features/send/send_page.dart'; import 'package:flutter_starter/shared/number_pad.dart'; @@ -20,7 +21,7 @@ void main() { WidgetHelpers.testableWidget(child: const SendPage()), ); - expect(find.text('Send'), findsOneWidget); + expect(find.widgetWithText(FilledButton, 'Send'), findsOneWidget); }); testWidgets('should change send amount after number pad press', diff --git a/frontend/test/features/withdraw/withdraw_page_test.dart b/frontend/test/features/withdraw/withdraw_page_test.dart index c8480297..584d3dfe 100644 --- a/frontend/test/features/withdraw/withdraw_page_test.dart +++ b/frontend/test/features/withdraw/withdraw_page_test.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:flutter_starter/features/withdraw/withdraw_page.dart'; import 'package:flutter_starter/shared/currency_converter.dart'; import 'package:flutter_starter/shared/fee_details.dart'; @@ -28,7 +29,7 @@ void main() { WidgetHelpers.testableWidget(child: const WithdrawPage()), ); - expect(find.text('Next'), findsOneWidget); + expect(find.widgetWithText(FilledButton, 'Next'), findsOneWidget); }); testWidgets('should change withdraw input amount after number pad press',