strong_password 1.1.0
strong_password: ^1.1.0 copied to clipboard
A Dart/Flutter package that generates very strong passwords the way Gmail does, plus per-service deterministic passwords, passphrases, PINs, and password strength estimation.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:strong_password/strong_password.dart';
void main() {
runApp(const StrongPasswordDemo());
}
/// A copy-paste friendly demo of the `strong_password` package.
///
/// Runs on Android, Web, iOS, desktop — anywhere Flutter runs.
/// Build it with: `flutter run` (or `flutter run -d chrome` /
/// `flutter run -d <android-device>`).
class StrongPasswordDemo extends StatelessWidget {
const StrongPasswordDemo({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'strong_password demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final _generator = StrongPasswordGenerator();
// ————— State —————
String _strongPassword = '';
String _passphrase = '';
String _pin = '';
String _deterministic = '';
String _masterSecret = 'correct-horse-battery-staple';
String _service = 'gmail.com';
PasswordStrength? _strength;
@override
void initState() {
super.initState();
_generateAll();
}
// ————— Actions —————
void _generateAll() {
setState(() {
_strongPassword = _generator.generate();
_passphrase = PassphraseGenerator().generate();
_pin = PinGenerator().generate();
_regenerateDeterministic();
_updateStrength();
});
}
void _regenerateDeterministic() {
_deterministic = const DeterministicPasswordGenerator().generate(
secret: _masterSecret,
service: _service,
);
}
void _updateStrength() {
_strength = estimatePasswordStrength(_strongPassword);
}
Future<void> _copy(BuildContext context, String value) async {
if (value.isEmpty) return;
final messenger = ScaffoldMessenger.of(context);
await Clipboard.setData(ClipboardData(text: value));
messenger.showSnackBar(SnackBar(content: Text('Copied: $value')));
}
Widget _resultCard({
required BuildContext context,
required String title,
required String value,
Widget? trailing,
}) {
return Card(
margin: const EdgeInsets.symmetric(vertical: 6),
child: ListTile(
title: Text(title, style: Theme.of(context).textTheme.labelLarge),
subtitle: SelectableText(value, style: const TextStyle(fontSize: 18)),
trailing: trailing ??
IconButton(
icon: const Icon(Icons.copy),
tooltip: 'Copy',
onPressed: () => _copy(context, value),
),
),
);
}
@override
Widget build(BuildContext context) {
final strength = _strength;
return Scaffold(
appBar: AppBar(title: const Text('strong_password demo')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
Text(
'Gmail-style strong passwords, deterministic per-service '
'passwords, passphrases, PINs and strength checks.',
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 16),
// ————— Strong password —————
_resultCard(
context: context,
title: 'Gmail-style strong password (16 chars)',
value: _strongPassword,
),
// ————— Strength + crack time —————
if (strength != null)
Card(
margin: const EdgeInsets.symmetric(vertical: 6),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Strength: ${strength.strength.name.toUpperCase()}',
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
LinearProgressIndicator(
value: strength.score / 100,
color: _strengthColor(strength.strength),
),
const SizedBox(height: 8),
Text(
'Score ${strength.score}/100 · '
'${strength.entropyBits.toStringAsFixed(1)} bits · '
'crack time: ${strength.crackTimeLabel()}',
),
],
),
),
),
// ————— Deterministic —————
_resultCard(
context: context,
title: 'Deterministic password for "$_service"',
value: _deterministic,
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
children: [
Expanded(
child: TextField(
decoration: const InputDecoration(
labelText: 'Master secret',
border: OutlineInputBorder(),
),
onChanged: (v) => _masterSecret = v.isEmpty
? _masterSecret
: v,
),
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
children: [
Expanded(
child: TextField(
decoration: const InputDecoration(
labelText: 'Service (e.g. github.com)',
border: OutlineInputBorder(),
),
onChanged: (v) => _service = v.isEmpty ? _service : v,
),
),
const SizedBox(width: 8),
FilledButton.tonal(
onPressed: () => setState(_regenerateDeterministic),
child: const Text('Derive'),
),
],
),
),
// ————— Passphrase —————
_resultCard(
context: context,
title: 'Memorable passphrase',
value: _passphrase,
),
// ————— PIN —————
_resultCard(context: context, title: '6-digit PIN', value: _pin),
// ————— Profiles —————
_resultCard(
context: context,
title: 'Paranoid profile (32 chars)',
value: _generator.generate(
PasswordProfiles.options(PasswordProfiles.paranoid),
),
),
const SizedBox(height: 16),
FilledButton.icon(
onPressed: _generateAll,
icon: const Icon(Icons.refresh),
label: const Text('Generate all again'),
),
],
),
);
}
Color _strengthColor(Strength s) {
switch (s) {
case Strength.weak:
return Colors.red;
case Strength.fair:
return Colors.orange;
case Strength.good:
return Colors.lightGreen;
case Strength.strong:
return Colors.green;
}
}
}