car_license_plate 2.0.1
car_license_plate: ^2.0.1 copied to clipboard
Realistic, scalable licence plate widgets and validators for 20 countries: Iran, EU, UK, USA, Japan, China, India, Brazil and more.
car_license_plate #
Realistic licence plate widgets for Flutter, with a validator for each country. They're drawn as vectors to real millimetre specs, so they stay sharp and in proportion at any size.

Features #
- 28 designs across 20 countries and regions. Included are Iran, the EU (Germany, France, Italy, Spain, the Netherlands, plus a generic EU plate), the UK, four US states, Japan, China, South Korea, India, Indonesia, Brazil, Russia, Turkey, Ukraine, Saudi Arabia, Dubai and New South Wales.
- True to spec. Each design uses the plate's real size and aspect ratio, its colours and bands (including the EU star circle and national codes), and its character heights and separators, taken from the regulations where available.
- Validators. Each country can check, normalise, parse and format a registration, for example
ab123cd→AB-123-CD. Persian and Arabic-Indic digits, Cyrillic letters and their Latin look-alikes are all accepted. - Responsive. Plates scale to any width or height, keep their aspect ratio and never overflow their parent.
- Accessible. Each plate is one semantics node, and screen readers spell the registration out. System text scaling can't break the layout.
- Works on every platform. The library is pure Dart with no
dart:ioand no platform channels. Fonts are bundled (OFL, subset, about 210 KB), so plates look the same everywhere. - RTL-safe. The plate layout doesn't mirror in Persian or Arabic apps.
- Extensible. You can draw any other plate by extending
PlateSpec. - Backward compatible. The 1.x Iranian
CarLicensePlatewidget, including its input mode, still works.
Install #
dependencies:
car_license_plate: ^2.0.0
Quick start #
import 'package:car_license_plate/car_license_plate.dart';
LicensePlate(GermanPlate('B MW 1234', inspectionYear: 2026))
LicensePlate(const UkPlate('AB12 CDE'), width: 240) // rear (yellow)
LicensePlate(UsPlate('123ABC1', style: UsPlateStyle.california))
LicensePlate(const JapanesePlate(
region: '品川', classNumber: '330', hiragana: 'さ', serial: '1234',
))
LicensePlate(const IranPlate(
twoDigits: '12', letter: 'ب', threeDigits: '345', provinceCode: '67',
))
Validate and format registrations:
FrenchPlate.format.isValid('AB-123-CD'); // true
FrenchPlate.format.format('ab123cd'); // 'AB-123-CD'
UkPlate.format.parse('AB12CDE')!['age']; // '12'
IranPlate.parse('۱۲ ب ۳۴۵ ایران ۶۷'); // IranPlate(12, ب, 345, 67)
BrazilPlate.toMercosur('ABC-1234'); // 'ABC1C34'
If the country is only known at runtime (from a database, or from a camera reading a plate), look it up by code:
final plate = PlateRegistry.create('fr', 'ab123cd'); // a FrenchPlate
PlateRegistry.format('GB')!.isValid('AB12 CDE'); // true
PlateRegistry.detect('AB-123-CD'); // ['FR', ...]
Supported plates #
| Country | Class | Size (mm) | Example | Validator |
|---|---|---|---|---|
IranPlate, CarLicensePlate |
520 × 110 | 12 ب 345 67 |
two digits, letter, three digits, province | |
Germany |
GermanPlate |
520 × 110 | B MW 1234 |
district, 1–2 letters, 1–4 digits, E/H, ≤ 8 characters |
France |
FrenchPlate |
520 × 110 | AB-123-CD |
SIV, no I/O/U, no 000/SS/WW |
Italy |
ItalianPlate |
520 × 110 | AB 123 CD |
no I/O/Q/U |
Spain |
SpanishPlate |
520 × 110 | 1234 BCD |
consonants only, no Ñ/Q |
Netherlands |
DutchPlate |
520 × 110 | GBB-12-X |
sidecodes 1–14 |
Any EU country |
EuPlate |
520 × 110 | WI 12345 |
optional (pass your own) |
United Kingdom |
UkPlate (front/rear, UK band, green flash) |
520 × 111 | AB12 CDE |
current, prefix and suffix series |
USA |
UsPlate + UsPlateStyle (CA, TX, FL, NY, or your own) |
305 × 152 | 123ABC1 |
per state |
Japan |
JapanesePlate (private, commercial, kei) |
330 × 165 | 品川 330 さ 12-34 |
office, class, hiragana, serial (・・12 style) |
China |
ChinesePlate (blue, new energy, yellow) |
440/480 × 140 | 京A·12345 |
31 provinces, no I/O; new-energy D/F series |
South Korea |
KoreanPlate |
520 × 110 | 123가 4567 |
private, commercial and rental syllables |
India |
IndianPlate (private, commercial, EV, rental) |
500 × 120 | MH 12 AB 1234 |
state series and BH series |
Indonesia |
IndonesianPlate |
460 × 135 | B 1234 XYZ |
plus MM.YY expiry |
Brazil |
BrazilPlate (six categories) |
400 × 130 | ABC1D23 |
Mercosur, and converts old plates |
Russia |
RussianPlate |
520 × 112 | А 123 ВС 77 |
the 12 allowed letters, region code |
Turkey |
TurkishPlate |
520 × 110 | 34 ABC 123 |
province 01–81 |
Ukraine |
UkrainianPlate |
520 × 112 | АА 1234 ВВ |
Cyrillic or Latin look-alikes |
Saudi Arabia |
SaudiPlate |
520 × 110 | 1234 ABJ |
the 17 letters; the Arabic row is derived |
UAE (Dubai) |
DubaiPlate |
520 × 110 | A 12345 |
code plus up to 5 digits |
Australia (NSW) |
NswPlate |
372 × 134 | AB·12·CD |
2004+ series |
Every design is also available as a high-resolution screenshot, including a dark-background gallery.
Size, shadow and theme #
LicensePlate(plate) // available width, at most 360 px
LicensePlate(plate, width: 200) // exact width; height follows the aspect ratio
LicensePlate(plate, height: 48) // exact height
LicensePlate(plate, elevation: 4) // soft shadow
LicensePlate(plate, onTap: () {}) // tappable (announced as a button)
LicensePlateTheme( // defaults for every plate below
width: 240,
elevation: 2,
child: ...,
)
Plates always keep their real colours: a white plate stays white in dark mode, as it does on a car. The look of a plate is set by its parameters, for example:
UkPlate(rear: false, band: UkBand.green)JapanesePlate(type: JapanesePlateType.keiPrivate)IndianPlate(category: IndianPlateCategory.electric)BrazilPlate(category: BrazilPlateCategory.commercial)ChinesePlate(type: ChinesePlateType.newEnergy)GermanPlate(inspectionYear: 2027, front: true)FrenchPlate(department: '13')
For any other US state or Canadian province, define a UsPlateStyle with your own name, slogan and colours.
Iranian plates and input (1.x API) #
The original widget still works, including the input mode:
CarLicensePlate(
isInputType: true,
twoNumberSection: two,
alphabetSection: letter,
threeNumberSection: three,
cityNumber: province,
usePersianNumbers: true,
onTwoNumberChanged: (v) => setState(() => two = v), // always 0-9 digits
onThreeNumberChanged: (v) => setState(() => three = v),
onCityNumberChanged: (v) => setState(() => province = v),
onAlphabetTap: () async {
final l = await showIranPlateLetterPicker(context);
if (l != null) setState(() => letter = l);
},
)
IranPlate.letterslists every plate letter, andIranPlate.privateLetterslists the private-car series.ژwithdisability: truedraws the wheelchair symbol.
Accessibility #
- Each
LicensePlateis announced as one item, for example "Germany licence plate B MW 1234", with the registration marked to be spelled out character by character. - Use
semanticLabel:to translate the label. - The plate ignores the system text scale factor so its layout stays correct. It still scales with the size you give it.
Draw your own plate #
class FreedoniaPlate extends PlateSpec {
const FreedoniaPlate(this.registration);
@override final String registration;
@override String get countryCode => 'XF';
@override String get countryName => 'Freedonia';
@override Size get size => const Size(520, 110); // millimetres
@override PlateFormat get registrationFormat => PlateFormat(
countryCode: 'XF', name: 'Freedonia',
pattern: r'^[A-Z]{2} ?\d{4}$', example: 'AB 1234');
@override
Widget buildFace(BuildContext context) => PlateSurface(
size: size, borderWidth: 2,
children: [
const PlateBox(left: 0, top: 0, width: 44, height: 110,
child: EuBand(code: 'XF', stars: false)),
PlateBox(left: 50, top: 0, width: 460, height: 110,
child: PlateText(registration, height: 75,
color: PlateColors.black)),
],
);
}
buildFace is laid out at the plate's size in millimetres; LicensePlate scales it. Helpers you can use:
PlateTextdraws text by real character height and condenses it to fit, as plate typefaces do.PlateRow,PlateSegment,PlateDotandPlateDashlay out character groups and separators.EuBand,StripeFlagandUnionFlagdraw bands and flags.
Migrating from 1.x #
2.0 is a major release because the Iranian plate now draws correctly, and that changes what existing apps see:
- Section order. The plate reads left to right as on real plates:
twoNumberSection, letter,threeNumberSection, thencityNumberunder «ایران». 1.x drew the numbers in reverse order. If you swapped your arguments to compensate, swap them back. twoNumberSectiontakes 2 digits, not 3, in input mode.- Dark mode. The plate stays white with black characters instead of switching to a white-on-white image.
- Input callbacks always receive Latin digits (0-9), even when typed on a Persian or Arabic keyboard. Non-digits are ignored.
- Leading zeros are kept (
05no longer becomes5). textSizeis now relative to the 1.x default for the same width, and is clamped so text can't leave its section.- The
intldependency and the PNG assets have been removed. Plates are drawn in code.
Platform support #
| Platform | Status |
|---|---|
| Web | Example app built with flutter build web |
| Windows (tests) | 79 package tests and 1 example test, including golden and pixel checks, run with real fonts |
| Android, iOS, macOS, Linux, Windows desktop | Supported but not built for this release |
The last row is supported because the library is pure Dart: no dart:io, no plugins. Those targets haven't been built or run on a device for this release.
Accuracy and fonts #
Dimensions, colours and formats come from the regulations where they're public. These include EU Regulation 2411/98, the German FZV, the French SIV order, the Spanish RGV, Brazil's CONTRAN 780/2019, Russia's GOST R 50577 and the UK's BS AU 145e. Elsewhere they come from well-sourced secondary references.
National emblems, seals and holograms are simplified. Official typefaces (FE-Schrift, Charles Wright and others) aren't freely licensed, so the plates use similar open fonts. These renderings are for apps and illustrations, not for making real plates.
The bundled fonts are subsets of Barlow Condensed, Vazirmatn, Noto Sans SC, Noto Sans JP and Noto Sans KR. All are under the SIL Open Font License 1.1, and the licence texts are in fonts/. Characters missing from a subset fall back to the system font. To use your own licensed plate font, extend PlateSpec and use PlateFont.latin.withFamily('YourFont').
License #
MIT
Germany
France
Italy
Spain
Netherlands
Any EU country
United Kingdom
USA
Japan
China
South Korea
India
Indonesia
Brazil
Russia
Turkey
Ukraine
Saudi Arabia
UAE (Dubai)
Australia (NSW)