请添加图片描述

二维码生成是扫码应用的另一个核心功能。用户不仅需要扫描别人的二维码,也需要生成自己的二维码分享给别人。这篇文章介绍生成主界面的实现,包括二维码类型选择、快速生成入口和样式设置入口。

生成界面的设计思路

生成界面需要展示多种二维码类型供用户选择,我采用了网格布局来展示这些选项。每种类型用一个卡片表示,包含图标和名称,点击后跳转到对应的生成页面。

除了类型选择,还提供了快速生成入口,用户可以直接输入文本或网址快速生成二维码,不需要先选择类型。底部还有样式设置入口,让用户可以自定义二维码的颜色、大小和 Logo。

GenerateView 的基础结构

先来看文件的导入和类定义:

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../routes/app_pages.dart';
import 'generate_controller.dart';

class GenerateView extends StatelessWidget {
  const GenerateView({super.key});

导入了 Flutter 的 material 组件库、GetX 框架、flutter_screenutil 屏幕适配库,以及路由配置和生成控制器。

GenerateView 使用 StatelessWidget,状态管理交给 GenerateController 处理。这种分离让视图层保持简洁,业务逻辑集中在控制器中。

控制器的初始化

build 方法开始时初始化控制器:

  
  Widget build(BuildContext context) {
    Get.put(GenerateController());
    
    return Scaffold(
      appBar: AppBar(
        title: const Text('生成二维码'),
        automaticallyImplyLeading: false,
        actions: [
          IconButton(
            icon: const Icon(Icons.history),
            onPressed: () => Get.toNamed(Routes.GENERATE_HISTORY),
          ),
        ],
      ),

Get.put 创建 GenerateController 实例并注册到依赖容器中。这样在子页面中可以通过 Get.find 获取同一个控制器实例,实现状态共享。

AppBar 的 title 设置为"生成二维码"。automaticallyImplyLeading 设为 false 隐藏返回按钮,因为这是底部导航的一个 Tab 页。actions 中放了一个历史记录按钮,点击跳转到生成历史页面。

页面主体的布局

body 使用 SingleChildScrollView 包裹,支持内容超出屏幕时滚动:

      body: SingleChildScrollView(
        padding: EdgeInsets.all(16.w),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              '选择二维码类型',
              style: TextStyle(
                fontSize: 18.sp,
                fontWeight: FontWeight.w600,
              ),
            ),
            SizedBox(height: 16.h),

SingleChildScrollView 让整个页面可以滚动,适合内容较多的页面。padding 设置四周 16.w 的内边距。

Column 的 crossAxisAlignment 设为 start,让子组件左对齐。第一个元素是标题文字"选择二维码类型",字号 18.sp,字重 w600 表示半粗体。SizedBox 添加 16.h 的垂直间距。

二维码类型网格

使用 GridView.count 创建固定列数的网格:

            GridView.count(
              shrinkWrap: true,
              physics: const NeverScrollableScrollPhysics(),
              crossAxisCount: 3,
              mainAxisSpacing: 12.h,
              crossAxisSpacing: 12.w,
              childAspectRatio: 1,
              children: [
                _QrTypeCard(
                  icon: Icons.text_fields,
                  label: '文本',
                  color: Colors.blue,
                  onTap: () => Get.toNamed(Routes.TEXT_QR),
                ),

shrinkWrap 设为 true 让 GridView 根据内容自适应高度,而不是占满可用空间。physics 设为 NeverScrollableScrollPhysics 禁用 GridView 自身的滚动,因为外层已经有 SingleChildScrollView 了。

crossAxisCount 设为 3 表示每行 3 个卡片。mainAxisSpacing 和 crossAxisSpacing 分别设置垂直和水平间距。childAspectRatio 设为 1 表示每个卡片是正方形。

第一个卡片是文本类型,使用 Icons.text_fields 图标,蓝色主题,点击跳转到文本二维码生成页面。

更多二维码类型

网格中包含 9 种二维码类型:

                _QrTypeCard(
                  icon: Icons.link,
                  label: '网址',
                  color: Colors.green,
                  onTap: () => Get.toNamed(Routes.URL_QR),
                ),
                _QrTypeCard(
                  icon: Icons.wifi,
                  label: 'WiFi',
                  color: Colors.orange,
                  onTap: () => Get.toNamed(Routes.WIFI_QR),
                ),
                _QrTypeCard(
                  icon: Icons.person,
                  label: '联系人',
                  color: Colors.purple,
                  onTap: () => Get.toNamed(Routes.CONTACT_QR),
                ),

每种类型使用不同的图标和颜色,方便用户快速识别。网址用绿色链接图标,WiFi 用橙色无线图标,联系人用紫色人物图标。

这种设计让用户一眼就能找到需要的类型,不需要阅读文字就能通过颜色和图标区分。

继续添加类型卡片

剩余的类型卡片:

                _QrTypeCard(
                  icon: Icons.email,
                  label: '邮箱',
                  color: Colors.red,
                  onTap: () => Get.toNamed(Routes.EMAIL_QR),
                ),
                _QrTypeCard(
                  icon: Icons.phone,
                  label: '电话',
                  color: Colors.teal,
                  onTap: () => Get.toNamed(Routes.PHONE_QR),
                ),
                _QrTypeCard(
                  icon: Icons.sms,
                  label: '短信',
                  color: Colors.indigo,
                  onTap: () => Get.toNamed(Routes.SMS_QR),
                ),

邮箱用红色邮件图标,电话用青色电话图标,短信用靛蓝色短信图标。每个卡片的 onTap 回调跳转到对应的生成页面。

颜色的选择考虑了视觉区分度,相邻的卡片尽量使用差异较大的颜色,避免用户混淆。

最后两个类型卡片

位置和事件类型:

                _QrTypeCard(
                  icon: Icons.location_on,
                  label: '位置',
                  color: Colors.pink,
                  onTap: () => Get.toNamed(Routes.LOCATION_QR),
                ),
                _QrTypeCard(
                  icon: Icons.event,
                  label: '事件',
                  color: Colors.amber,
                  onTap: () => Get.toNamed(Routes.EVENT_QR),
                ),
              ],
            ),
            SizedBox(height: 24.h),

位置用粉色定位图标,事件用琥珀色日历图标。这两种类型相对不那么常用,放在最后一行。

GridView 后面添加 24.h 的间距,与下方的快速生成区域分隔开。

快速生成区域

快速生成区域使用 Card 包裹:

            // 快速生成区域
            Card(
              child: Padding(
                padding: EdgeInsets.all(16.w),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      '快速生成',
                      style: TextStyle(
                        fontSize: 16.sp,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                    SizedBox(height: 12.h),

Card 提供了圆角和阴影效果,让这个区域在视觉上与其他内容区分开。内部使用 Padding 添加 16.w 的内边距。

标题"快速生成"使用 16.sp 字号和半粗体,比页面主标题小一号,形成层次感。

快速生成输入框

输入框让用户直接输入内容:

                    TextField(
                      decoration: InputDecoration(
                        hintText: '输入文本或网址',
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(8.r),
                        ),
                        suffixIcon: IconButton(
                          icon: const Icon(Icons.qr_code),
                          onPressed: () => Get.toNamed(Routes.TEXT_QR),
                        ),
                      ),
                      onSubmitted: (value) {
                        if (value.isNotEmpty) {
                          Get.toNamed(Routes.TEXT_QR, arguments: value);
                        }
                      },
                    ),

TextField 的 decoration 设置了提示文字、边框样式和后缀图标。hintText 提示用户可以输入文本或网址。OutlineInputBorder 创建带圆角的边框。

suffixIcon 是一个二维码图标按钮,点击跳转到文本生成页面。onSubmitted 在用户按下键盘确认键时触发,如果输入不为空就跳转到文本生成页面,并通过 arguments 传递输入的内容。

样式设置入口

页面底部是样式设置入口:

                  ],
                ),
              ),
            ),
            SizedBox(height: 16.h),
            // 样式设置入口
            Card(
              child: ListTile(
                leading: const Icon(Icons.palette),
                title: const Text('二维码样式设置'),
                subtitle: const Text('自定义颜色、大小、Logo'),
                trailing: const Icon(Icons.chevron_right),
                onTap: () => Get.toNamed(Routes.QR_STYLE),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

使用 Card 和 ListTile 组合创建一个可点击的设置入口。leading 是调色板图标,title 是主标题,subtitle 是副标题说明功能,trailing 是右箭头表示可以点击进入。

这种设计在设置类页面中很常见,用户一眼就能理解这是一个可以点击进入的入口。

_QrTypeCard 组件的定义

_QrTypeCard 是一个私有组件,封装了类型卡片的通用样式:

class _QrTypeCard extends StatelessWidget {
  final IconData icon;
  final String label;
  final Color color;
  final VoidCallback onTap;

  const _QrTypeCard({
    required this.icon,
    required this.label,
    required this.color,
    required this.onTap,
  });

组件接收四个必需参数:icon 是图标数据,label 是显示的文字,color 是主题颜色,onTap 是点击回调。

使用 const 构造函数可以让 Flutter 在编译时创建这些组件,提高性能。下划线前缀表示这是一个私有类,只在当前文件中使用。

_QrTypeCard 的 build 方法

build 方法返回卡片的具体结构:

  
  Widget build(BuildContext context) {
    return Card(
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(12.r),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              padding: EdgeInsets.all(12.w),
              decoration: BoxDecoration(
                color: color.withOpacity(0.1),
                shape: BoxShape.circle,
              ),
              child: Icon(icon, color: color, size: 28.sp),
            ),

Card 提供基础的卡片样式。InkWell 添加点击效果,borderRadius 设置水波纹的圆角范围。Column 让图标和文字垂直居中排列。

图标外面包了一个圆形的 Container,背景色是主题色的 10% 透明度版本,这样既有颜色区分又不会太刺眼。图标大小 28.sp,颜色使用传入的主题色。

_QrTypeCard 的文字部分

图标下方是类型名称:

            SizedBox(height: 8.h),
            Text(
              label,
              style: TextStyle(fontSize: 12.sp, fontWeight: FontWeight.w500),
            ),
          ],
        ),
      ),
    );
  }
}

SizedBox 添加 8.h 的间距。Text 显示类型名称,字号 12.sp,字重 w500 表示中等粗细。

整个卡片的设计简洁明了,图标突出显示,文字作为辅助说明。用户可以通过颜色和图标快速识别不同类型。

控制器的作用

GenerateController 在这个页面中的作用主要是:

  1. 状态共享:子页面可以通过 Get.find 获取同一个控制器实例
  2. 生成逻辑:封装二维码生成的业务逻辑
  3. 样式管理:管理用户选择的二维码样式设置

虽然在主界面中没有直接使用控制器的方法,但在子页面中会频繁用到。

路由设计

页面中使用了多个路由常量:

  • Routes.TEXT_QR:文本二维码生成页
  • Routes.URL_QR:网址二维码生成页
  • Routes.WIFI_QR:WiFi 二维码生成页
  • Routes.CONTACT_QR:联系人二维码生成页
  • Routes.EMAIL_QR:邮箱二维码生成页
  • Routes.PHONE_QR:电话二维码生成页
  • Routes.SMS_QR:短信二维码生成页
  • Routes.LOCATION_QR:位置二维码生成页
  • Routes.EVENT_QR:事件二维码生成页
  • Routes.QR_STYLE:样式设置页
  • Routes.GENERATE_HISTORY:生成历史页

这些路由常量定义在 app_pages.dart 中,使用常量而不是字符串可以避免拼写错误,也方便重构。

用户体验优化

生成主界面的设计考虑了以下用户体验要点:

  1. 快速访问:最常用的类型放在前面,快速生成入口让用户可以跳过类型选择
  2. 视觉区分:不同类型使用不同颜色,方便快速识别
  3. 层次清晰:标题、网格、快速生成、样式设置四个区域层次分明
  4. 滚动支持:内容较多时可以滚动,不会被截断

小结

这篇文章介绍了生成主界面的完整实现。从页面布局的设计,到类型网格的实现,再到快速生成和样式设置入口,覆盖了生成功能的入口页面。

生成主界面是用户进入生成功能的第一个页面,设计要清晰直观,让用户快速找到需要的功能。网格布局展示多种类型,快速生成入口提供便捷操作,样式设置入口满足个性化需求。这些元素组合在一起,形成了一个功能完整、体验良好的生成入口。


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

Logo

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

更多推荐