Step-by-Step Guide to Creating a Custom PhoneEntry with Country Code Dropdown in .NET MAUI


A country code dropdown is a crucial component in mobile apps, especially when paired with a custom phone entry field for a seamless user experience. In this article, we'll create a custom country code dropdown with a customized entry field in .NET MAUI.
Features of Our Custom Dropdown π―
A custom design with Entry field displaying the country flag, code, and phone number input π±
A dropdown (Modal Page) to select the country code π½
A searchable country list for better usability π
Data binding using Bindable Properties π
Custom handlers to remove the entry border for Andorid, IOS, Mac and Windows π¨
Step 1: Write a custom handler to remove the border from the entry π
To ensure a clean design, we remove the default border from the Entry field using platform-specific handlers.
Android, IOS, Mac, Windows Entry Handler (MauiProgram.cs) π€π±π₯οΈπͺ
// Custom mapper for borderless entry
Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping(nameof(Entry), (handler, view) =>
{
#if IOS || MACCATALYST
handler.PlatformView.BackgroundColor = UIKit.UIColor.Clear;
handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.None;
#elif ANDROID
handler.PlatformView.BackgroundTintList = Android.Content.Res.ColorStateList.ValueOf(Microsoft.Maui.Graphics.Colors.Transparent.ToAndroid());
#elif WINDOWS
handler.PlatformView.BorderThickness = new Microsoft.UI.Xaml.Thickness(0);
handler.PlatformView.Background = null;
#endif
});
Step 2: Create the Custom Control for PhoneEntry βοΈ
In this step, you will create a custom control for the phone entry field. This control will display the country flag, code, and provide an input for the phone number, enhancing the user interface and experience.
CustomPhoneEntry.Xaml π±
<Grid xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PhoneEntryControl.Controls.CustomPhoneEntry"
xmlns:ios="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;assembly=Microsoft.Maui.Controls"
xmlns:ff="clr-namespace:FFImageLoading.Svg.Maui;assembly=FFImageLoading.Compat.Svg.Maui"
RowDefinitions="Auto"
ColumnDefinitions="Auto,*"
x:Name="this">
<Border Grid.Column="0" Stroke="#9CA0B1" StrokeShape="RoundRectangle 7" Padding="10,3">
<HorizontalStackLayout BindingContext="{Binding SelectedCountry, Source={x:Reference this}}" Spacing="10" VerticalOptions="Center" HorizontalOptions="Fill">
<!-- Flag -->
<ff:SvgCachedImage x:Name="imgFlag" Source="{Binding Image}"
HeightRequest="25" WidthRequest="40" Aspect="AspectFit" LoadingPlaceholder="imgloading.gif" DownsampleToViewSize="True"/>
<!-- Country Name -->
<Label x:Name="lblName" Text="{Binding Value}" VerticalOptions="Center"/>
<!-- DD Arrow -->
<Image Source="black_dropdown" Opacity=".5" HeightRequest="15" WidthRequest="15" VerticalOptions="Center"/>
</HorizontalStackLayout>
<Border.GestureRecognizers>
<TapGestureRecognizer Tapped="OpenCountiesCodeModel"/>
</Border.GestureRecognizers>
</Border>
<Border Grid.Column="1" Stroke="#9CA0B1" StrokeShape="RoundRectangle 7" Padding="10,3">
<Entry
x:Name="txtEntry"
Grid.Column="0"
ios:Entry.CursorColor="Black"
IsSpellCheckEnabled="False"
ClearButtonVisibility="WhileEditing"
IsTextPredictionEnabled="False"
Keyboard="{Binding Source={x:Reference this},Path= Keyboard}"
Text="{Binding Source={x:Reference this},Path= Text,Mode=TwoWay}"
</Entry.Behaviors>
</Entry>
</Border>
</Grid>
Now we need to create a bindable property for the list of countries that we will pass to generic dropdown page and selected country to display on the phone entry.
CustomPhoneEntry.cs
public partial class CustomPhoneEntry: Grid
{
public CustomPhoneEntry()
{
try
{
InitializeComponent();
}
catch { }
}
public static readonly BindableProperty SelectedCountryProperty =
BindableProperty.Create(nameof(SelectedCountry), typeof(Generic), typeof(CustomPhoneEntry), null, BindingMode.TwoWay);
public Generic SelectedCountry
{
get => (Generic)GetValue(SelectedCountryProperty);
set { SetValue(SelectedCountryProperty, value); }
}
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create(nameof(ItemsSource), typeof(ObservableCollection<Generic>), typeof(CustomPhoneEntry), new ObservableCollection<Generic>(), BindingMode.TwoWay);
public ObservableCollection<Generic> ItemsSource
{
get => (ObservableCollection<Generic>)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public static readonly BindableProperty TextProperty = BindableProperty.Create(
propertyName: nameof(Text),
returnType: typeof(string),
declaringType: typeof(CustomPhoneEntry),
defaultValue: null,
defaultBindingMode: BindingMode.TwoWay);
public string Text
{
get => (string)GetValue(TextProperty);
set { SetValue(TextProperty, value); }
}
private async Task OpenModal()
{
// Push the modal for selecting a country
var countrySelectionPage = new GenericDropdownPage(ItemsSource, SelectedCountry,"Search by code");
countrySelectionPage.CountrySelected += (s, selectedCountry) =>
{
SelectedCountry = selectedCountry;
};
await AppShell.Current.Navigation.PushModalAsync(countrySelectionPage);
}
async void OpenCountiesCodeModel(System.Object sender, Microsoft.Maui.Controls.TappedEventArgs e)
{
await OpenModal();
}
}
Step 3: Create the Custom Dropdown (Modal Page) π
A modal page can be used to display the country list:
GenericDropdownPage.Xaml
ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PhoneEntryControl.Controls.GenericDropdownPage"
xmlns:ff="clr-namespace:FFImageLoading.Svg.Maui;assembly=FFImageLoading.Compat.Svg.Maui"
Title="CountryCodeDropdown">
<Grid RowDefinitions="Auto,*" Padding="15" RowSpacing="15">
<!-- Search Bar -->
<Grid Grid.Row="0" ColumnDefinitions="Auto,*" ColumnSpacing="0">
<ImageButton Grid.Column="0" Source="left_arrow" Clicked="OnBackPressed"/>
<Border Grid.Column="1" BackgroundColor="#F0F0F0" Stroke="#E0E0E0" StrokeThickness="1" Style="{StaticResource RoundRecBorder}" Padding="5">
<Grid ColumnDefinitions="Auto,*" ColumnSpacing="10" VerticalOptions="Center">
<Image Source="search"
Grid.Column="0"
Margin="10,0,0,0"
WidthRequest="20"
HeightRequest="20"
VerticalOptions="Center" />
<Entry x:Name="txtSearch"
Placeholder="Search by name or code"
Grid.Column="1"
FontSize="16"
Text="{Binding SearchQuery, Mode=TwoWay}"
VerticalOptions="Center"
ClearButtonVisibility="WhileEditing"
TextChanged="OnSearchTextChanged"/>
</Grid>
</Border>
</Grid>
<!-- Dropdown Menu -->
<CollectionView x:Name="dropdownMenu"
Grid.Row="1"
ItemsSource="{Binding FilteredItemsSource}"
SelectionMode="Single"
SelectionChanged="OnSelectionChanged"
VerticalOptions="FillAndExpand">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="5"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Border Style="{StaticResource RoundRecBorder}">
<HorizontalStackLayout Spacing="10" VerticalOptions="Center" Padding="10">
<!-- Country Flag -->
<ff:SvgCachedImage Source="{Binding Image}"
WidthRequest="40"
HeightRequest="25"
Aspect="AspectFit"
LoadingPlaceholder="imgloading.gif" DownsampleToViewSize="True" />
<!-- Country Details -->
<Label Text="{Binding Value}" Style="{StaticResource SmallMediumHorizontalStart}" VerticalOptions="Center"/>
</HorizontalStackLayout>
</Border>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</ContentPage>
GenericDropdownPage.cs
public partial class GenericDropdownPage : ContentPage
{
public event EventHandler<Generic> CountrySelected;
public ObservableCollection<Generic> ItemsSource { get; set; }
public ObservableCollection<Generic> _cachedList;
public string SearchQuery { get; set; }
public GenericDropdownPage(ObservableCollection<Generic> itemsSource, Generic selectedCountry, string placeholder)
{
InitializeComponent();
txtSearch.Placeholder = placeholder;
ItemsSource = itemsSource;
dropdownMenu.ItemsSource = new ObservableCollection<Generic>(itemsSource);
_cachedList= new ObservableCollection<Generic>(itemsSource);
BindingContext = this;
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection.FirstOrDefault() is Generic selectedCountry)
{
CountrySelected?.Invoke(this, selectedCountry);
Navigation.PopModalAsync();
}
}
private void OnSearchTextChanged(object sender, TextChangedEventArgs e)
{
var query = e.NewTextValue?.ToLowerInvariant();
if (string.IsNullOrWhiteSpace(query))
{
// Reset to original list
dropdownMenu.ItemsSource = new ObservableCollection<Generic>(_cachedList);
}
else
{
// Filter from the master list (_allItems) instead of already filtered data
dropdownMenu.ItemsSource = new ObservableCollection<Generic>(
_cachedList.Where(c => c.Value.ToLowerInvariant().Contains(query))
);
}
}
private async void OnBackPressed(System.Object sender, System.EventArgs e)
{
await AppShell.Current.Navigation.PopModalAsync();
}
}
Finally, we are done with our dropdown. It's time to add it to a page and make it work .π₯
Finally, integrate the newly built control into any design and enjoy!
CreateAccountPage.Xaml
ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PhoneEntryControl.Views.Registeration.CreateAccountPage"
xmlns:customEntry="clr-namespace:PhoneEntryControl.Controls"
Title="CountryCodeDropdown">
<VerticalStackLayout>
<customEntry:CustomPhoneEntry Text="{Binding MobileNumber}" ItemsSource="{Binding CountriesList}" SelectedCountry="{Binding SelectedCountry}"/>
</VerticalStackLayout>
</ContentPage>
CreateAccountPage.cs
public partial class CreateAccountPage : ContentPage
{
public CreateAccountPage(CreateAccountViewModel createAccountViewModel)
{
try
{
InitializeComponent();
BindingContext = createAccountViewModel;
}
catch(Exception ex) { }
}
}
CreateAccountViewModel.cs
public class CreateAccountViewModel : ObservableObject
{
#region Properties
[ObservableProperty]
public Generic _selectedCountry;
[ObservableProperty]
public ObservableCollection<Generic> _countriesList;
[ObservableProperty]
public string _mobileNumber;
#endregion
public CountryPickerViewModel()
{
CountriesList = new ObservableCollection<Country>
{
new Country { Name = "Pakistan", Code = "+92", Flag = "pk" },
new Country { Name = "Saudia Arabia", Code = "+966", Flag = "sa" },
new Country { Name = "Turkey", Code = "+90", Flag = "tr" },
};
SelectedCountry = Countries.First();
}
}
Conclusion π―
By implementing a custom country code dropdown with an entry field, we enhance the user experience and provide a more intuitive phone number input system. This approach can be further customized based on specific app requirements.
Stay tuned for more awesome .NET MAUI tutorials.
Happy coding! ππ₯
Subscribe to my newsletter
Read articles from Ali Raza directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ali Raza
Ali Raza
π Tech Lead | .NET MAUI Expert | Mobile App Developer I'm Ali Raza, a passionate Tech Lead with over 6 years of experience in mobile app development. I specialize in .NET MAUI/Xamarin and have led multiple high-impact projects, including enterprise apps, fintech solutions, and eSIM technology. πΉ What I Do: β .NET MAUI & Xamarin β Building cross-platform apps with robust architectures β eSIM & Fintech Apps β Leading innovations in digital connectivity & finance β Performance Optimization β Creating high-quality, scalable mobile solutions β Mentorship & Community Sharing β Helping developers master .NET MAUI π’ Sharing Weekly Insights on .NET MAUI/Xamarin to help developers level up! Follow me for deep dives into Native Interop, API Optimization, and Advanced UI/UX techniques. Letβs connect and build something amazing! π