Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Controller select all #72

Merged
merged 10 commits into from
Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## [4.3.0] 16 / 10 / 2021
## [4.3.0] 18 / 10 / 2021
- Added most of textfield params to the phone input.
- Added method to select the current national number from the controller
- Changed how controllers worked under the hood
- Fix an issue where a phone number could not start with its country code
- uses phone_numbers_parser v4.1.0


## [4.2.0] 16 / 10 / 2021
- [deprecated] PhoneValidator.invalid in favor of PhoneValidator.valid as the naming did not make sens and was backward.
Expand Down
30 changes: 21 additions & 9 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -227,22 +227,34 @@ class _PhoneFormFieldScreenState extends State<PhoneFormFieldScreen> {
),
),
SizedBox(
height: 40,
height: 12,
),
Text(controller.value.toString()),
Text('is valid mobile number '
'${controller.value?.validate(type: PhoneNumberType.mobile) ?? 'false'}'),
Text(
'is valid fixed line number ${controller.value?.validate(type: PhoneNumberType.fixedLine) ?? 'false'}'),
SizedBox(height: 12),
ElevatedButton(
onPressed: controller.value == null
? null
: () => formKey.currentState?.reset(),
: () => controller.reset(),
child: Text('reset'),
),
SizedBox(
height: 40,
SizedBox(height: 12),
ElevatedButton(
onPressed: () => controller.selectNationalNumber(),
child: Text('Select national number'),
),
SizedBox(height: 12),
ElevatedButton(
onPressed: () =>
controller.value = PhoneNumber.fromIsoCode(
'fr',
'699999999',
),
child: Text('Set +33 699 999 999'),
),
Text(controller.value.toString()),
Text('is valid mobile number '
'${controller.value?.validate(type: PhoneNumberType.mobile) ?? 'false'}'),
Text(
'is valid fixed line number ${controller.value?.validate(type: PhoneNumberType.fixedLine) ?? 'false'}'),
],
),
),
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ packages:
name: phone_numbers_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.1"
version: "4.1.0"
sky_engine:
dependency: transitive
description: flutter
Expand Down
2 changes: 1 addition & 1 deletion lib/phone_form_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export 'src/validator/phone_validator.dart';
export 'l10n/generated/phone_field_localization.dart';

export 'src/models/selector_config.dart';
export 'src/models/phone_controller.dart';
export 'src/models/phone_form_field_controller.dart';
export 'src/models/country.dart';
export 'src/models/all_countries.dart';

Expand Down
3 changes: 2 additions & 1 deletion lib/src/models/country.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ class Country {
/// returns "+ [countryCode]"
String get displayCountryCode => '+ $countryCode';

Country(this.isoCode) : assert(isoCodes.contains(isoCode));
Country(this.isoCode)
: assert(isoCodes.contains(isoCode), 'isocode $isoCode not found');

@override
bool operator ==(Object other) =>
Expand Down
7 changes: 0 additions & 7 deletions lib/src/models/phone_controller.dart

This file was deleted.

48 changes: 48 additions & 0 deletions lib/src/models/phone_field_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'package:flutter/material.dart';

class PhoneFieldController extends ChangeNotifier {
late final ValueNotifier<String?> isoCodeController;
late final TextEditingController nationalController;
final String defaultIsoCode;

/// focus node of the national number
final FocusNode focusNode;

String? get isoCode => isoCodeController.value;
String? get national =>
nationalController.text.isEmpty ? null : nationalController.text;
set isoCode(String? isoCode) => isoCodeController.value = isoCode;
set national(String? national) => nationalController.value = TextEditingValue(
text: national ?? '',
selection: TextSelection.fromPosition(
TextPosition(offset: national?.length ?? 0),
),
);

PhoneFieldController({
required String? national,
required String? isoCode,
required this.defaultIsoCode,
required this.focusNode,
}) {
isoCodeController = ValueNotifier(isoCode);
nationalController = TextEditingController(text: national);
isoCodeController.addListener(notifyListeners);
nationalController.addListener(notifyListeners);
}

selectNationalNumber() {
nationalController.selection = TextSelection(
baseOffset: 0,
extentOffset: nationalController.value.text.length,
);
focusNode.requestFocus();
}

@override
void dispose() {
isoCodeController.dispose();
nationalController.dispose();
super.dispose();
}
}
27 changes: 27 additions & 0 deletions lib/src/models/phone_form_field_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:phone_form_field/phone_form_field.dart';

class PhoneController extends ValueNotifier<PhoneNumber?> {
final PhoneNumber? initialValue;
// when we want to select the national number
final StreamController _selectionRequest$ = StreamController();
Stream get selectionRequest$ => _selectionRequest$.stream;

PhoneController(this.initialValue) : super(initialValue);

selectNationalNumber() {
_selectionRequest$.add(null);
}

reset() {
value = null;
}

@override
void dispose() {
_selectionRequest$.close();
super.dispose();
}
}
64 changes: 13 additions & 51 deletions lib/src/widgets/phone_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:phone_form_field/src/constants/constants.dart';
import 'package:phone_form_field/src/models/simple_phone_number.dart';
import 'package:phone_form_field/src/models/phone_field_controller.dart';
import 'package:phone_form_field/src/widgets/measure_initial_size.dart';

import '../../phone_form_field.dart';
Expand All @@ -15,12 +15,10 @@ import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle;
///
/// This deals with mostly UI and has no dependency on any phone parser library
class PhoneField extends StatefulWidget {
final ValueNotifier<SimplePhoneNumber?> controller;
final PhoneFieldController controller;
final bool showFlagInInput;
final String defaultCountry;
final String? errorText;
final double flagSize;
final FocusNode? focusNode;
final InputDecoration decoration;

/// configures the way the country picker selector is shown
Expand Down Expand Up @@ -69,12 +67,10 @@ class PhoneField extends StatefulWidget {
// form field params
Key? key,
required this.controller,
required this.defaultCountry,
required this.showFlagInInput,
required this.selectorNavigator,
required this.flagSize,
required this.errorText,
required this.focusNode,
required this.decoration,
// textfield inputs
required this.keyboardType,
Expand Down Expand Up @@ -120,66 +116,34 @@ class PhoneField extends StatefulWidget {
}

class _PhoneFieldState extends State<PhoneField> {
late final FocusNode _focusNode;
Size? _size;

/// this is the controller for the national phone number
late TextEditingController _nationalNumberController;

bool get _isOutlineBorder => widget.decoration.border is OutlineInputBorder;

SimplePhoneNumber? get value => widget.controller.value;
String get _isoCode => value?.isoCode ?? widget.defaultCountry;

PhoneFieldController get controller => widget.controller;
_PhoneFieldState();

@override
void initState() {
_focusNode = widget.focusNode ?? FocusNode();
_nationalNumberController = TextEditingController(text: value?.national);
widget.controller.addListener(() => _updateValue(widget.controller.value));
_focusNode.addListener(() => setState(() {}));
controller.focusNode.addListener(onFocusChange);
super.initState();
}

/// to update the current value of the input
void _updateValue(SimplePhoneNumber? phoneNumber) async {
final national = phoneNumber?.national ?? '';
// if the national number has changed from outside we need to update
// the controller value
if (national != _nationalNumberController.text) {
// we need to use a future here because when resetting
// there is a race condition between the focus out event (clicking on reset)
// which updates the value to the current one without text selection
// and the actual reset
await Future.value();
_nationalNumberController.value = TextEditingValue(
text: national,
selection: TextSelection.fromPosition(
TextPosition(offset: national.length),
),
);
}
// when updating from within
if (widget.controller.value != phoneNumber) {
widget.controller.value = phoneNumber;
}
void onFocusChange() {
setState(() {});
}

@override
void dispose() {
_focusNode.dispose();
_nationalNumberController.dispose();
controller.focusNode.removeListener(onFocusChange);
super.dispose();
}

void selectCountry() async {
final selected = await widget.selectorNavigator.navigate(context);
if (selected != null) {
_updateValue(SimplePhoneNumber(
isoCode: selected.isoCode, national: value?.national ?? ''));
controller.isoCode = selected.isoCode;
}
_focusNode.requestFocus();
controller.focusNode.requestFocus();
}

Widget build(BuildContext context) {
Expand All @@ -193,18 +157,16 @@ class _PhoneFieldState extends State<PhoneField> {
onSizeFound: (size) => setState(() => _size = size),
child: _textField(),
),
if (_focusNode.hasFocus || _nationalNumberController.text.isNotEmpty)
if (controller.focusNode.hasFocus || controller.national != null)
_inkWellOverlay(),
],
);
}

Widget _textField() {
return TextField(
focusNode: _focusNode,
controller: _nationalNumberController,
onChanged: (national) => _updateValue(
SimplePhoneNumber(isoCode: _isoCode, national: national)),
focusNode: controller.focusNode,
controller: controller.nationalController,
enabled: widget.enabled,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(
Expand Down Expand Up @@ -285,7 +247,7 @@ class _PhoneFieldState extends State<PhoneField> {
visible: visible,
child: CountryCodeChip(
key: visible ? Key('country-code-chip') : null,
country: Country(_isoCode),
country: Country(controller.isoCode ?? controller.defaultIsoCode),
showFlag: widget.showFlagInInput,
textStyle: TextStyle(
fontSize: 16,
Expand Down
Loading