在这里插入图片描述
强密码是账户安全的第一道防线。下面这篇实战内容基于真实项目代码拆解实现思路:页面结构、交互配置、密码生成逻辑、强度提示和复制反馈。为了便于阅读,代码分成多个小片段,并在每段后面给出说明。

依赖与页面入口

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';

说明

  • Random.secure() 用于生成不可预测的随机序列。
  • flutter_screenutil 让 UI 在多尺寸下保持一致比例。
  • Get.snackbar 是实际项目里常用的轻量反馈方式。
class PasswordGeneratorPage extends StatefulWidget {
  const PasswordGeneratorPage({Key? key}) : super(key: key);

  
  State<PasswordGeneratorPage> createState() => _PasswordGeneratorPageState();
}

说明

  • 这里用 StatefulWidget,是因为页面需要实时响应用户的滑块和勾选项。

页面状态与初始化

class _PasswordGeneratorPageState extends State<PasswordGeneratorPage> {
  String _password = '';
  double _length = 16;
  bool _includeUppercase = true;
  bool _includeLowercase = true;
  bool _includeNumbers = true;
  bool _includeSymbols = true;

  final _random = Random.secure();

说明

  • 默认长度设为 16,既能兼顾强度也不至于过长。
  • 通过布尔开关控制字符类型组合,便于后续扩展。
  
  void initState() {
    super.initState();
    _generatePassword();
  }

说明

  • 进入页面立刻生成一次密码,避免首次展示为空。

密码展示与基础操作

页面结构先保持简单,真实项目里通常会先把主卡片搭出来,再逐步补齐按钮和配置区块。

appBar: AppBar(title: const Text('密码生成器')),
body: SingleChildScrollView(
  padding: EdgeInsets.all(16.w),
  child: Column(
    children: [
      Card(
        elevation: 4,
        child: Padding(
          padding: EdgeInsets.all(20.w),
          child: Column(
            children: [
              Text('生成的密码', style: TextStyle(fontSize: 14.sp, color: Colors.grey[600])),
            ],
          ),
        ),
      ),
    ],
  ),
),

说明

  • 先用 Card 做视觉聚焦,后续内容会继续往里填。
  • SingleChildScrollView 避免小屏内容溢出。
SelectableText(
  _password,
  style: TextStyle(fontSize: 24.sp, fontWeight: FontWeight.bold, fontFamily: 'monospace'),
  textAlign: TextAlign.center,
),

说明

  • SelectableText 支持用户长按选择复制,体验更贴近桌面端。
  • 使用等宽字体能让口令更易读,尤其是相似字符较多时。
Row(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    ElevatedButton.icon(
      onPressed: _generatePassword,
      icon: const Icon(Icons.refresh),
      label: const Text('重新生成'),
    ),
    SizedBox(width: 12.w),
    OutlinedButton.icon(
      onPressed: _copyPassword,
      icon: const Icon(Icons.copy),
      label: const Text('复制'),
    ),
  ],
),

说明

  • 重新生成和复制是高频操作,放在同一行保证触达性。
  • 使用 OutlinedButton 区分主/次操作,避免视觉负担。
  • 按钮图标采用系统图标,和平台风格一致。

长度与字符类型配置

Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    Text('密码长度', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold)),
    Text('${_length.toInt()} 位', style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold, color: Colors.blue)),
  ],
),

说明

  • 右侧实时显示长度,用户不需要猜测当前值。
Slider(
  value: _length,
  min: 6,
  max: 32,
  divisions: 26,
  onChanged: (value) {
    setState(() {
      _length = value;
      _generatePassword();
    });
  },
),

说明

  • 拖动滑块即时刷新密码,给用户“实时反馈”的感觉。
  • 长度范围 6~32 对常见业务场景足够友好。
CheckboxListTile(
  title: const Text('大写字母 (A-Z)'),
  value: _includeUppercase,
  onChanged: (v) {
    setState(() {
      _includeUppercase = v ?? true;
      _generatePassword();
    });
  },
),

说明

  • 每次勾选变化都触发重新生成,避免出现“配置生效不及时”的错觉。
CheckboxListTile(
  title: const Text('小写字母 (a-z)'),
  value: _includeLowercase,
  onChanged: (v) {
    setState(() {
      _includeLowercase = v ?? true;
      _generatePassword();
    });
  },
),

说明

  • 小写字母默认开启,符合大部分业务默认配置。
CheckboxListTile(
  title: const Text('数字 (0-9)'),
  value: _includeNumbers,
  onChanged: (v) {
    setState(() {
      _includeNumbers = v ?? true;
      _generatePassword();
    });
  },
),

说明

  • 数字是多数平台的硬性要求,所以放在中间更容易被注意到。
CheckboxListTile(
  title: const Text('特殊符号 (!@#\$%^&*)'),
  value: _includeSymbols,
  onChanged: (v) {
    setState(() {
      _includeSymbols = v ?? true;
      _generatePassword();
    });
  },
),

说明

  • 保留一组常见符号,避免部分系统不支持过于复杂的字符。

密码强度反馈

强度提示是产品经理最常问的功能之一,这里做成了一个轻量的进度条,让用户一眼就能判断风险。

Widget _buildStrengthIndicator() {
  final strength = _calculateStrength();
  Color color;
  String label;

说明

  • 先算出强度值,再把颜色和文案准备好,后面渲染会更顺。
if (strength < 30) {
  color = Colors.red;
  label = '弱';
} else if (strength < 60) {
  color = Colors.orange;
  label = '中等';
} else if (strength < 80) {
  color = Colors.blue;
  label = '强';
} else {
  color = Colors.green;
  label = '非常强';
}

说明

  • 强度阈值使用简单分段,便于在真实项目中快速解释给产品或测试。
return Column(
  children: [
    Row(
      children: [
        Expanded(
          child: LinearProgressIndicator(
            value: strength / 100,
            backgroundColor: Colors.grey[200],
            color: color,
            minHeight: 8.h,
          ),
        ),
        SizedBox(width: 12.w),
        Text(label, style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.bold, color: color)),
      ],
    ),

说明

  • 进度条在左,文本提示在右,布局更符合移动端阅读习惯。
    SizedBox(height: 8.h),
    Text('强度: ${strength.toInt()}%', style: TextStyle(fontSize: 12.sp, color: Colors.grey[600])),
  ],
);

说明

  • 进度条 + 标签 + 数字三层信息,用户想要的细节都能覆盖。
double _calculateStrength() {
  double strength = 0;
  strength += (_length / 32) * 40;

说明

  • 长度占 40% 权重,长度拉开后强度变化更明显。
  int types = 0;
  if (_includeUppercase) types++;
  if (_includeLowercase) types++;
  if (_includeNumbers) types++;
  if (_includeSymbols) types++;
  strength += (types / 4) * 60;

  return strength.clamp(0, 100);
}

说明

  • 评分拆为“长度权重 + 字符多样性权重”,逻辑清晰且便于调整。

生成与复制逻辑

void _generatePassword() {
  String chars = '';
  if (_includeUppercase) chars += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  if (_includeLowercase) chars += 'abcdefghijklmnopqrstuvwxyz';
  if (_includeNumbers) chars += '0123456789';
  if (_includeSymbols) chars += '!@#\$%^&*()_+-=[]{}|;:,.<>?';

说明

  • 先拼出可用字符池,后面只需要做随机抽取即可。
  if (chars.isEmpty) {
    setState(() => _password = '请至少选择一种字符类型');
    return;
  }

说明

  • 优先处理“空字符集”这一真实场景,避免异常或空输出。
  setState(() {
    _password = List.generate(
      _length.toInt(),
      (index) => chars[_random.nextInt(chars.length)],
    ).join();
  });
}

说明

  • List.generate + Random.secure() 组合能保证随机性和性能。
void _copyPassword() {
  Clipboard.setData(ClipboardData(text: _password));
  Get.snackbar('成功', '密码已复制', snackPosition: SnackPosition.BOTTOM, duration: const Duration(seconds: 2));
}

说明

  • 复制后立即弹出反馈,比静默成功更符合真实产品习惯。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐