Flutter for OpenHarmony Web开发助手App实战:二维码生成
二维码在现代应用中无处不在。今天我们来实现一个二维码生成工具,让文本、URL等信息快速转换为二维码。

二维码在现代应用中无处不在。今天我们来实现一个二维码生成工具,让文本、URL等信息快速转换为二维码。
功能规划
二维码生成工具需要:
文本转二维码:输入任意文本生成二维码。
尺寸调整:自定义二维码大小。
颜色定制:修改二维码的前景色和背景色。
容错级别:选择不同的容错等级。
保存分享:保存二维码图片或分享。
完整代码实现
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
class QrCodeGeneratorPage extends StatefulWidget {
const QrCodeGeneratorPage({Key? key}) : super(key: key);
State<QrCodeGeneratorPage> createState() => _QrCodeGeneratorPageState();
}
class _QrCodeGeneratorPageState extends State<QrCodeGeneratorPage> {
final TextEditingController _textController = TextEditingController();
double _qrSize = 200;
Color _foregroundColor = Colors.black;
Color _backgroundColor = Colors.white;
int _errorCorrectionLevel = 0; // 0: L, 1: M, 2: Q, 3: H
void initState() {
super.initState();
_textController.text = 'https://openharmonycrossplatform.csdn.net';
}
void dispose() {
_textController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('二维码生成'),
actions: [
IconButton(
icon: const Icon(Icons.download),
onPressed: _saveQrCode,
tooltip: '保存二维码',
),
],
),
body: SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Column(
children: [
// 输入区域
_buildInputSection(),
SizedBox(height: 24.h),
// 二维码预览
_buildQrCodePreview(),
SizedBox(height: 24.h),
// 自定义选项
_buildCustomOptions(),
SizedBox(height: 24.h),
// 快捷模板
_buildQuickTemplates(),
],
),
),
);
}
Widget _buildInputSection() {
return Card(
elevation: 2,
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.edit, size: 20.sp, color: Colors.blue),
SizedBox(width: 8.w),
Text(
'输入内容',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
],
),
SizedBox(height: 12.h),
TextField(
controller: _textController,
maxLines: 3,
style: TextStyle(fontSize: 14.sp),
decoration: InputDecoration(
hintText: '输入文本、URL、电话号码等...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
),
contentPadding: EdgeInsets.all(12.w),
),
onChanged: (_) => setState(() {}),
),
SizedBox(height: 8.h),
Text(
'字符数: ${_textController.text.length}',
style: TextStyle(fontSize: 12.sp, color: Colors.grey[600]),
),
],
),
),
);
}
Widget _buildQrCodePreview() {
return Card(
elevation: 4,
child: Container(
padding: EdgeInsets.all(24.w),
child: Column(
children: [
Text(
'二维码预览',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16.h),
Container(
width: _qrSize,
height: _qrSize,
decoration: BoxDecoration(
color: _backgroundColor,
borderRadius: BorderRadius.circular(8.r),
border: Border.all(color: Colors.grey[300]!),
),
child: _textController.text.isEmpty
? Center(
child: Text(
'输入内容生成二维码',
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey[600],
),
),
)
: _buildQrCodeWidget(),
),
SizedBox(height: 16.h),
Text(
'尺寸: ${_qrSize.toInt()}x${_qrSize.toInt()}',
style: TextStyle(fontSize: 12.sp, color: Colors.grey[600]),
),
],
),
),
);
}
Widget _buildQrCodeWidget() {
// 简化的二维码显示
// 实际项目中使用 qr_flutter 包
return CustomPaint(
painter: QrCodePainter(
data: _textController.text,
foregroundColor: _foregroundColor,
),
size: Size(_qrSize, _qrSize),
);
}
Widget _buildCustomOptions() {
return Card(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'自定义选项',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16.h),
// 尺寸调整
Text('尺寸', style: TextStyle(fontSize: 14.sp)),
Slider(
value: _qrSize,
min: 100,
max: 300,
divisions: 20,
label: _qrSize.toInt().toString(),
onChanged: (value) {
setState(() => _qrSize = value);
},
),
SizedBox(height: 16.h),
// 颜色选择
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('前景色', style: TextStyle(fontSize: 14.sp)),
SizedBox(height: 8.h),
_buildColorPicker(_foregroundColor, (color) {
setState(() => _foregroundColor = color);
}),
],
),
),
SizedBox(width: 16.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('背景色', style: TextStyle(fontSize: 14.sp)),
SizedBox(height: 8.h),
_buildColorPicker(_backgroundColor, (color) {
setState(() => _backgroundColor = color);
}),
],
),
),
],
),
SizedBox(height: 16.h),
// 容错级别
Text('容错级别', style: TextStyle(fontSize: 14.sp)),
SizedBox(height: 8.h),
Wrap(
spacing: 8.w,
children: [
_buildErrorLevelChip('L (7%)', 0),
_buildErrorLevelChip('M (15%)', 1),
_buildErrorLevelChip('Q (25%)', 2),
_buildErrorLevelChip('H (30%)', 3),
],
),
],
),
),
);
}
Widget _buildColorPicker(Color currentColor, Function(Color) onColorChanged) {
final colors = [
Colors.black,
Colors.white,
Colors.red,
Colors.blue,
Colors.green,
Colors.orange,
Colors.purple,
Colors.pink,
];
return Wrap(
spacing: 8.w,
children: colors.map((color) {
final isSelected = color.value == currentColor.value;
return GestureDetector(
onTap: () => onColorChanged(color),
child: Container(
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
border: Border.all(
color: isSelected ? Colors.blue : Colors.grey[300]!,
width: isSelected ? 3 : 1,
),
),
),
);
}).toList(),
);
}
Widget _buildErrorLevelChip(String label, int level) {
final isSelected = _errorCorrectionLevel == level;
return ChoiceChip(
label: Text(label),
selected: isSelected,
onSelected: (selected) {
if (selected) {
setState(() => _errorCorrectionLevel = level);
}
},
);
}
Widget _buildQuickTemplates() {
final templates = [
{'icon': Icons.link, 'title': 'URL', 'prefix': 'https://'},
{'icon': Icons.email, 'title': '邮箱', 'prefix': 'mailto:'},
{'icon': Icons.phone, 'title': '电话', 'prefix': 'tel:'},
{'icon': Icons.message, 'title': '短信', 'prefix': 'sms:'},
{'icon': Icons.wifi, 'title': 'WiFi', 'prefix': 'WIFI:T:WPA;S:;P:;;'},
{'icon': Icons.location_on, 'title': '位置', 'prefix': 'geo:'},
];
return Card(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'快捷模板',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 12.h),
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 8.w,
mainAxisSpacing: 8.h,
childAspectRatio: 1.5,
),
itemCount: templates.length,
itemBuilder: (context, index) {
final template = templates[index];
return InkWell(
onTap: () {
_textController.text = template['prefix'] as String;
setState(() {});
},
child: Container(
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.circular(8.r),
border: Border.all(color: Colors.blue[200]!),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
template['icon'] as IconData,
color: Colors.blue,
size: 24.sp,
),
SizedBox(height: 4.h),
Text(
template['title'] as String,
style: TextStyle(
fontSize: 12.sp,
color: Colors.blue[900],
),
),
],
),
),
);
},
),
],
),
),
);
}
void _saveQrCode() {
if (_textController.text.isEmpty) {
Get.snackbar('提示', '请先输入内容',
snackPosition: SnackPosition.BOTTOM);
return;
}
// 实际项目中这里会保存二维码图片
Get.snackbar('提示', '保存功能需要文件系统权限',
snackPosition: SnackPosition.BOTTOM);
}
}
// 简化的二维码绘制器
class QrCodePainter extends CustomPainter {
final String data;
final Color foregroundColor;
QrCodePainter({
required this.data,
required this.foregroundColor,
});
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = foregroundColor
..style = PaintingStyle.fill;
// 简化的二维码模拟
// 实际项目中使用 qr_flutter 包生成真实的二维码
final cellSize = size.width / 25;
// 绘制定位标记
_drawPositionMarker(canvas, paint, 0, 0, cellSize);
_drawPositionMarker(canvas, paint, size.width - 7 * cellSize, 0, cellSize);
_drawPositionMarker(canvas, paint, 0, size.height - 7 * cellSize, cellSize);
// 绘制随机模式(模拟数据)
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 25; j++) {
if ((i + j + data.length) % 3 == 0) {
canvas.drawRect(
Rect.fromLTWH(i * cellSize, j * cellSize, cellSize * 0.9, cellSize * 0.9),
paint,
);
}
}
}
}
void _drawPositionMarker(Canvas canvas, Paint paint, double x, double y, double cellSize) {
// 外框
canvas.drawRect(
Rect.fromLTWH(x, y, cellSize * 7, cellSize * 7),
paint,
);
// 内部空白
canvas.drawRect(
Rect.fromLTWH(x + cellSize, y + cellSize, cellSize * 5, cellSize * 5),
Paint()..color = Colors.white,
);
// 中心点
canvas.drawRect(
Rect.fromLTWH(x + cellSize * 2, y + cellSize * 2, cellSize * 3, cellSize * 3),
paint,
);
}
bool shouldRepaint(QrCodePainter oldDelegate) {
return oldDelegate.data != data || oldDelegate.foregroundColor != foregroundColor;
}
}
二维码生成原理
二维码是一种二维条码,可以存储大量信息。实际项目中使用qr_flutter包:
import 'package:qr_flutter/qr_flutter.dart';
QrImageView(
data: _textController.text,
version: QrVersions.auto,
size: _qrSize,
foregroundColor: _foregroundColor,
backgroundColor: _backgroundColor,
)
我们的示例代码用CustomPainter模拟了二维码的外观。
尺寸调整
使用Slider控制二维码大小:
Slider(
value: _qrSize,
min: 100,
max: 300,
divisions: 20,
label: _qrSize.toInt().toString(),
onChanged: (value) {
setState(() => _qrSize = value);
},
)
范围从100到300像素,分成20个刻度。
颜色定制
提供前景色和背景色选择:
Widget _buildColorPicker(Color currentColor, Function(Color) onColorChanged) {
final colors = [Colors.black, Colors.white, Colors.red, ...];
return Wrap(
children: colors.map((color) {
return GestureDetector(
onTap: () => onColorChanged(color),
child: Container(
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
),
);
}).toList(),
);
}
用圆形色块展示可选颜色,点击即可切换。
容错级别
二维码有四个容错级别:
L级:约7%的容错能力
M级:约15%的容错能力
Q级:约25%的容错能力
H级:约30%的容错能力
ChoiceChip(
label: Text('L (7%)'),
selected: _errorCorrectionLevel == 0,
onSelected: (selected) {
setState(() => _errorCorrectionLevel = 0);
},
)
容错级别越高,二维码越复杂,但损坏后仍能识别。
快捷模板
提供常用的二维码类型模板:
final templates = [
{'icon': Icons.link, 'title': 'URL', 'prefix': 'https://'},
{'icon': Icons.email, 'title': '邮箱', 'prefix': 'mailto:'},
{'icon': Icons.phone, 'title': '电话', 'prefix': 'tel:'},
// ...
];
点击模板自动填充对应的前缀,用户只需补充具体内容。
WiFi二维码
WiFi二维码的格式比较特殊:
WIFI:T:WPA;S:网络名称;P:密码;;
T是加密类型(WPA/WEP/nopass),S是SSID,P是密码。
保存功能
保存二维码为图片:
void _saveQrCode() {
// 使用 RepaintBoundary 捕获Widget为图片
// 然后保存到相册
}
实际实现需要文件系统权限和图片处理库。
功能扩展建议
Logo嵌入:在二维码中心添加Logo。
样式定制:圆角、渐变等特殊样式。
批量生成:一次生成多个二维码。
扫码功能:集成扫码识别二维码内容。
vCard支持:生成电子名片二维码。
实战经验
做二维码工具时,最重要的是提供足够的定制选项。
一开始我只支持黑白二维码,但用户反馈说想要彩色的。加入颜色选择后,工具的实用性大大提升。
还有一个细节:快捷模板。很多人不知道二维码可以存储电话、邮箱等特殊格式。提供模板后,用户可以快速生成各种类型的二维码。
小结
二维码生成器通过简洁的界面和丰富的定制选项,让二维码生成变得简单。支持多种内容类型,满足不同场景的需求。
记住:工具要考虑用户的实际需求,提供常用的功能和模板。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)