qr_flutter 4.x 完整用法指南
qr_flutter 是 Flutter 生态里常用的二维码展示库:负责把字符串编码成 QR 矩阵,并在界面上绘制出来。它不负责扫码;扫码需配合 mobile_scanner 等库。
本文基于 qr_flutter 4.1.0(底层依赖 qr 包),覆盖每一个常用参数及其作用。
安装
# pubspec.yaml
dependencies:
qr_flutter: ^4.1.0
flutter pub get
最小可用示例:
import 'package:qr_flutter/qr_flutter.dart';
QrImageView(
data: 'https://example.com',
version: QrVersions.auto,
size: 200,
)
二维码结构速览
扫出来的二维码,本质是一个二维黑白矩阵:
- 定位眼(Eye / Finder Pattern):左上、右上、左下三个「回」字框,扫码器靠它定位方向。
- 数据模块(Data Module):中间大量小方块/小圆点,存放真实内容。
- 静默区(Quiet Zone):二维码四周留白,扫码识别需要一定对比度。
flowchart TD
A[data 字符串] --> B{version}
B -->|auto| C[自动计算矩阵大小]
B -->|固定 1-40| D[手动指定矩阵]
A --> E{errorCorrectionLevel}
E --> F[容量 vs 容错权衡]
C --> G[QrImageView 编码]
D --> G
F --> G
G --> H[eyeStyle 画定位角]
G --> I[dataModuleStyle 画数据点]
G --> J[embeddedImage 叠加 Logo]
K[size + padding] --> L[每个模块像素大小]
L --> M{圆点是否可见}
data:能编码什么
data 是必填 String,常见用途:
| 类型 | 示例 | 说明 |
|---|---|---|
| URL | 'https://example.com' |
最常见 |
| 纯文本 | 'Hello World' |
任意 UTF-8 文本 |
| WiFi | 'WIFI:T:WPA;S:MyWiFi;P:***;;' |
标准 WiFi 二维码格式 |
| vCard | 'BEGIN:VCARD\n...' |
名片 |
| JSON | '{"sn":"ABC123","env":"dev"}' |
自定义业务数据 |
注意:
- 不能为空字符串,否则会校验失败。
- 内容越长,需要的 version 越高。
- 加中心 Logo 时,建议把纠错提到 H。
QrImageView:主 Widget
日常展示用 QrImageView。有两种构造方式:
| 构造 | 用途 |
|---|---|
QrImageView({required String data, ...}) |
直接传字符串,内部自动编码 |
QrImageView.withQr({required QrCode qr, ...}) |
先校验/缓存 QrCode,再传入 |
全参数说明
data(必填,第一种构造)
data: 'https://example.com',
要编码进二维码的原始字符串。空字符串或超出当前 version 容量时会进入 errorStateBuilder。
key
Flutter 常规 Widget 标识,用于 diff / 复用,对二维码逻辑无特殊影响。
size
size: 200,
整个二维码 Widget 的宽高(正方形)。
- 不传时,使用父级约束的
shortestSide。 - 屏幕展示建议 160 ~ 240。
- 圆点样式建议
size >= 160,否则每个点可能只有 3~4px,高 DPI 屏上几乎看不见。
实际绘制区域 ≈ size - padding.left - padding.right。
padding
padding: const EdgeInsets.all(10.0), // 默认 10
padding: EdgeInsets.zero, // 无内边距,码点更大
二维码内容区与 Widget 外框之间的留白(静默区的一部分)。
- 默认:
EdgeInsets.all(10.0)。 - padding 越大,中间码图越小。
- 圆点样式建议
padding: EdgeInsets.zero或较小值。
backgroundColor
backgroundColor: Colors.white, // 常用
backgroundColor: Colors.transparent, // 默认
二维码 Widget 底层背景色。展示在彩色页面上时建议用白色,对比度更好、更易扫。
version
version: QrVersions.auto, // 推荐
version: 6, // 固定版本 6
QR 码版本号,决定矩阵大小(模块数)。
QrVersions.auto(推荐):根据data长度 + 纠错等级自动选择。1 ~ 40:手动指定。
| Version | 矩阵模块数 | 大致容量(字节模式,纠错 L) |
|---|---|---|
| 1 | 21×21 | ~17 字节 |
| 3 | 29×29 | ~44 字节 |
| 6 | 41×41 | ~134 字节 |
| 10 | 57×57 | ~271 字节 |
固定 version 太小装不下 data 时会校验失败。
errorCorrectionLevel
errorCorrectionLevel: QrErrorCorrectLevel.L, // 默认
纠错等级:越高越能容忍遮挡/污损,但同样 version 下可存数据越少。
| 常量 | 可恢复比例 | 典型场景 |
|---|---|---|
QrErrorCorrectLevel.L |
~7% | 普通展示,数据尽量短 |
QrErrorCorrectLevel.M |
~15% | 一般业务 |
QrErrorCorrectLevel.Q |
~25% | 有一定遮挡 |
QrErrorCorrectLevel.H |
~30% | 中心放 Logo 时推荐 |
eyeStyle
eyeStyle: const QrEyeStyle(
eyeShape: QrEyeShape.circle, // 或 square
color: Colors.black,
),
控制三个定位角(Eye)的形状和颜色。详见下文「样式类」。
dataModuleStyle
dataModuleStyle: const QrDataModuleStyle(
dataModuleShape: QrDataModuleShape.circle, // 或 square
color: Colors.black,
),
控制中间数据区每个小点的形状和颜色。
gapless
gapless: true, // 默认
gapless: false,
数据模块之间是否有缝隙。
true:相邻模块紧贴(默认,更紧凑、更易扫)。false:每个模块之间留约 1px 间隙。
embeddedImage
embeddedImage: const AssetImage('images/logo.webp'),
在二维码正中心叠加一张图(Logo)。类型为 ImageProvider(AssetImage、NetworkImage 等)。
- 图片异步加载,加载完成前可能短暂空白。
- Logo 不要太大,一般占二维码 15%~25%。
- 配合
errorCorrectionLevel: QrErrorCorrectLevel.H使用。
embeddedImageStyle
embeddedImageStyle: const QrEmbeddedImageStyle(
size: Size(36, 36),
),
控制中心 Logo 的尺寸和可选染色。不传 size 时,默认约为二维码短边的 25%。
embeddedImageEmitsError
embeddedImageEmitsError: false, // 默认
embeddedImageEmitsError: true,
Logo 加载失败时是否触发错误回调。
false:忽略 Logo,只显示二维码。true:调用errorStateBuilder。
errorStateBuilder
errorStateBuilder: (context, error) {
return Text('二维码生成失败', style: TextStyle(color: Colors.red));
},
编码失败或(在 embeddedImageEmitsError: true 时)Logo 加载失败时的自定义错误 UI。
默认不设置时显示空白 Container,容易误以为没渲染。生产环境建议务必设置。
常见触发原因:
data为空- 固定
version太小 - Logo 路径错误且
embeddedImageEmitsError: true
constrainErrorBounds
constrainErrorBounds: true, // 默认
错误 Widget 是否限制在二维码原定尺寸内。false 时错误 UI 可自由扩展,可能导致布局跳动。
semanticsLabel
semanticsLabel: '示例链接二维码',
无障碍读屏描述。默认:'qr code'。
foregroundColor(已废弃)
已废弃,请改用 eyeStyle.color 和 dataModuleStyle.color。
枚举值
QrEyeShape — 定位角形状
| 值 | 效果 |
|---|---|
QrEyeShape.square |
方形回字框(传统样式) |
QrEyeShape.circle |
圆形回字框(App 常见风格) |
QrDataModuleShape — 数据点形状
| 值 | 效果 |
|---|---|
QrDataModuleShape.square |
方形小方块 |
QrDataModuleShape.circle |
圆形小圆点 |
「圆形二维码」通常指:圆点 + 圆眼,不是把整个 QR 裁成圆形(裁圆可能影响识别)。
样式类详解
QrEyeStyle
const QrEyeStyle({
QrEyeShape? eyeShape, // 默认 square
Color? color, // 默认 Colors.black
})
| 字段 | 作用 |
|---|---|
eyeShape |
三个定位角的形状 |
color |
定位角颜色 |
QrDataModuleStyle
const QrDataModuleStyle({
QrDataModuleShape? dataModuleShape, // 默认 square
Color? color, // 默认 Colors.black
})
| 字段 | 作用 |
|---|---|
dataModuleShape |
数据区每个模块的形状 |
color |
数据区颜色 |
Eye 和 Module 可以分别设色,例如蓝眼 + 浅蓝点。
QrEmbeddedImageStyle
const QrEmbeddedImageStyle({
Size? size,
Color? color, // 可选:给 Logo 染色
})
| 字段 | 作用 |
|---|---|
size |
Logo 宽高;只设一边时按比例缩放 |
color |
用 ColorFilter 给 Logo 着色 |
QrVersions 与 QrErrorCorrectLevel
QrVersions.auto // 自动选版本(推荐)
QrVersions.min // 最小支持版本(1)
QrVersions.max // 最大支持版本(40)
QrVersions.isSupportedVersion(6) // 检查版本是否合法
纠错常量:
QrErrorCorrectLevel.L // 低,~7%
QrErrorCorrectLevel.M // 中,~15%
QrErrorCorrectLevel.Q // 较高,~25%
QrErrorCorrectLevel.H // 高,~30%,适合 Logo
完整示例
QrImageView(
data: 'https://example.com',
size: 200,
padding: EdgeInsets.zero,
backgroundColor: Colors.white,
version: QrVersions.auto,
errorCorrectionLevel: QrErrorCorrectLevel.H,
gapless: true,
eyeStyle: const QrEyeStyle(
eyeShape: QrEyeShape.circle,
color: Color(0xFF1F68D5),
),
dataModuleStyle: const QrDataModuleStyle(
dataModuleShape: QrDataModuleShape.circle,
color: Color(0xFF8399FF),
),
embeddedImage: const AssetImage('images/logo.webp'),
embeddedImageStyle: const QrEmbeddedImageStyle(size: Size(40, 40)),
embeddedImageEmitsError: false,
errorStateBuilder: (context, error) => Text('失败: $error'),
semanticsLabel: '示例链接二维码',
)
外层「圆形白底」不是库自带参数,用 Flutter 容器实现:
Container(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: QrImageView(
data: 'https://example.com',
size: 168,
padding: EdgeInsets.zero,
eyeStyle: const QrEyeStyle(
eyeShape: QrEyeShape.circle,
color: Colors.black,
),
dataModuleStyle: const QrDataModuleStyle(
dataModuleShape: QrDataModuleShape.circle,
color: Colors.black,
),
),
)
进阶:QrValidator 预校验
final result = QrValidator.validate(
data: 'https://example.com',
version: QrVersions.auto,
errorCorrectionLevel: QrErrorCorrectLevel.L,
);
if (result.isValid) {
final qrCode = result.qrCode;
// 可直接给 QrImageView.withQr 使用
} else {
print(result.error);
}
QrValidationResult 字段:
| 字段 | 含义 |
|---|---|
status |
valid / contentTooLong / error |
qrCode |
编码成功的 QrCode 对象 |
error |
失败时的异常 |
isValid |
是否成功 |
进阶:QrPainter 导出图片
不只要 Widget,还要保存成 PNG 时:
import 'dart:ui' as ui;
import 'package:qr_flutter/qr_flutter.dart';
final painter = QrPainter(
data: 'https://example.com',
version: QrVersions.auto,
errorCorrectionLevel: QrErrorCorrectLevel.H,
eyeStyle: const QrEyeStyle(
eyeShape: QrEyeShape.circle,
color: Colors.black,
),
dataModuleStyle: const QrDataModuleStyle(
dataModuleShape: QrDataModuleShape.circle,
color: Colors.black,
),
);
// 导出 ui.Image
final image = await painter.toImage(512);
// 或导出 PNG 字节
final bytes = await painter.toImageData(
512,
format: ui.ImageByteFormat.png,
);
QrPainter 与 QrImageView 参数大体对应,区别:
- 必须传
version(不能省略)。 embeddedImage类型是ui.Image?,不是ImageProvider。- 没有
size、padding、backgroundColor(由外层 Container 控制)。
参数组合建议
| 场景 | 建议 |
|---|---|
| 圆点样式 | size >= 160,padding: EdgeInsets.zero |
| 中心 Logo | errorCorrectionLevel: H,Logo ≤ 25% 面积 |
| 长 URL | version: auto,或手动提高 version |
| 彩色背景页 | backgroundColor: Colors.white |
| 生产环境 | 必须设 errorStateBuilder |
常见坑
| 现象 | 原因 | 处理 |
|---|---|---|
| 圆点码「看不见」 | size 太小 + 默认 padding |
加大 size,减小 padding |
| 整段 QR 区域空白 | data 为空或 version 不够 |
设 errorStateBuilder 看错误 |
| Logo 码扫不出 | 纠错太低或 Logo 太大 | 用 H,缩小 Logo |
| 只有第一个样式正常 | 某个子 Widget build 抛错 | 去掉空 data、坏图片路径 |
| 透明背景扫不出 | 对比度不够 | 加白底 backgroundColor |