phone_field_plus 1.0.2 copy "phone_field_plus: ^1.0.2" to clipboard
phone_field_plus: ^1.0.2 copied to clipboard

Offline-first Flutter phone input with country picker and auto-detection.

example/lib/main.dart

// example/lib/main.dart
//
// Demonstrates all major PhoneField features.

import 'package:flutter/material.dart';
import 'package:phone_field_plus/phone_field_plus.dart';

void main() => runApp(const PhoneFieldExampleApp());

class PhoneFieldExampleApp extends StatelessWidget {
  const PhoneFieldExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'PhoneField+ Demo',
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: Colors.indigo,
      ),
      home: const ExampleScreen(),
    );
  }
}

class ExampleScreen extends StatefulWidget {
  const ExampleScreen({super.key});

  @override
  State<ExampleScreen> createState() => _ExampleScreenState();
}

class _ExampleScreenState extends State<ExampleScreen> {
  PhoneValue? _lastValue;
  final _formKey = GlobalKey<FormState>();

  // Shared controller for the programmatic example
  late final PhoneController _controller;

  @override
  void initState() {
    super.initState();
    _controller = PhoneController(
      initialCountry: Countries.tunisia,
      autoDetectCountry: true,
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('PhoneField+ Demo')),
      body: ListView(
        padding: const EdgeInsets.all(24),
        children: [
          // ── Basic ──────────────────────────────────────────────────────────
          _SectionHeader('Basic — auto-detect + auto-validate'),
          PhoneField(
            initialCountry: Countries.france,
            autoDetectCountry: true,
            autoValidate: true,
            onChanged: (v) => setState(() => _lastValue = v),
            decoration: const InputDecoration(
              labelText: 'Phone number',
              border: OutlineInputBorder(),
            ),
          ),
          const SizedBox(height: 12),
          if (_lastValue != null) _ValueCard(_lastValue!),

          const SizedBox(height: 32),

          // ── Loose validation ───────────────────────────────────────────────
          _SectionHeader('Loose validation mode'),
          PhoneField(
            initialCountry: Countries.unitedStates,
            autoValidate: true,
            validatorMode: ValidatorMode.loose,
            decoration: const InputDecoration(
              labelText: 'US / CA number (loose)',
              border: OutlineInputBorder(),
            ),
          ),

          const SizedBox(height: 32),

          // ── Custom flag builder ────────────────────────────────────────────
          _SectionHeader('Custom flag builder (ISO badge)'),
          PhoneField(
            initialCountry: Countries.germany,
            flagBuilder: (country) => Container(
              padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
              decoration: BoxDecoration(
                color: Colors.indigo.shade50,
                borderRadius: BorderRadius.circular(4),
              ),
              child: Text(
                country.isoCode,
                style: const TextStyle(
                  fontSize: 12,
                  fontWeight: FontWeight.bold,
                  color: Colors.indigo,
                ),
              ),
            ),
            decoration: const InputDecoration(
              labelText: 'German number',
              border: OutlineInputBorder(),
            ),
          ),

          const SizedBox(height: 32),

          // ── Programmatic controller ────────────────────────────────────────
          _SectionHeader('Programmatic controller (Tunisia default)'),
          PhoneField(
            controller: _controller,
            autoValidate: true,
            decoration: const InputDecoration(
              labelText: 'Controlled field',
              border: OutlineInputBorder(),
            ),
          ),
          const SizedBox(height: 12),
          Row(
            children: [
              OutlinedButton(
                onPressed: () => _controller.setCountry(Countries.morocco),
                child: const Text('Switch to Morocco'),
              ),
              const SizedBox(width: 12),
              OutlinedButton(
                onPressed: () {
                  _controller.clear();
                  _controller.setCountry(Countries.tunisia);
                },
                child: const Text('Reset'),
              ),
            ],
          ),

          const SizedBox(height: 32),

          // ── Form integration ───────────────────────────────────────────────
          _SectionHeader('Form integration with custom validator'),
          Form(
            key: _formKey,
            child: Column(
              children: [
                PhoneField(
                  initialCountry: Countries.saudiArabia,
                  autoValidate: false,
                  validator: (v) {
                    if (!v.hasNumber) return 'Required';
                    if (!v.isValid) {
                      return 'Invalid ${v.country.name} number';
                    }
                    return null;
                  },
                  decoration: const InputDecoration(
                    labelText: 'Saudi number',
                    border: OutlineInputBorder(),
                  ),
                ),
                const SizedBox(height: 12),
                ElevatedButton(
                  onPressed: () => _formKey.currentState?.validate(),
                  child: const Text('Validate'),
                ),
              ],
            ),
          ),

          const SizedBox(height: 48),
        ],
      ),
    );
  }
}

class _SectionHeader extends StatelessWidget {
  final String title;
  const _SectionHeader(this.title);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 8),
      child: Text(
        title,
        style: Theme.of(context).textTheme.titleSmall?.copyWith(
              color: Colors.grey.shade600,
              fontWeight: FontWeight.w600,
            ),
      ),
    );
  }
}

class _ValueCard extends StatelessWidget {
  final PhoneValue value;
  const _ValueCard(this.value);

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            _Row('Country', '${value.country.flagEmoji} ${value.country.name}'),
            _Row('E.164', value.e164),
            _Row('National', value.nationalNumber),
            _Row('Valid', value.isValid ? '✅ Yes' : '❌ No'),
          ],
        ),
      ),
    );
  }
}

class _Row extends StatelessWidget {
  final String label;
  final String value;
  const _Row(this.label, this.value);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 2),
      child: Row(
        children: [
          SizedBox(
            width: 80,
            child: Text(
              label,
              style: const TextStyle(
                fontWeight: FontWeight.w600,
                fontSize: 12,
                color: Colors.grey,
              ),
            ),
          ),
          Expanded(child: Text(value, style: const TextStyle(fontSize: 13))),
        ],
      ),
    );
  }
}
1
likes
145
points
--
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Offline-first Flutter phone input with country picker and auto-detection.

License

MIT (license)

Dependencies

flutter

More

Packages that depend on phone_field_plus