ip_address_input 0.0.1
ip_address_input: ^0.0.1 copied to clipboard
A Flutter widget for entering IPv4 addresses with 4 segmented fields, auto-advance, paste support, form validation, and full visual customization.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:ip_address_input/ip_address_input.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ip_address_input demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
useMaterial3: true,
),
home: const IpAddressInputDemo(),
);
}
}
class IpAddressInputDemo extends StatefulWidget {
const IpAddressInputDemo({super.key});
@override
State<IpAddressInputDemo> createState() => _IpAddressInputDemoState();
}
class _IpAddressInputDemoState extends State<IpAddressInputDemo> {
String _value = '';
String _completed = '';
final _controller = IpAddressInputController();
final _formKey = GlobalKey<FormState>();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Demo — IP Address Input')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ─── onChanged + onCompleted ──────────────────────────────────
_Section(
title: 'onChanged + onCompleted',
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IpAddressInput(
label: 'IP Address',
onChanged: (v) => setState(() => _value = v),
onCompleted: (v) => setState(() => _completed = v),
),
const SizedBox(height: 8),
Text('onChanged: $_value'),
if (_completed.isNotEmpty)
Text(
'onCompleted: $_completed',
style: const TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
const SizedBox(height: 32),
// ─── IpAddressInputController ──────────────────────────────────
_Section(
title: 'IpAddressInputController',
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IpAddressInput(
label: 'With controller',
controller: _controller,
),
const SizedBox(height: 12),
Row(
children: [
FilledButton(
onPressed: () => _controller.setValue('10.0.0.1'),
child: const Text('Set 10.0.0.1'),
),
const SizedBox(width: 8),
OutlinedButton(
onPressed: _controller.clear,
child: const Text('Clear'),
),
],
),
],
),
),
const SizedBox(height: 32),
// ─── Paste ────────────────────────────────────────────────────
_Section(
title: 'Auto paste',
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const IpAddressInput(label: 'Paste an IP here (e.g. 192.168.1.1)'),
const SizedBox(height: 4),
Text(
'Paste a full IP in the first field — it distributes automatically.',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
const SizedBox(height: 32),
// ─── separatorWidget ──────────────────────────────────────────
_Section(
title: 'Custom separatorWidget',
child: IpAddressInput(
label: 'With icon separator',
separatorWidget: const Padding(
padding: EdgeInsets.symmetric(horizontal: 4),
child: Icon(Icons.fiber_manual_record, size: 6),
),
),
),
const SizedBox(height: 32),
// ─── IpAddressFormField ───────────────────────────────────────
_Section(
title: 'IpAddressFormField (validator)',
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IpAddressFormField(
label: 'Router IP',
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (v) {
if (v == null || v.replaceAll('.', '').isEmpty) {
return 'Please fill in the IP address.';
}
final parts = v.split('.');
if (parts.length != 4 || parts.any((p) => p.isEmpty)) {
return 'Incomplete IP address.';
}
for (final p in parts) {
final n = int.tryParse(p);
if (n == null || n < 0 || n > 255) {
return 'Invalid octet: $p (must be 0–255).';
}
}
return null;
},
),
const SizedBox(height: 12),
FilledButton(
onPressed: () => _formKey.currentState?.validate(),
child: const Text('Validate'),
),
],
),
),
),
const SizedBox(height: 32),
// ─── Disabled ─────────────────────────────────────────────────
const _Section(
title: 'Disabled',
child: IpAddressInput(
label: 'Read only',
initialValue: '8.8.8.8',
enabled: false,
),
),
],
),
),
);
}
}
class _Section extends StatelessWidget {
const _Section({required this.title, required this.child});
final String title;
final Widget child;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 12),
child,
],
);
}
}