Android
主页 > 软件编程 > Android >

基于Flutter实现扫描二维码功能

2024-11-22 | 佚名 | 点击:

在今天的移动开发中,二维码扫描已经成为了常见的功能之一。Flutter作为一款跨平台的开发框架,提供了丰富的插件和功能,使得开发者可以轻松实现二维码扫描以及图像识别功能。本文将介绍如何在Flutter中通过结合 scan 插件、permission_handler 插件以及 image_picker 插件,实现二维码扫描和从相册选择二维码图片的功能。

效果图

1、相机扫描二维码

2、相册选择二维码并扫描

1、项目依赖

首先,我们需要在 pubspec.yaml 文件中添加以下依赖:

1

2

3

4

5

6

dependencies:

  flutter:

    sdk: flutter

  scan: ^1.6.0          # 用于扫描二维码

  permission_handler: ^10.2.0  # 用于权限请求

  image_picker: ^1.0.7    # 用于从相册选择图片

这些插件的作用如下:

scan:提供二维码扫描功能。

permission_handler:用于请求相机权限,确保用户授权后才能使用相机进行二维码扫描。

image_picker:允许从相册选择图片,便于用户上传二维码图片进行识别。

2、配置权限

在 Android 平台上,为了使用相机功能,需要在 AndroidManifest.xml 文件中添加必要的权限:

1

<uses-permission android:name="android.permission.CAMERA"/>

在 IOS平台上修改 Info.plist 文件,打开 ios/Runner/Info.plist 文件,确保添加以下配置(iOS用虚拟机是调试不了的,实测打不开相机):

<key>NSCameraUsageDescription</key>
<string>需要访问相机用于二维码扫描</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册选择图片</string>

3、实现二维码扫描功能

接下来,我们来看看如何实现二维码扫描功能。我们需要创建一个主界面,在其中添加按钮来请求相机权限,并进行二维码扫描。

没有二维码的小伙伴可以查看上一篇文章:Flutter 生成二维码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

import 'package:flutter/material.dart';

import 'package:scan/scan.dart';  // 导入 scan 插件

import 'package:permission_handler/permission_handler.dart';  // 导入权限请求插件

import 'package:image_picker/image_picker.dart';  // 导入相册选择插件

  

void main() {

  runApp(MyApp());

}

  

class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      theme: ThemeData(

        primarySwatch: Colors.blue,

        visualDensity: VisualDensity.adaptivePlatformDensity,

      ),

      home: QRScannerScreen(),

    );

  }

}

  

class QRScannerScreen extends StatefulWidget {

  @override

  _QRScannerScreenState createState() => _QRScannerScreenState();

}

  

class _QRScannerScreenState extends State<QRScannerScreen> {

  ScanController controller = ScanController();  // 创建扫描控制器

  String qrcode = 'Unknown';  // 默认二维码内容

  

  // 执行二维码扫描前请求权限

  _requestPermissions() async {

    var cameraStatus = await Permission.camera.request();

    if (cameraStatus.isGranted) {

      // 权限已授予,开始扫描

      print("相机权限已授予,开始扫描");

      // 点击按钮后跳转到全屏扫描界面

      Navigator.push(

        context,

        MaterialPageRoute(builder: (context) => QRScanPage(controller: controller)),

      ).then((result) {

        controller.pause();  // 暂停扫描

        // 扫描完成后获取返回的二维码数据并更新显示

        if (result != null) {

          setState(() {

            qrcode = result;  // 更新二维码内容

          });

        }

      });

    } else {

      // 权限被拒绝或未授权

      print("相机权限未授予,请授权");

      // 可以引导用户去设置页面

    }

  }

  

  // 选择相册中的二维码图片

  _selectImageFromGallery() async {

    final picker = ImagePicker();

    final pickedFile = await picker.pickImage(source: ImageSource.gallery);

    if (pickedFile != null) {

      String result = await Scan.parse(pickedFile.path) ?? '';  // 解析选中的图片

      setState(() {

        qrcode = result;  // 更新二维码内容

      });

    }

  }

  

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: const Text('二维码扫描器'),

      ),

      body: Center(

        child: Column(

          mainAxisAlignment: MainAxisAlignment.center,

          children: <Widget>[

            // 显示二维码扫描结果

            const Text(

              '二维码扫描结果:',

              style: TextStyle(fontSize: 18),

            ),

            const SizedBox(height: 10),

            Text(

              qrcode,

              style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),

            ),

            const SizedBox(height: 30),

            // 扫描按钮

            ElevatedButton(

              onPressed: _requestPermissions,  // 执行权限请求

              child: const Text('请求权限并开始扫描'),

            ),

            const SizedBox(height: 30),

            // 选择相册按钮

            ElevatedButton(

              onPressed: _selectImageFromGallery,  // 选择相册

              child: const Text('从相册选择二维码图片'),

            ),

          ],

        ),

      ),

    );

  }

}

代码解释

权限请求:我们通过 permission_handler 插件请求相机权限。用户允许后才能进行二维码扫描。

扫描功能:我们使用 ScanController 来控制扫描过程。点击按钮后,跳转到全屏扫描页面 QRScanPage,在该页面,用户可以通过相机扫描二维码。

从相册选择图片:我们利用 image_picker 插件允许用户从相册选择图片,并解析二维码。

4、全屏扫描页面

当用户点击扫描按钮时,应用会进入一个全屏扫描页面。在该页面中,二维码扫描功能全屏展示,并且添加了一个“选择相册”按钮,让用户可以在扫描时直接选择图片。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

class QRScanPage extends StatelessWidget {

  final ScanController controller;

  const QRScanPage({super.key, required this.controller});

  

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(title: Text('扫描二维码')),

      body: Stack(

        alignment: Alignment.center,

        children: [

          // 使用 ScanView 进行全屏扫描

          ScanView(

            controller: controller,

            scanAreaScale: 0.8,  // 设置扫描区域占满整个屏幕

            scanLineColor: Colors.green.shade400,  // 设置扫描线颜色

            onCapture: (data) {

              // 扫描到二维码后,返回数据

              controller.pause();  // 暂停扫描

              Navigator.pop(context, data);  // 返回扫描结果

            },

          ),

          // 在屏幕上添加选择相册按钮

          Positioned(

            bottom: 100,

            child: ElevatedButton(

              onPressed: () async {

                // 选择相册

                final picker = ImagePicker();

                final pickedFile = await picker.pickImage(source: ImageSource.gallery);

                if (pickedFile != null) {

                  String result = await Scan.parse(pickedFile.path) ?? '';  // 解析选中的图片

                  Navigator.pop(context, result);  // 返回二维码结果

                }

              },

              child: const Text(

                '选择相册',

                style: TextStyle(

                  fontSize: 18,

                  color: Colors.white,

                  fontWeight: FontWeight.bold,

                ),

              ),

            ),

          ),

        ],

      ),

    );

  }

}

代码解释

ScanView:提供扫描界面,支持自定义扫描区域和扫描线颜色。

onCapture:当扫描到二维码时,会返回扫描结果并暂停扫描。

选择相册按钮:点击按钮可以让用户从相册选择二维码图片进行解析。

完整demo

可以直接复制到新项目跑起来

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

import 'package:flutter/material.dart';

import 'package:scan/scan.dart';  // 导入 scan 插件

import 'package:permission_handler/permission_handler.dart';  // 导入权限请求插件

import 'package:image_picker/image_picker.dart';  // 导入相册选择插件

  

void main() {

  runApp(MyApp());

}

  

class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      theme: ThemeData(

        primarySwatch: Colors.blue,

        visualDensity: VisualDensity.adaptivePlatformDensity,

      ),

      home: QRScannerScreen(),

    );

  }

}

  

class QRScannerScreen extends StatefulWidget {

  @override

  _QRScannerScreenState createState() => _QRScannerScreenState();

}

  

class _QRScannerScreenState extends State<QRScannerScreen> {

  ScanController controller = ScanController();  // 创建扫描控制器

  String qrcode = 'Unknown';  // 默认二维码内容

  

  // 执行二维码扫描前请求权限

  _requestPermissions() async {

    var cameraStatus = await Permission.camera.request();

    if (cameraStatus.isGranted) {

      // 权限已授予,开始扫描

      print("相机权限已授予,开始扫描");

      // 点击按钮后跳转到全屏扫描界面

      Navigator.push(

        context,

        MaterialPageRoute(builder: (context) => QRScanPage(controller: controller)),

      ).then((result) {

        controller.pause();  // 暂停扫描

        // 扫描完成后获取返回的二维码数据并更新显示

        if (result != null) {

          setState(() {

            qrcode = result;  // 更新二维码内容

          });

        }

      });

    } else {

      // 权限被拒绝或未授权

      print("相机权限未授予,请授权");

      // 可以引导用户去设置页面

    }

  }

  

  // 选择相册中的二维码图片

  _selectImageFromGallery() async {

    final picker = ImagePicker();

    final pickedFile = await picker.pickImage(source: ImageSource.gallery);

    if (pickedFile != null) {

      String result = await Scan.parse(pickedFile.path) ?? '';  // 解析选中的图片

      setState(() {

        qrcode = result;  // 更新二维码内容

      });

    }

  }

  

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: const Text('二维码扫描器'),

      ),

      body: Center(

        child: Column(

          mainAxisAlignment: MainAxisAlignment.center,

          children: <Widget>[

            // 显示二维码扫描结果

            const Text(

              '二维码扫描结果:',

              style: TextStyle(fontSize: 18),

            ),

            const SizedBox(height: 10),

            Text(

              qrcode,

              style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),

            ),

            const SizedBox(height: 30),

            // 扫描按钮

            ElevatedButton(

              onPressed: _requestPermissions,  // 执行权限请求

              child: const Text('请求权限并开始扫描'),

            ),

            const SizedBox(height: 30),

            // 选择相册按钮

            ElevatedButton(

              onPressed: _selectImageFromGallery,  // 选择相册

              child: const Text('从相册选择二维码图片'),

            ),

          ],

        ),

      ),

    );

  }

}

  

class QRScanPage extends StatelessWidget {

  final ScanController controller;

  const QRScanPage({super.key, required this.controller});

  

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(title: Text('扫描二维码')),

      body: Stack(

        alignment: Alignment.center,

        children: [

          // 使用 ScanView 进行全屏扫描

          ScanView(

            controller: controller,

            scanAreaScale: 0.8,  // 设置扫描区域占满整个屏幕

            scanLineColor: Colors.green.shade400,  // 设置扫描线颜色

            onCapture: (data) {

              // 扫描到二维码后,返回数据

              controller.pause();  // 暂停扫描

              Navigator.pop(context, data);  // 返回扫描结果

            },

          ),

          // 在屏幕上添加选择相册按钮

          Positioned(

            bottom: 100,

            child: ElevatedButton(

              onPressed: () async {

                // 选择相册

                final picker = ImagePicker();

                final pickedFile = await picker.pickImage(source: ImageSource.gallery);

                if (pickedFile != null) {

                  String result = await Scan.parse(pickedFile.path) ?? '';  // 解析选中的图片

                  Navigator.pop(context, result);  // 返回二维码结果

                }

              },

              child: const Text(

                '选择相册',

                style: TextStyle(

                  fontSize: 18,

                  color: Colors.white,

                  fontWeight: FontWeight.bold,

                ),

              ),

            ),

          ),

        ],

      ),

    );

  }

}

总结

本文展示了如何在 Flutter 中实现二维码扫描和从相册选择二维码图片的功能。通过使用 scan、permission_handler 和 image_picker 插件,我们可以轻松地添加二维码扫描和图片识别功能。在开发实际应用时,可能还需要处理更多细节,例如处理不同平台的权限请求、优化扫描体验等。

原文链接:
相关文章
最新更新