Skip to content

Commit

Permalink
Component phone input (#28)
Browse files Browse the repository at this point in the history
* chore: update contributing

* fix: Fix button group immutability (#1)

* Fix errors

* fix copywith function

* [automated commit] lint format and import sort

---------

Co-authored-by: Osman <[email protected]>
Co-authored-by: github-actions <[email protected]>

* [automated commit] lint format and import sort

* update on-main to push to firebase (#3)

* ci: move firebase to flutter main host for qa (#4)

* feat: Add List Item (#5)

* feat: Add List Item

* [automated commit] lint format and import sort

---------

Co-authored-by: Simeon Dimitrov <[email protected]>
Co-authored-by: github-actions <[email protected]>

* fix(main): ListItem disabled color (#8)

* fix(main): ListItem disabled color

* [automated commit] lint format and import sort

---------

Co-authored-by: github-actions <[email protected]>

* feat : Dropdown menu (#7)

* Create dropdown

* Add sizes

* create stoyrybook and add size

* Fix errrs and respond to comments

* Fix issues

* [automated commit] lint format and import sort

* Alter isLarge

* Fix spacing

* [automated commit] lint format and import sort

* Alter leading styles

* [automated commit] lint format and import sort

---------

Co-authored-by: Osman <[email protected]>
Co-authored-by: github-actions <[email protected]>

* Component ZetaSwitch (#6)

* create ZetaSwitch

* ZetaSwitch using MaterialSwitch

* widgetbook for ZetaSwitch

* remove hover; fix initState

* add showHover parameter

* add comments 'Zeta change' in material_switch.dart

* remove size parameter and factory constructors

* fix example and widgetbook

* Component Zeta Radio Button (#9)

* create component Zeta Radio Button

* remove hover color

* fix label line height

* feat(main): SnackBar (#10)

* add snackbar example

* Add snackbar widgetbook

* feat(main): SnackBar

* [automated commit] lint format and import sort

* remove view icon

* Add view icon

* Add widgetbook icon helper

* [automated commit] lint format and import sort

* fix alphabetical imports

* Fix delete and error background color

---------

Co-authored-by: github-actions <[email protected]>

* feat(main): Tabs (#11)

* feat(main): Tabs

* [automated commit] lint format and import sort

---------

Co-authored-by: github-actions <[email protected]>

* chore: Update text styles (#13)

* fix: switch on web (#14)

* Component date input (#12)

* create ZetaDateInput

* create different ZetaDateInput variants

* fix show error style

* date validation and input mask; documentation for ZetaDateInput properties

* create widgetbook

* changes according to comments

* Component date input (#16)

* create ZetaDateInput

* create different ZetaDateInput variants

* fix show error style

* date validation and input mask; documentation for ZetaDateInput properties

* create widgetbook

* changes according to comments

* fix Typography of Date Input

* restore

* remove text line height

* ZetaPhoneInput initial commit

* complete ZetaPhoneInput; add flags

* create phoneInputUseCase in Widgetbook

* refactor phone input to use native alert dialog

* don't use root navigator in widgetbook

* pass parameter useRootNavigator

* restore some missing countries in the list

* countries search

* add searchHint

* fix comments

---------

Co-authored-by: Luke <[email protected]>
Co-authored-by: ahmed-osman3 <[email protected]>
Co-authored-by: Osman <[email protected]>
Co-authored-by: github-actions <[email protected]>
Co-authored-by: Luke Walton <[email protected]>
Co-authored-by: Simeon Dimitrov <[email protected]>
Co-authored-by: sd-athlon <[email protected]>
  • Loading branch information
8 people committed Apr 25, 2024
1 parent 090ffc7 commit ebeaa90
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 30 deletions.
165 changes: 138 additions & 27 deletions lib/src/components/phone_input/countries_dialog.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../../../zeta_flutter.dart';
import 'countries.dart';

/// Class for [CountriesDialog]
class CountriesDialog<T> extends StatefulWidget {
class CountriesDialog extends StatefulWidget {
///Constructor of [CountriesDialog]
const CountriesDialog({
super.key,
Expand All @@ -12,6 +13,7 @@ class CountriesDialog<T> extends StatefulWidget {
required this.items,
required this.onChanged,
this.enabled = true,
this.searchHint,
this.useRootNavigator = true,
});

Expand All @@ -21,46 +23,52 @@ class CountriesDialog<T> extends StatefulWidget {
/// The button, which opens the dialog.
final Widget button;

/// List of [DropdownMenuItem]
final List<DropdownMenuItem<T>> items;
/// List of [CountriesMenuItem]
final List<CountriesMenuItem> items;

/// Called when an item is selected.
final ValueSetter<T?> onChanged;
final ValueSetter<Country?> onChanged;

/// Determines if the button should be enabled (default) or disabled.
final bool enabled;

/// The hint to be shown inside the country search input field.
/// Default is `Search by name or dial code`.
final String? searchHint;

/// Determines if the root navigator should be used.
final bool useRootNavigator;

@override
State<CountriesDialog<T>> createState() => _CountriesDialogState();
State<CountriesDialog> createState() => _CountriesDialogState();

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(IterableProperty<DropdownMenuItem<T>>('items', items))
..add(ObjectFlagProperty<ValueSetter<T?>>.has('onChanged', onChanged))
..add(IterableProperty<CountriesMenuItem>('items', items))
..add(ObjectFlagProperty<ValueSetter<Country?>>.has('onChanged', onChanged))
..add(DiagnosticsProperty<bool>('enabled', enabled))
..add(DiagnosticsProperty<bool>('useRootNavigator', useRootNavigator));
..add(DiagnosticsProperty<bool>('useRootNavigator', useRootNavigator))
..add(StringProperty('searchHint', searchHint));
}
}

class _CountriesDialogState<T> extends State<CountriesDialog<T>> {
Future<T?> _showCountriesDialog(
class _CountriesDialogState extends State<CountriesDialog> {
Future<Country?> _showCountriesDialog(
BuildContext context, {
Zeta? zeta,
required List<DropdownMenuItem<T>> items,
required List<CountriesMenuItem> items,
bool barrierDismissible = true,
bool useRootNavigator = true,
}) =>
showDialog<T?>(
showDialog<Country?>(
context: context,
barrierDismissible: barrierDismissible,
useRootNavigator: useRootNavigator,
builder: (_) => _CountriesList(
items: items,
searchHint: widget.searchHint,
zeta: zeta,
),
);
Expand All @@ -84,40 +92,143 @@ class _CountriesDialogState<T> extends State<CountriesDialog<T>> {
}
}

class _CountriesList<T> extends StatelessWidget {
class _CountriesList extends StatefulWidget {
const _CountriesList({
required this.items,
this.searchHint,
this.zeta,
});

final Zeta? zeta;
final List<DropdownMenuItem<T>> items;
final List<CountriesMenuItem> items;
final String? searchHint;

@override
State<_CountriesList> createState() => _CountriesListState();

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(IterableProperty<CountriesMenuItem>('items', items))
..add(StringProperty('searchHint', searchHint));
}
}

class _CountriesListState extends State<_CountriesList> {
late final bool _enableSearch = widget.items.length > 20;
final _controller = TextEditingController();
List<CountriesMenuItem> _items = [];

@override
void initState() {
super.initState();
_items = List.from(widget.items);
}

void _search(String value) {
setState(() {
_items = widget.items.where((item) {
return item.value.name.toLowerCase().contains(value.toLowerCase()) ||
(RegExp(r'^\d+$').hasMatch(value) && item.value.dialCode.indexOf('+$value') == 0);
}).toList();
});
}

void _clearSearch() {
_controller.clear();
setState(() {
_items = List.from(widget.items);
});
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
final zeta = this.zeta ?? Zeta.of(context);
final zeta = widget.zeta ?? Zeta.of(context);

return AlertDialog(
surfaceTintColor: zeta.colors.surfacePrimary,
shape: const RoundedRectangleBorder(borderRadius: ZetaRadius.large),
content: SizedBox(
width: double.maxFinite,
child: ListView.builder(
shrinkWrap: true,
itemCount: items.length,
itemBuilder: (_, index) => InkWell(
onTap: () {
Navigator.of(context).pop(items[index].value);
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: ZetaSpacing.xs,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (_enableSearch)
Padding(
padding: const EdgeInsets.only(bottom: ZetaSpacing.b),
child: TextField(
controller: _controller,
onChanged: _search,
decoration: InputDecoration(
hintText: widget.searchHint ?? 'Search by name or dial code',
prefixIcon: const Icon(ZetaIcons.search_round),
suffixIcon: _controller.text.isEmpty
? null
: IconButton(
onPressed: _clearSearch,
icon: Icon(
ZetaIcons.cancel_round,
color: zeta.colors.cool.shade70,
),
),
),
),
),
if (_enableSearch)
Expanded(
child: _listView(context),
)
else
_listView(context),
Padding(
padding: const EdgeInsets.only(top: ZetaSpacing.b),
child: TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Close'),
),
child: items[index].child,
),
),
],
),
),
);
}

Widget _listView(BuildContext context) => ListView.builder(
shrinkWrap: true,
itemCount: _items.length,
itemBuilder: (_, index) => InkWell(
onTap: () {
Navigator.of(context).pop(_items[index].value);
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: ZetaSpacing.xs,
),
child: _items[index].child,
),
),
);
}

/// [CountriesMenuItem]
/// Item for the country selection dialog.
class CountriesMenuItem {
/// Constructor for [CountriesMenuItem].
const CountriesMenuItem({
required this.value,
required this.child,
});

/// The selected value from the list.
final Country value;

/// The widget which will represent each item in the list.
final Widget child;
}
13 changes: 10 additions & 3 deletions lib/src/components/phone_input/phone_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class ZetaPhoneInput extends StatefulWidget {
this.countryDialCode,
this.phoneNumber,
this.countries,
this.countrySearchHint,
this.useRootNavigator = true,
});

Expand Down Expand Up @@ -56,6 +57,10 @@ class ZetaPhoneInput extends StatefulWidget {
/// List of countries ISO 3166-1 alpha-2 codes
final List<String>? countries;

/// The hint to be shown inside the country search input field.
/// Default is `Search by name or dial code`.
final String? countrySearchHint;

/// Determines if the root navigator should be used in the [CountriesDialog].
final bool useRootNavigator;

Expand All @@ -75,7 +80,8 @@ class ZetaPhoneInput extends StatefulWidget {
..add(StringProperty('countryDialCode', countryDialCode))
..add(StringProperty('phoneNumber', phoneNumber))
..add(IterableProperty<String>('countries', countries))
..add(DiagnosticsProperty<bool>('useRootNavigator', useRootNavigator));
..add(DiagnosticsProperty<bool>('useRootNavigator', useRootNavigator))
..add(StringProperty('countrySearchHint', countrySearchHint));
}
}

Expand Down Expand Up @@ -173,10 +179,11 @@ class _ZetaPhoneInputState extends State<ZetaPhoneInput> {
left: BorderSide(color: zeta.colors.cool.shade40),
),
),
child: CountriesDialog<Country>(
child: CountriesDialog(
zeta: zeta,
useRootNavigator: widget.useRootNavigator,
enabled: widget.enabled,
searchHint: widget.countrySearchHint,
button: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expand All @@ -201,7 +208,7 @@ class _ZetaPhoneInputState extends State<ZetaPhoneInput> {
),
items: _countries
.map(
(country) => DropdownMenuItem<Country>(
(country) => CountriesMenuItem(
value: country,
child: Row(
children: [
Expand Down

0 comments on commit ebeaa90

Please sign in to comment.