Last active
May 15, 2025 04:37
-
-
Save md-riaz/e5d81784528f5b978d14bcfcb4468cba to your computer and use it in GitHub Desktop.
How to use multiple phone number in flutter using texteditor controller
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'dart:convert'; | |
void main() { | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Contact Phone Input Demo', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: const ContactInputScreen(), | |
debugShowCheckedModeBanner: false, | |
); | |
} | |
} | |
class ContactInputScreen extends StatefulWidget { | |
const ContactInputScreen({super.key}); | |
@override | |
State<ContactInputScreen> createState() => _ContactInputScreenState(); | |
} | |
class _ContactInputScreenState extends State<ContactInputScreen> { | |
// GlobalKey for the Form to manage its state and validation | |
final _formKey = GlobalKey<FormState>(); | |
// Controller for the contact name input field | |
late TextEditingController _contactNameController; | |
// List to store phone number entries | |
List<PhoneEntry> phoneNumbers = []; | |
// Available phone types | |
final List<PhoneType> phoneTypes = [ | |
PhoneType.mobile, | |
PhoneType.home, | |
PhoneType.work, | |
PhoneType.other, | |
]; | |
@override | |
void initState() { | |
super.initState(); | |
// Initialize the contact name controller | |
_contactNameController = TextEditingController(); | |
// Add an initial phone number entry when the widget is initialized | |
addPhoneNumber(); | |
} | |
@override | |
void dispose() { | |
// Dispose of the contact name controller to prevent memory leaks | |
_contactNameController.dispose(); | |
super.dispose(); | |
} | |
// Function to add a new phone number entry | |
void addPhoneNumber() { | |
setState(() { | |
// If the phone numbers list is empty, create the first entry and set it as primary | |
if (phoneNumbers.isEmpty) { | |
phoneNumbers.add(PhoneEntry( | |
number: '', | |
isPrimary: true, | |
phoneType: phoneTypes.first, | |
controller: TextEditingController())); | |
} else { | |
// Otherwise, create a new entry and set it as not primary | |
phoneNumbers.add(PhoneEntry( | |
number: '', | |
isPrimary: false, | |
phoneType: phoneTypes.first, | |
controller: TextEditingController())); | |
} | |
}); | |
} | |
// Function to update a phone number in the list | |
void updatePhoneNumber(int index, String number) { | |
setState(() { | |
// Create a new PhoneEntry object with the updated number and replace the old one in the list | |
phoneNumbers[index] = phoneNumbers[index].copyWith(number: number); | |
}); | |
} | |
// Function to update the phone type of a phone number entry | |
void updatePhoneType(int index, PhoneType? phoneType) { | |
// If the phoneType is null, return early | |
if (phoneType == null) { | |
return; | |
} | |
setState(() { | |
// Create a new PhoneEntry object with the updated phoneType and replace the old one in the list | |
phoneNumbers[index] = phoneNumbers[index].copyWith(phoneType: phoneType); | |
}); | |
} | |
// Function to set a phone number as the primary number | |
void setPrimaryNumber(int index) { | |
setState(() { | |
// Iterate over the phone numbers list and set the isPrimary flag to true for the selected index and false for the rest | |
for (int i = 0; i < phoneNumbers.length; i++) { | |
phoneNumbers[i] = phoneNumbers[i].copyWith(isPrimary: i == index); | |
} | |
}); | |
} | |
// Function to delete a phone number entry | |
void deletePhoneNumber(int index) { | |
setState(() { | |
// Remove the phone number entry at the specified index | |
phoneNumbers.removeAt(index); | |
// If the phone numbers list is empty, add a new entry to ensure at least one phone number exists | |
if (phoneNumbers.isEmpty) { | |
addPhoneNumber(); | |
} else if (phoneNumbers.every((element) => !element.isPrimary)) { | |
// If no phone number is primary, set the first one as primary | |
setPrimaryNumber(0); | |
} | |
}); | |
} | |
// Function to convert the contact data to JSON format | |
String toJson() { | |
// Map the phoneNumbers list to a list of JSON objects | |
final numbersJson = phoneNumbers | |
.map((entry) => { | |
'number': entry.number, | |
'is_primary': entry.isPrimary ? 1 : 0, | |
'phone_type': entry.phoneType.index, | |
}) | |
.toList(); | |
// Create a JSON object with the contact name and phone numbers | |
final json = { | |
'name': _contactNameController.text, | |
'numbers': numbersJson, | |
}; | |
// Encode the JSON object to a string | |
return jsonEncode(json); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text('Contact Phone Numbers'), | |
), | |
body: Form( | |
key: _formKey, | |
child: ListView( | |
children: [ | |
// Input field for contact name | |
Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: TextFormField( | |
controller: _contactNameController, | |
decoration: const InputDecoration(labelText: 'Contact Name'), | |
validator: (value) { | |
if (value == null || value.isEmpty) { | |
return 'Please enter a name'; | |
} | |
return null; | |
}, | |
), | |
), | |
// Map the phoneNumbers list to a list of PhoneEntryWidget widgets | |
...phoneNumbers.asMap().entries.map((entry) { | |
int index = entry.key; | |
PhoneEntry phoneEntry = entry.value; | |
return PhoneEntryWidget( | |
index: index, | |
phoneEntry: phoneEntry, | |
updatePhoneNumber: updatePhoneNumber, | |
updatePhoneType: updatePhoneType, | |
setPrimaryNumber: setPrimaryNumber, | |
deletePhoneNumber: deletePhoneNumber, | |
phoneTypes: phoneTypes, | |
key: Key(index.toString()), // Provide a unique key for each PhoneEntryWidget | |
); | |
}).toList(), | |
// Button to add a new phone number entry | |
Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: ElevatedButton( | |
onPressed: () { | |
addPhoneNumber(); | |
}, | |
child: const Text('Add Phone Number'), | |
), | |
), | |
// Button to save the contact | |
Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: ElevatedButton( | |
onPressed: () { | |
// Validate the form | |
if (_formKey.currentState!.validate()) { | |
// Show a snackbar with the contact data in JSON format | |
ScaffoldMessenger.of(context).showSnackBar( | |
SnackBar(content: Text('Saving Contact: ${toJson()}')), | |
); | |
} | |
}, | |
child: const Text('Save Contact'), | |
), | |
), | |
], | |
), | |
), | |
); | |
} | |
} | |
// Enum to represent phone types | |
enum PhoneType { | |
mobile, | |
home, | |
work, | |
other, | |
} | |
// Data model for a phone number entry | |
class PhoneEntry { | |
final String number; | |
final bool isPrimary; | |
final PhoneType phoneType; | |
final TextEditingController controller; | |
PhoneEntry({ | |
required this.number, | |
required this.isPrimary, | |
required this.phoneType, | |
required this.controller, | |
}); | |
// Method to create a copy of the PhoneEntry object with updated values | |
PhoneEntry copyWith( | |
{String? number, bool? isPrimary, PhoneType? phoneType}) { | |
return PhoneEntry( | |
number: number ?? this.number, | |
isPrimary: isPrimary ?? this.isPrimary, | |
phoneType: phoneType ?? this.phoneType, | |
controller: controller, | |
); | |
} | |
} | |
// Widget to display and edit a phone number entry | |
class PhoneEntryWidget extends StatefulWidget { | |
final int index; | |
final PhoneEntry phoneEntry; | |
final Function(int, String) updatePhoneNumber; | |
final Function(int, PhoneType?) updatePhoneType; | |
final Function(int) setPrimaryNumber; | |
final Function(int) deletePhoneNumber; | |
final List<PhoneType> phoneTypes; | |
const PhoneEntryWidget({ | |
required Key key, | |
required this.index, | |
required this.phoneEntry, | |
required this.updatePhoneNumber, | |
required this.updatePhoneType, | |
required this.setPrimaryNumber, | |
required this.deletePhoneNumber, | |
required this.phoneTypes, | |
}) : super(key: key); | |
@override | |
State<PhoneEntryWidget> createState() => _PhoneEntryWidgetState(); | |
} | |
class _PhoneEntryWidgetState extends State<PhoneEntryWidget> { | |
@override | |
Widget build(BuildContext context) { | |
return Card( | |
margin: const EdgeInsets.all(8.0), | |
child: Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
Row( | |
children: [ | |
Expanded( | |
child: TextFormField( | |
controller: widget.phoneEntry.controller, | |
decoration: const InputDecoration(labelText: 'Phone Number'), | |
keyboardType: TextInputType.phone, | |
validator: (value) { | |
if (value == null || value.isEmpty) { | |
return 'Please enter a phone number'; | |
} | |
return null; | |
}, | |
onChanged: (text) { | |
widget.updatePhoneNumber(widget.index, text); | |
}, | |
), | |
), | |
const SizedBox(width: 8.0), | |
// Dropdown button to select the phone type | |
DropdownButton<PhoneType>( | |
value: widget.phoneEntry.phoneType, | |
items: widget.phoneTypes.map((PhoneType type) { | |
return DropdownMenuItem<PhoneType>( | |
value: type, | |
child: Text(type.toString().split('.').last), | |
); | |
}).toList(), | |
onChanged: (PhoneType? newValue) { | |
widget.updatePhoneType(widget.index, newValue); | |
}, | |
), | |
], | |
), | |
Row( | |
mainAxisAlignment: MainAxisAlignment.spaceBetween, | |
children: [ | |
Row( | |
children: [ | |
const Text('Primary: '), | |
// Radio button to select the primary phone number | |
Radio<bool>( | |
value: true, | |
groupValue: widget.phoneEntry.isPrimary, | |
onChanged: (bool? value) { | |
if (value != null && value) { | |
widget.setPrimaryNumber(widget.index); | |
} | |
}, | |
), | |
], | |
), | |
// Icon button to delete the phone number entry | |
IconButton( | |
icon: const Icon(Icons.delete), | |
onPressed: () { | |
widget.deletePhoneNumber(widget.index); | |
}, | |
), | |
], | |
), | |
], | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment