country_kit

Country data + flag widgets for Flutter. Everything you need to build phone country pickers, country selectors, currency lists — without shipping assets you don't use.
- 250 countries with name, official name, native name, ISO 3166-1 alpha-2/alpha-3/numeric codes, dial code, currency (code, name, symbol), continent, subregion, capital, and languages
- Localized names in 40 languages (CLDR data), each an opt-in import so
unused languages are tree-shaken away:
country.nameIn('fr')→Algérie - Phone metadata per country: example number, min/max length, and
display masks like
### ## ## ##(derived from Google's libphonenumber — including the alternate formats for the 183 countries that group numbers differently by leading digits), plus aTextInputFormatterthat applies the right mask as the user types - Search that matches how people type: diacritic-insensitive
(
espanafinds España) and ranked by relevance, not alphabetically - Flags in 3 shapes — rectangle (3:2), square, circle — as SVGs in optional per-shape asset packages, plus emoji flags at zero asset cost
- Extra flags: Scotland, Wales, EU, UN, Canary Islands, and more — with
names and their own search (
SpecialFlags.search('scotland'))
Install
dependencies:
country_kit: ^1.0.0
# Add ONLY the flag shapes you render (~1 MB each). None needed for
# emoji flags.
country_kit_flags_circle: ^1.0.0
country_kit_flags_rectangle: ^1.0.0
country_kit_flags_square: ^1.0.0
Each shape lives in its own asset package so your app never bundles shapes it
doesn't use. If a shape package is missing, CountryFlag automatically falls
back to the emoji flag.
Country data
import 'package:country_kit/country_kit.dart';
final dz = Countries.byAlpha2('DZ')!;
dz.name; // Algeria
dz.nativeName; // الجزائر
dz.dialCode; // +213
dz.currencyCode; // DZD
dz.continent; // Continent.africa
dz.flagEmoji; // 🇩🇿
dz.phoneMask; // ### ## ## ##
dz.phoneExample; // 551234567
Countries.all; // all countries, name-sorted
Countries.byAlpha3('DZA');
Countries.byDialCode('+1'); // [US, CA, ...] — shared codes return all
Countries.byName('algeria');
Countries.byContinent(Continent.africa);
Countries.search('alger'); // matches names, codes, dial codes
Countries.ofLocale(locale); // from a Locale's country code
Countries.current; // from the device locale — great picker default
Localized names
Localized names ship as one generated library per language (40 languages, CLDR data). Import the ones you render and register them once — languages you don't import are tree-shaken out of the binary:
import 'package:country_kit/l10n/fr.dart';
void main() {
registerCountryNamesFr();
runApp(const MyApp());
}
dz.nameIn('fr'); // Algérie (English name before registration)
Registered names also feed Countries.search, so French users typing
allemagne find Germany. Countries.registerTranslations accepts your own
{alpha2: name} maps too — handy for overrides or an unshipped language.
Search
Countries.search is diacritic-insensitive and ranked by relevance, so
results arrive in the order a user expects rather than alphabetically.
Countries.search('espana'); // España — accents optional
Countries.search('turkiye'); // Türkiye
Countries.search('US').first; // United States, not a substring match
Countries.search('ind').first;// India, ahead of British Indian Ocean Territory
Countries.search('213'); // Algeria first, then partial dial matches
Countries.search('burma'); // Myanmar — common alternate names resolve too
foldDiacritics is exported if you want the same folding to highlight
matches in your own list tiles.
Flags
CountryFlag(country: dz, shape: FlagShape.circle, size: 32)
CountryFlag(country: dz, shape: FlagShape.rectangle,
borderRadius: BorderRadius.circular(4))
CountryFlag.fromCode('GB-SCT', shape: FlagShape.square) // extra flags
CountryFlag.emoji(dz, size: 32) // no assets needed
size is the flag height; rectangles are 1.5 × size wide.
Fallback chain: bundled SVG → parent-territory SVG (e.g. Réunion → France) →
emoji flag → placeholder with the code. Emoji rendering is platform
dependent (Windows shows letter pairs). Kosovo (XK) has no SVG in the
source icon set and renders via the fallback chain.
The placeholder tints itself from the ambient DefaultTextStyle, falling
back to the platform brightness, so it stays legible in dark mode. Pass
placeholderBuilder (or loadingBuilder, for the frame before assets
resolve) to take over completely:
CountryFlag.fromCode('EU',
placeholderBuilder: (context, code) => MyOwnChip(code))
Special flags (Scotland, EU, UN, …)
The asset packages also bundle 29 non-ISO flags: subdivisions (Scotland,
Wales, Sicily, Hawaii, …), organizations (EU, UN) and other regions. Each is
a SpecialFlag with an English name, native name, search aliases, and the
alpha-2 code of the country it belongs to:
SpecialFlags.search('scotland'); // [SpecialFlag(GB-SCT, Scotland)]
SpecialFlags.search('euskadi'); // Basque Country — native names work
SpecialFlags.byCode('GB-SCT'); // lookup by asset code
SpecialFlags.byCountry('GB'); // Scotland, Wales, Orkney
Countries.byAlpha2('GB')!.specialFlags;// same, from a Country
SpecialFlags.flags; // all 29, name-sorted
CountryFlag.fromCode(SpecialFlags.scotland, shape: FlagShape.square)
These are deliberately not countries: a SpecialFlag has no dial code,
currency or continent, and Countries.search never returns one — so a phone
picker can never surface Scotland. A country selector that wants them opts
in by composing the two searches:
final countries = Countries.search(query);
final extras = SpecialFlags.search(query);
To persist a selection, store country.alpha2 (or flag.code) and
rehydrate with Countries.byAlpha2 — a Country never needs serializing.
Phone picker in a few lines
PhoneInputFormatter applies the country's mask live as the user types, and
handles the fiddly parts: inserting mid-string, backspacing over a separator,
and pasting an already-formatted number. 183 countries group numbers
differently depending on the leading digits — a German 151 mobile is not
grouped like a 157 one — so country.phoneFormats carries every variant
and the formatter re-picks the right one on each keystroke.
TextField(
keyboardType: TextInputType.phone,
inputFormatters: [PhoneInputFormatter.forCountry(country)],
decoration: InputDecoration(
hintText: country.phoneMask,
prefixIcon: Row(mainAxisSize: MainAxisSize.min, children: [
CountryFlag(country: country, shape: FlagShape.rectangle, size: 18),
Text(country.dialCode!),
]),
),
)
Validate on submit:
switch (country.validatePhoneNumber(controller.text)) {
case PhoneNumberValidity.empty: // nothing entered
case PhoneNumberValidity.tooShort: // below phoneMinLength
case PhoneNumberValidity.tooLong: // above phoneMaxLength
case PhoneNumberValidity.valid: // right number of digits
}
This is a length check, not a real-number check — it tells you a number is
the wrong shape, never that it is a working line. The 13 countries with no
phone metadata report valid for any non-empty input.
Formatting without a widget:
country.formatPhoneNumber('551234567'); // '551 23 45 67'
formatNationalNumber('2135551234', mask: '(###) ###-####');
Pasting an international number is not unwound: whether a leading 1 is a
country code or a subscriber digit is genuinely ambiguous, so strip the dial
code yourself before setting the field.
See example/ for a full searchable country selector and phone picker.
Data & asset credits
- Country data from mledoze/countries (ODbL 1.0)
- Localized names from Unicode CLDR (Unicode License)
- Phone metadata derived from google/libphonenumber (Apache 2.0)
- Flag SVGs from the free Untitled UI flag set
Regenerate the dataset with python3 tool/generate_data.py.
Libraries
- country_kit
- Country data + flag widgets for building phone pickers and country selectors.
- l10n/ar
- l10n/az
- l10n/bn
- l10n/br
- l10n/cs
- l10n/cy
- l10n/da
- l10n/de
- l10n/el
- l10n/es
- l10n/et
- l10n/fa
- l10n/fi
- l10n/fil
- l10n/fr
- l10n/he
- l10n/hi
- l10n/hr
- l10n/hu
- l10n/id
- l10n/it
- l10n/ja
- l10n/ko
- l10n/ms
- l10n/nb
- l10n/nl
- l10n/pl
- l10n/pt
- l10n/ro
- l10n/ru
- l10n/sk
- l10n/sr
- l10n/sv
- l10n/sw
- l10n/th
- l10n/tr
- l10n/uk
- l10n/ur
- l10n/vi
- l10n/zh