-
Notifications
You must be signed in to change notification settings - Fork 124
Open
Description
I am trying to preload all the SVGs inside this package to make them available offline. To do so, I went through the documentation of the circle_flags package, which introduced me to the preload function as below:
CircleFlag.preload(['fr', 'us']);
I have had a look at this function under the hood, it is using the same methodology as I am to precache my local SVGs and make them available offline. So, I decided to:
- Use this function in the
build
method ofcountry_button.dart
first. I moved the example here inside a dialog. Then when the app compiled, I right clicked the screen, clicked the Network tab, and then set the throttling to 'Offline'. Upon clicking the button to show the dialog, I could see that the US flag SVG was not rendering at all, indicating that the preload function had not worked properly. - I decided to create an
assets/svgs
folder at the same level as thelib
folder and moved all the SVGs inside circle_flags to this folder. I then decided to add a class with an initialization function that would precache these local SVGs. However, that approach has not worked either. - I then decided to add a class with an initialization function in
lib/src/phone_form_field.dart
which was supposed to preload the flags usingCircleFlag.preload
as below:
library phone_number_input;
export 'src/phone_form_field.dart';
export 'src/country_selector_navigator.dart';
export 'src/country_button.dart';
export 'src/country_button_style.dart';
export 'src/validation/phone_validator.dart';
export 'src/localization/localization.dart';
export 'package:phone_numbers_parser/phone_numbers_parser.dart'
show PhoneNumber, PhoneNumberType, IsoCode;
import 'package:circle_flags/circle_flags.dart';
/// Initialization class for the phone_form_field package
class PhoneNumberInputPackage {
static bool _initialized = false;
/// Initialize the package by preloading country flag assets
/// Call this in your app's main() function before runApp()
static Future<void> initialize() async {
if (_initialized) return;
print('Initializing phone_form_field package - preloading country flags...');
await _preloadCountryFlags();
_initialized = true;
print('phone_form_field package initialization complete');
}
static Future<void> _preloadCountryFlags() async {
try {
CircleFlag.preload(['fr', 'us']);
CircleFlag.cache;
print('CircleFlag preloaded France and US flags');
} catch (e) {
print('Failed to preload CircleFlag flags: $e');
}
}
}
Strangely, this has yielded no results either, as clicking the dialog when offline doesn't render any of the SVGs I need. I would appreciate any help that can be provided to help me accomplish this task based on point 3.
The example code I am using as given below:
import 'package:flutter/material.dart';
import 'package:phone_form_field/phone_form_field.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize the package to preload SVGs
await PhoneNumberInputPackage.initialize();
runApp(const MyApp());
}
// this example makes uses of lots of properties that would not be there
// in a real scenario for the sake of showing the features.
// For a simpler example see the README
class PhoneFieldView extends StatelessWidget {
static const supportedLocales = [
Locale('ar'),
// not supported by material yet
// Locale('ckb'),
Locale('de'),
Locale('el'),
Locale('en'),
Locale('es'),
Locale('fa'),
Locale('fr'),
Locale('hi'),
Locale('hu'),
Locale('it'),
// not supported by material yet
// Locale('ku'),
Locale('nb'),
Locale('nl'),
Locale('pt'),
Locale('ru'),
Locale('sv'),
Locale('tr'),
Locale('uz'),
Locale('zh'),
// ...
];
final PhoneController controller;
final FocusNode focusNode;
final CountrySelectorNavigator selectorNavigator;
final bool withLabel;
final bool outlineBorder;
final bool isCountryButtonPersistant;
final bool mobileOnly;
final Locale locale;
const PhoneFieldView({
Key? key,
required this.controller,
required this.focusNode,
required this.selectorNavigator,
required this.withLabel,
required this.outlineBorder,
required this.isCountryButtonPersistant,
required this.mobileOnly,
required this.locale,
}) : super(key: key);
PhoneNumberInputValidator? _getValidator(BuildContext context) {
List<PhoneNumberInputValidator> validators = [];
if (mobileOnly) {
validators.add(PhoneValidator.validMobile(context));
} else {
validators.add(PhoneValidator.valid(context));
}
return validators.isNotEmpty ? PhoneValidator.compose(validators) : null;
}
@override
Widget build(BuildContext context) {
return AutofillGroup(
child: Localizations.override(
context: context,
locale: locale,
child: Builder(
builder: (context) {
final label = PhoneFieldLocalization.of(context).phoneNumber;
return PhoneFormField(
focusNode: focusNode,
controller: controller,
isCountryButtonPersistent: isCountryButtonPersistant,
autofocus: false,
autofillHints: const [AutofillHints.telephoneNumber],
countrySelectorNavigator: selectorNavigator,
decoration: InputDecoration(
label: withLabel ? Text(label) : null,
border: outlineBorder
? OutlineInputBorder(
borderRadius: BorderRadius.circular(50))
: const UnderlineInputBorder(),
hintText: withLabel ? '' : label,
contentPadding: const EdgeInsets.all(0)),
enabled: true,
countryButtonStyle: CountryButtonStyle(
showFlag: true,
showIsoCode: false,
showDialCode: true,
showDropdownIcon: true,
borderRadius: BorderRadius.circular(50)),
validator: _getValidator(context),
autovalidateMode: AutovalidateMode.onUserInteraction,
cursorColor: Theme.of(context).colorScheme.primary,
// ignore: avoid_print
onSaved: (p) => print('saved $p'),
// ignore: avoid_print
onChanged: (p) => print('changed $p'),
);
},
),
),
);
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: PhoneFieldLocalization.delegates,
supportedLocales: PhoneFieldView.supportedLocales,
locale: const Locale('en'),
title: 'Phone field demo',
theme: ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.blue,
),
home: const PhoneFormFieldScreen(),
);
}
}
class PhoneFormFieldScreen extends StatefulWidget {
const PhoneFormFieldScreen({Key? key}) : super(key: key);
@override
PhoneFormFieldScreenState createState() => PhoneFormFieldScreenState();
}
class PhoneFormFieldScreenState extends State<PhoneFormFieldScreen> {
late PhoneController controller;
final FocusNode focusNode = FocusNode();
bool outlineBorder = true;
bool mobileOnly = true;
bool isCountryButtonPersistent = true;
bool withLabel = true;
CountrySelectorNavigator selectorNavigator =
const CountrySelectorNavigator.page();
Locale locale = const Locale('en');
final formKey = GlobalKey<FormState>();
@override
initState() {
super.initState();
controller = PhoneController();
controller.addListener(() => setState(() {}));
}
@override
void dispose() {
super.dispose();
controller.dispose();
}
void _showPhoneDialog() {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext dialogContext) {
return StatefulBuilder(
builder: (context, setDialogState) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Container(
width: MediaQuery.of(context).size.width * 0.9,
constraints: const BoxConstraints(maxWidth: 500, maxHeight: 600),
padding: const EdgeInsets.all(24.0),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Dialog Header
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Enter Phone Number',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
IconButton(
onPressed: () => Navigator.of(dialogContext).pop(),
icon: const Icon(Icons.close),
),
],
),
const SizedBox(height: 20),
// Phone Field
Form(
key: formKey,
child: PhoneFieldView(
controller: controller,
focusNode: focusNode,
selectorNavigator: selectorNavigator,
withLabel: withLabel,
outlineBorder: outlineBorder,
isCountryButtonPersistant: isCountryButtonPersistent,
mobileOnly: mobileOnly,
locale: locale,
),
),
const SizedBox(height: 16),
// Phone value display
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: Theme.of(context).colorScheme.outline,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Phone Number Details:',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
),
const SizedBox(height: 8),
Text('Value: ${controller.value.toString()}'),
Text('Valid mobile: ${controller.value.isValid(type: PhoneNumberType.mobile)}'),
Text('Valid fixed line: ${controller.value.isValid(type: PhoneNumberType.fixedLine)}'),
],
),
),
const SizedBox(height: 20),
// Action buttons
Wrap(
spacing: 8,
runSpacing: 8,
children: [
ElevatedButton(
onPressed: () {
formKey.currentState?.reset();
setDialogState(() {}); // Update dialog state
},
child: const Text('Reset'),
),
ElevatedButton(
onPressed: () {
controller.selectNationalNumber();
focusNode.requestFocus();
},
child: const Text('Select Number'),
),
ElevatedButton(
onPressed: () {
controller.value = PhoneNumber.parse('+33 699 999 999');
setDialogState(() {}); // Update dialog state
},
child: const Text('Set Example'),
),
],
),
const SizedBox(height: 20),
// Save/Cancel buttons
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Navigator.of(dialogContext).pop(),
child: const Text('Cancel'),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: () {
// Handle save/submit action
if (controller.value.isValid()) {
print('Phone number saved: ${controller.value}');
Navigator.of(dialogContext).pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Phone number saved: ${controller.value}'),
backgroundColor: Colors.green,
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please enter a valid phone number'),
backgroundColor: Colors.red,
),
);
}
},
child: const Text('Save'),
),
],
),
],
),
),
),
);
},
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Phone_form_field'),
),
body: SingleChildScrollView(
child: Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 600),
child: Card(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
// Title
const Text(
'Phone Form Field Configuration',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
// Configuration switches
SwitchListTile(
value: outlineBorder,
onChanged: (v) => setState(() => outlineBorder = v),
title: const Text('Outlined border'),
),
SwitchListTile(
value: withLabel,
onChanged: (v) => setState(() => withLabel = v),
title: const Text('Label'),
),
SwitchListTile(
value: isCountryButtonPersistent,
onChanged: (v) =>
setState(() => isCountryButtonPersistent = v),
title: const Text('Persistent country chip'),
),
SwitchListTile(
value: mobileOnly,
onChanged: (v) => setState(() => mobileOnly = v),
title: const Text('Mobile phone number only'),
),
const Divider(),
// Language selector
ListTile(
title: const Text('Language'),
trailing: DropdownButton<Locale>(
value: locale,
onChanged: (Locale? value) {
if (value != null) {
setState(() => locale = value);
}
},
items: [
for (final locale in PhoneFieldView.supportedLocales)
DropdownMenuItem(
value: locale,
child: Text(locale.toLanguageTag()),
),
],
),
),
// Country selector navigator
ListTile(
title: const Text('Country selector'),
trailing: DropdownButton<CountrySelectorNavigator>(
value: selectorNavigator,
onChanged: (CountrySelectorNavigator? value) {
if (value != null) {
setState(() => selectorNavigator = value);
}
},
items: const [
DropdownMenuItem(
value: CountrySelectorNavigator.bottomSheet(
favorites: [IsoCode.GU, IsoCode.GY]),
child: Text('Bottom sheet'),
),
DropdownMenuItem(
value: CountrySelectorNavigator.draggableBottomSheet(),
child: Text('Draggable modal sheet'),
),
DropdownMenuItem(
value: CountrySelectorNavigator.modalBottomSheet(),
child: Text('Modal sheet'),
),
DropdownMenuItem(
value: CountrySelectorNavigator.dialog(width: 720),
child: Text('Dialog'),
),
DropdownMenuItem(
value: CountrySelectorNavigator.page(),
child: Text('Page'),
),
],
),
),
const SizedBox(height: 30),
// Button to open dialog
Container(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: _showPhoneDialog,
icon: const Icon(Icons.phone, size: 24),
label: const Text(
'Open Phone Number Dialog',
style: TextStyle(fontSize: 16),
),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 16,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
const SizedBox(height: 20),
// Current phone number display
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Theme.of(context).colorScheme.outline,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Current Phone Number:',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Theme.of(context).colorScheme.primary,
),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Value: ${controller.value.toString()}',
style: const TextStyle(fontFamily: 'monospace'),
),
const SizedBox(height: 4),
Row(
children: [
Icon(
controller.value.isValid(type: PhoneNumberType.mobile)
? Icons.check_circle
: Icons.cancel,
size: 16,
color: controller.value.isValid(type: PhoneNumberType.mobile)
? Colors.green
: Colors.red,
),
const SizedBox(width: 4),
Text('Valid mobile: ${controller.value.isValid(type: PhoneNumberType.mobile)}'),
],
),
const SizedBox(height: 4),
Row(
children: [
Icon(
controller.value.isValid(type: PhoneNumberType.fixedLine)
? Icons.check_circle
: Icons.cancel,
size: 16,
color: controller.value.isValid(type: PhoneNumberType.fixedLine)
? Colors.green
: Colors.red,
),
const SizedBox(width: 4),
Text('Valid fixed line: ${controller.value.isValid(type: PhoneNumberType.fixedLine)}'),
],
),
],
),
),
],
),
),
],
),
),
),
),
),
),
);
}
}
Metadata
Metadata
Assignees
Labels
No labels