mre_fields 0.3.3 copy "mre_fields: ^0.3.3" to clipboard
mre_fields: ^0.3.3 copied to clipboard

Flutter form fields that follow the language typed (auto RTL/LTR text and alignment), phone numbers validated for every country with a picker, and image paste.

example/example.md

mre_fields example #

A small app that shows every piece of the package. The code below is what each card in the app is built from.

cd example
flutter run   # phone, desktop or web

The app bar has a light and dark switch and an English and Arabic switch. A slider changes the field radius for the whole app through MREFieldsTheme.

Typing English and Arabic: the field and the text under it change direction

1. Register the theme once #

Do this where you build your MaterialApp. Register it on both themes so dark mode keeps the values.

const fields = MREFieldsTheme(fieldBorderRadius: 16);

final app = MaterialApp(
  theme: ThemeData(extensions: const [fields]),
  darkTheme: ThemeData(
    brightness: Brightness.dark,
    extensions: const [fields],
  ),
  home: const HomePage(),
);

2. Text that follows its language #

MREAutoText takes every parameter of Text. The first word with a letter decides the direction, so 123 مرحبا is right to left and (#1) Hello is left to right.

final column = Column(
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: const [
    MREAutoText('مرحبا بالعالم'), // reads right to left
    MREAutoText('Hello world'), // reads left to right
    MREAutoText('مرحبا', textDirection: TextDirection.ltr), // fixed
  ],
);

Already have a Text? Add the direction and the alignment to it:

final text = Text(
  name,
  style: const TextStyle(fontSize: 20),
).autoDirection().autoAlign();

3. A field that follows the language #

final field = MRETextField(
  labelText: 'Name',
  hintText: 'Type in any language',
  showClearButton: true,
);

Lock the direction for emails, links and numbers:

final email = MRETextField(
  labelText: 'Email',
  keyboardType: TextInputType.emailAddress,
  textDirection: TextDirection.ltr,
);

4. Suggestions and select on focus #

final field = MRETextField(
  labelText: 'City',
  suggestions: const ['Cairo', 'Alexandria', 'Giza', 'القاهرة'],
  maxSuggestions: 5,
);
final field = MRETextField(
  labelText: 'Title',
  initialValue: 'Draft',
  selectTextOnFocus: true,
);

5. Validation in a form #

final form = Form(
  key: formKey,
  child: MRETextFormField(
    labelText: 'Name',
    validator: (value) {
      if (value == null || value.trim().isEmpty) {
        return 'Enter a name';
      }
      return null;
    },
  ),
);

6. Change one field #

A parameter beats the theme for that field only.

final field = MRETextField(
  labelText: 'Search',
  borderRadius: 28, // this field only; the theme keeps its own radius
  contentPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
);

7. A phone number field #

It checks the number for every country, switches the country when a number with its dial code is pasted, and opens a searchable picker.

Typing, pasting and picking a phone number

final field = MREPhoneFormField(
  labelText: 'Phone number',
  onChanged: (number) {
    if (number.isValid) {
      saveNumber(number.e164!); // '+201012345678'
    }
  },
);

Accept only some countries, with favorites on top:

const selection = MRECountrySelection(
  include: {'SA', 'AE', 'KW', 'QA', 'BH', 'OM'},
  favorites: ['SA', 'AE'],
  initial: 'SA',
);

final field = MREPhoneFormField(selection: selection);

Read a number in code, with no widget:

final number = MREPhoneNumber.parse('+20 101 234 5678');

final country = number.country!.name; // 'Egypt'
final e164 = number.e164; // '+201012345678'
final nice = number.international; // '+20 10 12345678'
final valid = number.isValid; // true

Translate the picker and the error messages:

final field = MREPhoneFormField(
  labelText: 'رقم الهاتف',
  countryNameBuilder: (country) =>
      arabicNames[country.isoCode] ?? country.name,
  pickerTitle: 'اختر الدولة',
  pickerSearchHint: 'ابحث عن دولة أو رمز',
);

// The error messages come from MREFieldsStrings, registered once in the theme:
const strings = MREFieldsStrings(
  phoneEmpty: 'اكتب رقم الهاتف',
  phoneTooShort: 'الرقم قصير',
  phoneInvalid: 'رقم غير صحيح',
);

8. Paste images #

Pasting three images into a field

A field takes one of three behaviours: nothing, a callback with the image, or thumbnails under the field with open, remove and replace.

// Text only. This is the default.
const textOnly = MRETextField(labelText: 'Message');

// Your code gets each image. Nothing is shown.
final callback = MRETextField(
  labelText: 'Message',
  imagePaste: MREImageCallbackPaste(
    onImagePasted: (image) => upload(image.bytes),
  ),
);

// Images appear inside the field.
const attachments = MRETextField(
  labelText: 'Message',
  imagePaste: MREImageAttachmentPaste(),
);

Read and change the images from your own code:

// Create the controller in initState and dispose it in dispose.
final field = MRETextField(
  labelText: 'Message',
  imagePaste: MREImageAttachmentPaste(controller: controller),
);

// Later: controller.images, controller.removeAt(0), controller.clear().

9. Translate the texts #

Every text a field shows is in MREFieldsStrings.

/// Returns the field texts for [locale]. English is the fallback.
MREFieldsStrings stringsFor(Locale locale) {
  return switch (locale.languageCode) {
    'ar' => const MREFieldsStrings(
      clearTooltip: 'مسح',
      countryPickerTitle: 'اختر الدولة',
      countrySearchHint: 'ابحث عن دولة أو رمز',
      noCountriesFound: 'لا توجد دول',
      removeImageTooltip: 'إزالة الصورة',
      replaceImageTooltip: 'استبدال الصورة',
      closeViewerTooltip: 'إغلاق',
    ),
    _ => const MREFieldsStrings(),
  };
}

/// Swaps the strings whenever the app locale changes.
Widget localizedFields(BuildContext context, Widget child) {
  final theme = Theme.of(context);
  final fields = MREFieldsTheme.of(
    context,
  ).copyWith(strings: stringsFor(Localizations.localeOf(context)));

  return Theme(
    data: theme.copyWith(
      extensions: [
        ...theme.extensions.values.where((e) => e is! MREFieldsTheme),
        fields,
      ],
    ),
    child: child,
  );
}

Widget localizedApp() {
  return MaterialApp(
    localizationsDelegates: GlobalMaterialLocalizations.delegates,
    supportedLocales: const [Locale('en'), Locale('ar')],
    builder: (context, child) => localizedFields(context, child!),
    home: const HomePage(),
  );
}

10. Use the rules without a widget #

final direction = detectTextDirection(name, fallback: TextDirection.ltr);
final strong = detectStrongTextDirection(name); // null when no letter
final side = name.autoTextAlign;
final clean = safeDisplayText(pasted); // lone surrogates become U+FFFD
const cities = ['Cairo', 'Alexandria', 'Giza', 'Luxor', 'Aswan'];
final matches = mreFilterSuggestions(cities, query, limit: 3);
final type = mreSniffImageMimeType(
  upload,
); // 'image/png', 'image/jpeg', ... or null
final isPng = type == 'image/png';

More #

4
likes
160
points
493
downloads
screenshot

Documentation

API reference

Publisher

verified publisherdev.mrecode.net

Weekly Downloads

Flutter form fields that follow the language typed (auto RTL/LTR text and alignment), phone numbers validated for every country with a picker, and image paste.

Repository (GitHub)
View/report issues

Topics

#form #text-field #rtl #input #phone-number

License

BSD-3-Clause (license)

Dependencies

flutter, meta, pasteboard, phone_numbers_parser, web

More

Packages that depend on mre_fields