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)。类型为 ImageProviderAssetImageNetworkImage 等)。

  • 图片异步加载,加载完成前可能短暂空白。
  • 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.colordataModuleStyle.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,
);

QrPainterQrImageView 参数大体对应,区别:

  • 必须version(不能省略)。
  • embeddedImage 类型是 ui.Image?,不是 ImageProvider
  • 没有 sizepaddingbackgroundColor(由外层 Container 控制)。

参数组合建议

场景 建议
圆点样式 size >= 160padding: 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

参考