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

A Brazilian ZIP code (CEP) lookup library for Dart and Flutter applications. Supports multiple output formats including JSON and XML.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_cep2/flutter_cep2.dart';
import 'package:url_launcher/url_launcher.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter CEP2 Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF2E7D32), // Brazilian green
          brightness: Brightness.light,
        ),
        useMaterial3: true,
        appBarTheme: const AppBarTheme(centerTitle: true, elevation: 2),
      ),
      home: const CepDemoPage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter CEP2 Demo'),
        backgroundColor: Theme.of(context).colorScheme.primaryContainer,
      ),
      body: const SingleChildScrollView(
        padding: EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            _HeaderCard(),
            SizedBox(height: 16),
            CepLookupWidget(),
            SizedBox(height: 24),
            _FeaturesList(),
          ],
        ),
      ),
    );
  }
}

class _HeaderCard extends StatelessWidget {
  const _HeaderCard();

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 4,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            Icon(
              Icons.location_on,
              size: 48,
              color: Theme.of(context).colorScheme.primary,
            ),
            const SizedBox(height: 8),
            Text(
              'Brazilian ZIP Code Lookup',
              style: Theme.of(
                context,
              ).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold),
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 8),
            Text(
              'Powered by ViaCEP API',
              style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                color: Theme.of(context).colorScheme.onSurfaceVariant,
              ),
              textAlign: TextAlign.center,
            ),
          ],
        ),
      ),
    );
  }
}

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

  @override
  State<CepLookupWidget> createState() => _CepLookupWidgetState();
}

class _CepLookupWidgetState extends State<CepLookupWidget> {
  final _cepService = FlutterCep2();
  final _controller = TextEditingController();

  Cep? _result;
  String? _error;
  bool _isLoading = false;
  CepOutputFormat _selectedFormat = CepOutputFormat.json;

  // Exemplos de CEPs para teste rápido
  final List<String> _exampleCeps = [
    '01310-100', // São Paulo - SP
    '20040-020', // Rio de Janeiro - RJ
    '30112-000', // Belo Horizonte - MG
    '80010-000', // Curitiba - PR
    '60160-230', // Fortaleza - CE
  ];

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

  Future<void> _searchCep() async {
    if (_controller.text.trim().isEmpty) {
      setState(() {
        _error = 'Please enter a CEP';
        _result = null;
      });
      return;
    }

    setState(() {
      _result = null;
      _error = null;
      _isLoading = true;
    });

    try {
      final result = await _cepService.search(
        _controller.text,
        output: _selectedFormat,
      );
      setState(() {
        _result = result;
        _isLoading = false;
      });
    } on CepException catch (e) {
      setState(() {
        _error = e.message;
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _error = 'Unexpected error: $e';
        _isLoading = false;
      });
    }
  }

  void _useExampleCep(String cep) {
    _controller.text = cep;
    _searchCep();
  }

  void _clearSearch() {
    setState(() {
      _controller.clear();
      _result = null;
      _error = null;
    });
  }

  void _copyToClipboard(String text, String label) {
    Clipboard.setData(ClipboardData(text: text));
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('$label copied to clipboard'),
        duration: const Duration(seconds: 2),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 4,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'CEP Lookup',
              style: Theme.of(
                context,
              ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 16),

            // CEP Input Field
            TextField(
              controller: _controller,
              decoration: InputDecoration(
                labelText: 'Enter CEP (e.g., 01310-100)',
                hintText: '12345-678 or 12345678',
                prefixIcon: const Icon(Icons.location_on),
                suffixIcon: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    if (_controller.text.isNotEmpty)
                      IconButton(
                        icon: const Icon(Icons.clear),
                        onPressed: _clearSearch,
                        tooltip: 'Clear',
                      ),
                    IconButton(
                      icon: _isLoading
                          ? const SizedBox(
                              width: 20,
                              height: 20,
                              child: CircularProgressIndicator(strokeWidth: 2),
                            )
                          : const Icon(Icons.search),
                      onPressed: _isLoading ? null : _searchCep,
                      tooltip: 'Search',
                    ),
                  ],
                ),
                border: const OutlineInputBorder(),
              ),
              keyboardType: TextInputType.number,
              textInputAction: TextInputAction.search,
              onSubmitted: (_) => _searchCep(),
              inputFormatters: [
                FilteringTextInputFormatter.allow(RegExp(r'[0-9-]')),
                LengthLimitingTextInputFormatter(10),
              ],
            ),
            const SizedBox(height: 12),

            // Output Format Selection
            Row(
              children: [
                Text(
                  'Format: ',
                  style: Theme.of(context).textTheme.labelMedium,
                ),
                SegmentedButton<CepOutputFormat>(
                  segments: const [
                    ButtonSegment(
                      value: CepOutputFormat.json,
                      label: Text('JSON'),
                      icon: Icon(Icons.code),
                    ),
                    ButtonSegment(
                      value: CepOutputFormat.xml,
                      label: Text('XML'),
                      icon: Icon(Icons.web),
                    ),
                  ],
                  selected: {_selectedFormat},
                  onSelectionChanged: (Set<CepOutputFormat> selection) {
                    setState(() {
                      _selectedFormat = selection.first;
                    });
                  },
                ),
              ],
            ),
            const SizedBox(height: 16),

            // Example CEPs
            Text(
              'Quick examples:',
              style: Theme.of(context).textTheme.labelMedium,
            ),
            const SizedBox(height: 8),
            Wrap(
              spacing: 8,
              runSpacing: 4,
              children: _exampleCeps
                  .map(
                    (cep) => ActionChip(
                      label: Text(cep),
                      onPressed: () => _useExampleCep(cep),
                      avatar: const Icon(Icons.flash_on, size: 16),
                    ),
                  )
                  .toList(),
            ),
            const SizedBox(height: 16),

            // Results
            if (_error != null) _ErrorDisplay(error: _error!),
            if (_result != null)
              _ResultDisplay(result: _result!, onCopy: _copyToClipboard),
          ],
        ),
      ),
    );
  }
}

class _ErrorDisplay extends StatelessWidget {
  const _ErrorDisplay({required this.error});

  final String error;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: Theme.of(context).colorScheme.errorContainer,
        borderRadius: BorderRadius.circular(8),
      ),
      child: Row(
        children: [
          Icon(
            Icons.error_outline,
            color: Theme.of(context).colorScheme.onErrorContainer,
          ),
          const SizedBox(width: 8),
          Expanded(
            child: Text(
              error,
              style: TextStyle(
                color: Theme.of(context).colorScheme.onErrorContainer,
                fontWeight: FontWeight.w500,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _ResultDisplay extends StatelessWidget {
  const _ResultDisplay({required this.result, required this.onCopy});

  final Cep result;
  final Function(String, String) onCopy;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return Container(
      width: double.infinity,
      decoration: BoxDecoration(
        color: theme.colorScheme.primaryContainer.withValues(alpha: 0.3),
        borderRadius: BorderRadius.circular(12),
        border: Border.all(
          color: theme.colorScheme.primary.withValues(alpha: 0.3),
        ),
      ),
      child: Column(
        children: [
          // Header
          Container(
            width: double.infinity,
            padding: const EdgeInsets.all(16),
            decoration: BoxDecoration(
              color: theme.colorScheme.primary,
              borderRadius: const BorderRadius.only(
                topLeft: Radius.circular(12),
                topRight: Radius.circular(12),
              ),
            ),
            child: Row(
              children: [
                Icon(Icons.check_circle, color: theme.colorScheme.onPrimary),
                const SizedBox(width: 8),
                Text(
                  'Address Found',
                  style: theme.textTheme.titleMedium?.copyWith(
                    color: theme.colorScheme.onPrimary,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                const Spacer(),
                IconButton(
                  icon: Icon(Icons.copy, color: theme.colorScheme.onPrimary),
                  onPressed: () => onCopy(result.toString(), 'Full address'),
                  tooltip: 'Copy all data',
                ),
              ],
            ),
          ),

          // Content
          Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              children: [
                _AddressRow(
                  icon: Icons.pin_drop,
                  label: 'CEP',
                  value: result.cep,
                  onCopy: onCopy,
                ),
                _AddressRow(
                  icon: Icons.location_on,
                  label: 'Street',
                  value: result.logradouro,
                  onCopy: onCopy,
                ),
                if (result.complemento?.isNotEmpty == true)
                  _AddressRow(
                    icon: Icons.info_outline,
                    label: 'Complement',
                    value: result.complemento!,
                    onCopy: onCopy,
                  ),
                _AddressRow(
                  icon: Icons.location_city,
                  label: 'Neighborhood',
                  value: result.bairro,
                  onCopy: onCopy,
                ),
                _AddressRow(
                  icon: Icons.location_city,
                  label: 'City',
                  value: result.localidade,
                  onCopy: onCopy,
                ),
                _AddressRow(
                  icon: Icons.map,
                  label: 'State',
                  value: result.uf,
                  onCopy: onCopy,
                ),
                if (result.ddd?.isNotEmpty == true)
                  _AddressRow(
                    icon: Icons.phone,
                    label: 'Area Code',
                    value: result.ddd!,
                    onCopy: onCopy,
                  ),
                _AddressRow(
                  icon: Icons.tag,
                  label: 'IBGE Code',
                  value: result.ibge,
                  onCopy: onCopy,
                ),
                if (result.gia?.isNotEmpty == true)
                  _AddressRow(
                    icon: Icons.account_balance,
                    label: 'GIA Code',
                    value: result.gia!,
                    onCopy: onCopy,
                  ),
                if (result.siaf?.isNotEmpty == true)
                  _AddressRow(
                    icon: Icons.account_balance,
                    label: 'SIAF Code',
                    value: result.siaf!,
                    onCopy: onCopy,
                  ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class _AddressRow extends StatelessWidget {
  const _AddressRow({
    required this.icon,
    required this.label,
    required this.value,
    required this.onCopy,
  });

  final IconData icon;
  final String label;
  final String value;
  final Function(String, String) onCopy;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Icon(icon, size: 20, color: Theme.of(context).colorScheme.primary),
          const SizedBox(width: 8),
          SizedBox(
            width: 80,
            child: Text(
              '$label:',
              style: Theme.of(context).textTheme.bodySmall?.copyWith(
                fontWeight: FontWeight.w500,
                color: Theme.of(context).colorScheme.onSurfaceVariant,
              ),
            ),
          ),
          Expanded(
            child: SelectableText(
              value,
              style: Theme.of(
                context,
              ).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w500),
            ),
          ),
          IconButton(
            icon: const Icon(Icons.copy, size: 16),
            onPressed: () => onCopy(value, label),
            tooltip: 'Copy $label',
            constraints: const BoxConstraints(minWidth: 32, minHeight: 32),
          ),
        ],
      ),
    );
  }
}

class _FeaturesList extends StatelessWidget {
  const _FeaturesList();

  @override
  Widget build(BuildContext context) {
    final features = [
      {
        'icon': Icons.flash_on,
        'title': 'Fast & Reliable',
        'description':
            'Powered by ViaCEP API with comprehensive error handling',
      },
      {
        'icon': Icons.code,
        'title': 'Multiple Formats',
        'description': 'Supports JSON and XML output formats',
      },
      {
        'icon': Icons.phone_android,
        'title': 'Cross Platform',
        'description': 'Works on Android, iOS, Web, Windows, macOS, and Linux',
      },
      {
        'icon': Icons.security,
        'title': 'Null Safety',
        'description': 'Built with modern Dart null safety features',
      },
      {
        'icon': Icons.bug_report,
        'title': 'Well Tested',
        'description': 'Comprehensive test suite with 100% coverage',
      },
      {
        'icon': Icons.integration_instructions,
        'title': 'Easy Integration',
        'description': 'Simple API with excellent documentation',
      },
    ];

    return Card(
      elevation: 4,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(Icons.stars, color: Theme.of(context).colorScheme.primary),
                const SizedBox(width: 8),
                Text(
                  'Plugin Features',
                  style: Theme.of(
                    context,
                  ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
                ),
              ],
            ),
            const SizedBox(height: 16),
            GridView.builder(
              shrinkWrap: true,
              physics: const NeverScrollableScrollPhysics(),
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                childAspectRatio: 1.2,
                crossAxisSpacing: 12,
                mainAxisSpacing: 12,
              ),
              itemCount: features.length,
              itemBuilder: (context, index) {
                final feature = features[index];
                return Container(
                  padding: const EdgeInsets.all(12),
                  decoration: BoxDecoration(
                    color: Theme.of(context).colorScheme.surfaceContainerHighest
                        .withValues(alpha: 0.3),
                    borderRadius: BorderRadius.circular(12),
                    border: Border.all(
                      color: Theme.of(
                        context,
                      ).colorScheme.outline.withValues(alpha: 0.2),
                    ),
                  ),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Icon(
                        feature['icon'] as IconData,
                        size: 32,
                        color: Theme.of(context).colorScheme.primary,
                      ),
                      const SizedBox(height: 8),
                      Text(
                        feature['title'] as String,
                        style: Theme.of(context).textTheme.titleSmall?.copyWith(
                          fontWeight: FontWeight.bold,
                        ),
                        textAlign: TextAlign.center,
                      ),
                      const SizedBox(height: 4),
                      Text(
                        feature['description'] as String,
                        style: Theme.of(context).textTheme.bodySmall,
                        textAlign: TextAlign.center,
                        maxLines: 3,
                        overflow: TextOverflow.ellipsis,
                      ),
                    ],
                  ),
                );
              },
            ),
            const SizedBox(height: 16),
            Center(
              child: FilledButton.icon(
                onPressed: () {
                  launchUrl(Uri.parse('https://pub.flutter-io.cn/packages/flutter_cep2'));
                },
                icon: const Icon(Icons.library_books),
                label: const Text('View Documentation'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
5
likes
160
points
55
downloads

Publisher

verified publishervitormelo.dev.br

Weekly Downloads

A Brazilian ZIP code (CEP) lookup library for Dart and Flutter applications. Supports multiple output formats including JSON and XML.

Repository (GitHub)
View/report issues
Contributing

Topics

#cep #brazil #address #postal-code #viacep

Documentation

Documentation
API reference

Funding

Consider supporting this project:

github.com

License

LGPL-2.1 (license)

Dependencies

http, meta, xml2json

More

Packages that depend on flutter_cep2