Skip to content

Instantly share code, notes, and snippets.

@md-riaz
Last active May 15, 2025 04:37
Show Gist options
  • Save md-riaz/e5d81784528f5b978d14bcfcb4468cba to your computer and use it in GitHub Desktop.
Save md-riaz/e5d81784528f5b978d14bcfcb4468cba to your computer and use it in GitHub Desktop.
How to use multiple phone number in flutter using texteditor controller
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