currency_input_field 0.1.1
currency_input_field: ^0.1.1 copied to clipboard
A reusable Flutter widget for selecting a currency and entering a monetary amount, with validation, flexible layouts, and configurable sizing.
Currency Input Field #
A reusable Flutter widget for selecting a currency and entering a monetary amount, with validation, flexible layouts, generic currency support, and configurable sizing.
Features #
- Currency dropdown plus amount input in a single reusable component
- Supports
String, enums, and custom currency models through generics - Built-in validation support for:
- currency selection
- amount input
- combined business-rule validation
- Inline, stacked, and adaptive layout modes
- Controller support for reading, updating, and clearing values
- Configurable sizing for compact or spacious UI
- Suitable for admin panels, mobile forms, donation flows, invoice screens, and payment forms
Preview #
The example app currently includes the following production-style scenarios:
| Scenario | What it demonstrates |
|---|---|
| Finance admin payout form | Compact inline layout, enterprise-style spacing, payout validation, and unsupported currency rules |
| Mobile donation flow | Stacked mobile-friendly layout, spacious padding, and donation-focused amount validation |
| Cross-border invoice payment | Enum-based currency support, adaptive layout, and currency-specific business rules |
| Fixed invoice settlement | Prefilled controller state, read-only amount handling, and review-and-confirm payment flow |
| Quick transfer inline with icons | Inline layout with decorated fields, compact fast-entry UX, and icon-based input styling |
| Wallet top-up with icons | Stacked consumer-style layout with icons, guided input decoration, and wallet top-up validation |
Finance admin payout formCompact inline layout, enterprise-style spacing, payout validation, and unsupported currency rules.
|
Mobile donation flowStacked mobile-friendly layout, spacious padding, and donation-focused amount validation.
|
Cross-border invoice paymentEnum-based currency support, adaptive layout, and currency-specific business rules.
|
Fixed invoice settlementPrefilled controller state, read-only amount handling, and review-and-confirm payment flow.
|
Quick transfer inline with iconsInline layout with decorated fields, compact fast-entry UX, and icon-based input styling.
|
Wallet top-up with iconsStacked consumer-style layout with icons, guided input decoration, and wallet top-up validation.
|
Preview coverage #
- Inline layout
- Stacked layout
- Adaptive layout
- Enum-based usage
- Validation states
- Compact styling
- Spacious styling
- Read-only amount configuration
- Controller-based prefilled values
- Input decoration with icons
- Enterprise/admin-style forms
- Consumer/mobile-friendly forms
Installation #
Add the dependency to your pubspec.yaml:
dependencies:
currency_input_field: ^0.1.1
Then run:
flutter pub get
Import #
import 'package:currency_input_field/currency_input_field.dart';
Basic Usage #
import 'package:currency_input_field/currency_input_field.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Currency Input Field Example')),
body: const Padding(
padding: EdgeInsets.all(16),
child: BasicExample(),
),
),
);
}
}
class BasicExample extends StatefulWidget {
const BasicExample({super.key});
@override
State<BasicExample> createState() => _BasicExampleState();
}
class _BasicExampleState extends State<BasicExample> {
final _formKey = GlobalKey<FormState>();
final _controller = CurrencyInputController<String>(
initialCurrency: 'USD',
);
String? result;
void _submit() {
final isValid = _formKey.currentState?.validate() ?? false;
setState(() {
if (!isValid) {
result = 'Please fix the validation errors.';
return;
}
result = 'Submitted: ${_controller.currency} ${_controller.amountText}';
});
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CurrencyInputField<String>(
controller: _controller,
currencies: const ['USD', 'GBP', 'ZWG'],
currencyLabelBuilder: (currency) => currency,
currencyHintText: 'Currency',
monetaryHintText: 'Amount',
containerPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0),
fieldHorizontalPadding: 10,
fieldVerticalPadding: 8,
inlineDividerHeight: 32,
stackedDividerSpacing: 1,
useLabelText: true,
amountValidator: (value) {
final amount = double.tryParse(value);
if (amount == null || amount <= 0) {
return 'Enter an amount greater than zero';
}
return null;
},
),
const SizedBox(height: 12),
FilledButton(
onPressed: _submit,
child: const Text('Submit'),
),
if (result != null) ...[
const SizedBox(height: 12),
Text(result!),
],
],
),
);
}
}
Using an Enum #
enum CurrencyCode {
usd,
gbp,
zwg,
}
extension CurrencyCodeX on CurrencyCode {
String get code {
switch (this) {
case CurrencyCode.usd:
return 'USD';
case CurrencyCode.gbp:
return 'GBP';
case CurrencyCode.zwg:
return 'ZWG';
}
}
}
final controller = CurrencyInputController<CurrencyCode>(
initialCurrency: CurrencyCode.usd,
);
CurrencyInputField<CurrencyCode>(
controller: controller,
currencies: CurrencyCode.values,
currencyLabelBuilder: (currency) => currency.code,
currencyHintText: 'Currency',
monetaryHintText: 'Amount',
containerPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0),
fieldHorizontalPadding: 10,
fieldVerticalPadding: 8,
inlineDividerHeight: 32,
stackedDividerSpacing: 1,
useLabelText: false,
)
Validation Options #
The widget supports three levels of validation.
currencyValidator #
Use this for currency-specific restrictions.
currencyValidator: (currency) {
if (currency == 'ZAR') {
return 'ZAR is not supported for this flow';
}
return null;
},
amountValidator #
Use this for amount-only rules.
amountValidator: (value) {
final amount = double.tryParse(value);
if (amount == null || amount <= 0) {
return 'Enter a valid amount';
}
if (amount < 5) {
return 'Minimum amount is 5';
}
return null;
},
validator #
Use this for combined business rules involving both currency and amount.
validator: (value) {
if (value.currency == 'USD' && (value.amount ?? 0) > 500) {
return 'USD amount cannot exceed 500';
}
return null;
},
Layout Modes #
Inline #
Best for admin dashboards and wider layouts.
layoutMode: CurrencyInputLayoutMode.inline,
Stacked #
Best for mobile-first forms and narrow screens.
layoutMode: CurrencyInputLayoutMode.stacked,
Adaptive #
Switches automatically based on available width.
layoutMode: CurrencyInputLayoutMode.adaptive,
stackBreakpoint: 360,
Controller Usage #
final controller = CurrencyInputController<String>(
initialCurrency: 'USD',
initialAmount: '25.00',
);
// Read current values
controller.currency;
controller.amountText;
controller.amount;
// Update values
controller.setCurrency('GBP');
controller.setAmountText('80');
// Clear values
controller.clear();
Design and Layout Customization #
CurrencyInputField<T> is designed to support different UI styles, from compact admin forms to spacious mobile-friendly flows.
This section documents the parameters you can adjust to achieve the design you want.
1. Layout Mode #
Controls whether the currency and amount fields appear side by side or stacked.
layoutMode
layoutMode: CurrencyInputLayoutMode.adaptive
Available values:
CurrencyInputLayoutMode.inline
Always shows the fields side by side.CurrencyInputLayoutMode.stacked
Always shows the currency field above the amount field.CurrencyInputLayoutMode.adaptive
Switches between inline and stacked depending on available width.
stackBreakpoint
stackBreakpoint: 360
Used only when layoutMode is adaptive.
If the available width is less than stackBreakpoint, the widget switches to stacked mode.
Recommended usage
- Use
inlinefor dashboards, admin portals, and desktop forms - Use
stackedfor mobile-first forms and narrow UIs - Use
adaptivewhen you want a single responsive widget
2. Outer Container Size #
These parameters control the size and feel of the main wrapper around both inputs.
containerPadding
containerPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0)
Controls padding inside the outer container.
Use smaller values for a tighter look, and larger values for a more spacious look.
Examples
Compact:
containerPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0)
Spacious:
containerPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4)
3. Inner Field Padding #
These parameters control how tall and wide each field feels.
fieldHorizontalPadding
fieldHorizontalPadding: 10
Controls left and right padding inside both inputs.
fieldVerticalPadding
fieldVerticalPadding: 8
Controls top and bottom padding inside both inputs.
This is one of the most important parameters for reducing or increasing vertical size.
Examples
Compact:
fieldHorizontalPadding: 10,
fieldVerticalPadding: 8,
Spacious:
fieldHorizontalPadding: 14,
fieldVerticalPadding: 14,
4. Divider Sizing #
These parameters affect the visual separation between the currency and amount fields.
inlineDividerHeight
inlineDividerHeight: 32
Height of the vertical divider in inline layout.
stackedDividerSpacing
stackedDividerSpacing: 1
Height used by the divider between the top and bottom fields in stacked mode.
Smaller values create a tighter layout.
5. Labels vs Hints #
useLabelText
useLabelText: true
Controls whether the provided hint texts are shown as floating labels.
When true, the widget uses:
currencyHintTextmonetaryHintText
as label text.
When false, they behave like standard hints.
Examples
Label style:
currencyHintText: 'Currency',
monetaryHintText: 'Amount',
useLabelText: true,
Hint-only style:
currencyHintText: 'Select currency',
monetaryHintText: 'Enter amount',
useLabelText: false,
Recommended usage
- Use
useLabelText: truefor enterprise/admin-style forms - Use
useLabelText: falsefor lighter consumer-style layouts
6. Field Width Balance #
When using inline layout, you can control how much horizontal space each section gets.
currencyFlex
currencyFlex: 4
amountFlex
amountFlex: 6
These are passed into Expanded widgets in inline mode.
Examples
Balanced:
currencyFlex: 5,
amountFlex: 5,
Amount gets more space:
currencyFlex: 4,
amountFlex: 6,
7. Dropdown Appearance #
These parameters affect the currency dropdown overlay.
dropdownBorderRadius
dropdownBorderRadius: BorderRadius.circular(12)
Controls the dropdown menu shape.
dropdownMenuMaxHeight
dropdownMenuMaxHeight: 280
Limits the maximum height of the dropdown menu.
Useful when you support many currencies and want a more controlled menu size.
8. Styling Support #
style
style: const CurrencyInputFieldStyle(...)
This allows deeper visual styling.
From the current implementation, style is used for:
backgroundColorborderRadiusborderColorfocusedBorderColorerrorBorderColordividerColorcurrencyDecorationamountDecorationcurrencyTextStyleamountTextStyle
This is the main place to customize colors, border radius, input decorations, and text styles.
9. UX and Behavior Options #
These are not purely visual, but they affect how the component feels in real usage.
enabled
enabled: true
Disables both inputs when set to false.
readOnlyAmount
readOnlyAmount: true
Makes the amount input read-only while still displaying its value.
Useful for review or fixed-amount payment flows.
autofocusAmount
autofocusAmount: true
Focuses the amount field automatically when the widget appears.
Useful for fast-entry forms.
10. Keyboard and Input Behavior #
These parameters affect the amount field input experience.
amountKeyboardType
amountKeyboardType: const TextInputType.numberWithOptions(decimal: true)
Controls the keyboard shown for the amount field.
amountTextInputAction
amountTextInputAction: TextInputAction.done
Controls the keyboard action button.
decimalDigits
decimalDigits: 2
Controls allowed decimal precision.
allowNegative
allowNegative: false
Allows or disallows negative values.
amountInputFormatters
amountInputFormatters: [...]
Lets you pass additional input formatters.
11. Practical Design Presets #
Compact admin / dashboard style
CurrencyInputField<String>(
currencies: const ['USD', 'EUR', 'GBP'],
currencyLabelBuilder: (currency) => currency,
currencyHintText: 'Currency',
monetaryHintText: 'Amount',
layoutMode: CurrencyInputLayoutMode.inline,
useLabelText: true,
containerPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0),
fieldHorizontalPadding: 10,
fieldVerticalPadding: 8,
inlineDividerHeight: 32,
stackedDividerSpacing: 1,
currencyFlex: 4,
amountFlex: 6,
)
Spacious mobile / consumer style
CurrencyInputField<String>(
currencies: const ['USD', 'EUR', 'GBP'],
currencyLabelBuilder: (currency) => currency,
currencyHintText: 'Choose currency',
monetaryHintText: 'Enter amount',
layoutMode: CurrencyInputLayoutMode.stacked,
useLabelText: true,
containerPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
fieldHorizontalPadding: 14,
fieldVerticalPadding: 14,
inlineDividerHeight: 40,
stackedDividerSpacing: 4,
)
Lightweight simple style
CurrencyInputField<String>(
currencies: const ['USD', 'EUR'],
currencyLabelBuilder: (currency) => currency,
currencyHintText: 'Currency',
monetaryHintText: 'Amount',
layoutMode: CurrencyInputLayoutMode.inline,
useLabelText: false,
containerPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0),
fieldHorizontalPadding: 10,
fieldVerticalPadding: 8,
inlineDividerHeight: 32,
stackedDividerSpacing: 1,
)
12. Quick Reference #
| Parameter | What it affects |
|---|---|
layoutMode |
Inline, stacked, or adaptive layout |
stackBreakpoint |
Width threshold for adaptive layout |
containerPadding |
Padding inside the outer wrapper |
fieldHorizontalPadding |
Left/right padding inside each field |
fieldVerticalPadding |
Top/bottom padding inside each field |
inlineDividerHeight |
Vertical divider height in inline mode |
stackedDividerSpacing |
Divider spacing in stacked mode |
useLabelText |
Floating labels vs hint-only behavior |
currencyFlex |
Width share of the currency field in inline mode |
amountFlex |
Width share of the amount field in inline mode |
dropdownBorderRadius |
Dropdown menu corner radius |
dropdownMenuMaxHeight |
Maximum dropdown height |
style |
Colors, decorations, text styles, border radius |
enabled |
Enabled/disabled state |
readOnlyAmount |
Makes amount read-only |
autofocusAmount |
Automatically focuses amount field |
amountKeyboardType |
Keyboard type for amount input |
amountTextInputAction |
Keyboard action button |
decimalDigits |
Decimal precision |
allowNegative |
Allows negative values |
amountInputFormatters |
Additional input formatters |
13. Recommended Starting Points #
If you are unsure where to start:
-
For admin or enterprise UIs:
layoutMode: CurrencyInputLayoutMode.inlineuseLabelText: true- smaller paddings
-
For mobile and consumer UIs:
layoutMode: CurrencyInputLayoutMode.stackeduseLabelText: true- larger vertical paddings
-
For reusable responsive forms:
layoutMode: CurrencyInputLayoutMode.adaptivestackBreakpoint: 360
Common Use Cases #
- Payment and checkout forms
- Donation and contribution screens
- Back-office payout tools
- Invoice payment and settlement flows
- Admin dashboards
- Mobile finance apps
Package Example #
See the /example app for real-world scenarios, including:
- admin payout form
- mobile donation flow
- enum-based invoice currency selection
- read-only invoice settlement
Breaking Changes in 0.1.0 #
Version 0.1.0 introduces breaking API changes from 0.0.5, including:
- generic currency type support
- updated validation API using
amountValidator - explicit sizing configuration
- refreshed production-oriented example patterns
License #
This project is licensed under the MIT License. See the LICENSE file for details.
Author #
Munyaradzi Chigangawa
Website: https://munyaradzichigangawa.co.zw