Flutter 实现验证码输入框学习

学习flutter代码 实现一个用于输入验证码的自定义组件,它允许用户输入固定长度的验证码,并在输入完成时触发回调。

前置知识点学习

TextStyle

`TextStyle` 是 Flutter 中用于定义文本样式的类。它提供了一组属性来控制文本的外观,如字体大小、颜色、粗细、字间距等。`TextStyle` 是 Flutter 应用中处理文本样式的核心部分,通过它可以实现丰富的文本展示效果。

`TextStyle` 的主要属性

1.`color`: 设置文本的颜色。可以使用 `Colors` 类提供的颜色常量或自定义颜色。

2.`fontSize`: 设置文本的字号大小,以逻辑像素为单位。

3.`fontWeight`: 定义文本的粗细程度。可以使用 `FontWeight` 枚举,如 `FontWeight.bold`。

4.`fontStyle`: 设置文本的样式(正常或斜体)。可以使用 `FontStyle` 枚举,如 `FontStyle.italic`。

5.`letterSpacing`: 设置字母之间的间距。

6.`wordSpacing`: 设置单词之间的间距。

7.`fontFamily`: 指定文本所使用的字体系列。可以是系统字体,也可以是自定义字体。

8.`backgroundColor`: 文本的背景颜色。

9.`decoration`: 为文本添加装饰,如下划线、上划线或删除线。可以使用 `TextDecoration` 枚举。

10.`decorationColor`: 文本装饰的颜色。

11.`decorationStyle`: 定义装饰线的样式,如实线、虚线等。可以使用 `TextDecorationStyle` 枚举。

12.`height`: 行高,相对于字体大小的倍数。

示例代码

以下是一个使用 `TextStyle` 的示例,展示了如何应用各种样式属性:

Dart 复制代码
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('TextStyle Example'),
      ),
      body: const Center(
        child: Text(
          'Hello, Flutter!',
          style: TextStyle(
            color: Colors.blue,
            fontSize: 24.0,
            fontWeight: FontWeight.bold,
            fontStyle: FontStyle.italic,
            letterSpacing: 2.0,
            wordSpacing: 4.0,
            fontFamily: 'Roboto',
            decoration: TextDecoration.underline,
            decorationColor: Colors.red,
            decorationStyle: TextDecorationStyle.dashed,
          ),
        ),
      ),
    );
  }

}

代码解析

  • 颜色与大小:`color: Colors.blue` 和 `fontSize: 24.0` 分别设置了文本的颜色和字号。
  • 字体粗细与样式:使用 `fontWeight: FontWeight.bold` 使文本加粗,`fontStyle: FontStyle.italic` 设置为斜体。
  • 字间距与单词间距:`letterSpacing: 2.0` 增加字母间距,`wordSpacing: 4.0` 增加单词间距。
  • 字体系列:`fontFamily: 'Roboto'` 指定文本使用 Roboto 字体系列。
  • 文本装饰:`decoration: TextDecoration.underline` 添加下划线,`decorationColor: Colors.red` 和 `decorationStyle: TextDecorationStyle.dashed` 分别设置装饰线的颜色和样式。
  • 字体加载:当使用自定义字体时,确保在 `pubspec.yaml` 文件中正确配置字体资源,并在项目中包含字体文件。这将确保在所有平台上正确加载和显示自定义字体。
Dart 复制代码
flutter:
  fonts:
    - family: Roboto
      fonts:
        - asset: fonts/Roboto-Regular.ttf
        - asset: fonts/Roboto-Bold.ttf
          weight: 700
  • 性能考虑:在大量文本或频繁更新文本样式的情况下,尽量减少不必要的样式变化,以提高性能。使用相同的 `TextStyle` 实例可以减少构建时间。
  • 继承与覆盖:`TextStyle` 支持通过 `copyWith` 方法创建一个新的样式对象,可以在现有样式的基础上进行局部修改:
Dart 复制代码
TextStyle baseStyle = TextStyle(fontSize: 20.0, color: Colors.black);
TextStyle newStyle = baseStyle.copyWith(color: Colors.red);

这在需要在不同部分稍微调整样式时非常有用。

  • 样式优先级:如果 `TextStyle` 被应用于 `Text` 小部件,而 `Text` 小部件又嵌套在一个使用 `DefaultTextStyle` 的父小部件中,那么 `Text` 小部件的 `TextStyle` 将覆盖 `DefaultTextStyle` 的样式。

实际应用中的建议

  • 使用主题:在大型应用中,使用 `ThemeData` 来统一管理文本样式,可以通过 `Theme.of(context).textTheme` 获取预定义的文本样式。这样可以保持应用风格的一致性。
Dart 复制代码
Text(
  'Themed Text',
  style: Theme.of(context).textTheme.headline6,
);
  • 响应式设计:在处理需要适配不同屏幕尺寸的文本时,可以结合 `MediaQuery` 或使用 `flutter_screenutil` 这样的库,根据屏幕尺寸动态调整 `fontSize`。
  • 国际化与本地化:在处理多语言支持时,确保字体和样式可以适应不同的字符集和语言特性。例如,某些语言可能需要更大的行高或不同的字体。

通过对 `TextStyle` 的深入理解,你可以在 Flutter 应用中创建美观、统一且响应式的文本展示效果。它是 Flutter UI 构建中不可或缺的一部分,熟练掌握它将帮助你更好地设计和实现高质量的用户界面。

ClipRRect

`ClipRRect` 是 Flutter 中的一个小部件,用于将其子组件裁剪为圆角矩形的形状。它非常适合在需要为组件添加圆角效果时使用,比如为图片、容器等添加圆角。`ClipRRect` 通过使用 `RRect`(圆角矩形)来定义裁剪的形状。

主要属性

1.`borderRadius`:

  • 类型:`BorderRadius`
  • 用于定义圆角的半径,可以指定每个角的圆角大小。
  • 示例:`BorderRadius.circular(8.0)` 会为所有角设置 8.0 的圆角。

2.`clipBehavior`:

  • 类型:`Clip`
  • 定义裁剪行为。常用值包括 `Clip.none`、`Clip.hardEdge`、`Clip.antiAlias` 和 `Clip.antiAliasWithSaveLayer`。
  • `Clip.antiAlias` 和 `Clip.antiAliasWithSaveLayer` 提供更平滑的边缘,但可能会影响性能。

3.`child`:

  • 被裁剪的子组件。可以是任何类型的 Widget。

使用场景

  • 应用圆角效果:为图片、按钮、容器等应用圆角效果。
  • 嵌套裁剪:在需要复杂形状裁剪的场合,可以结合其他裁剪小部件(如 `ClipOval`)使用。

示例代码

下面是一个简单的 `ClipRRect` 使用示例,将一张图片裁剪为圆角矩形:

Dart 复制代码
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ClipRRect Example'),
      ),
      body: Center(
        child: ClipRRect(
          borderRadius: BorderRadius.circular(16.0),
          child: Image.asset(
            "static/demo.png",
            width: 200,
            height: 200,
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }
}

代码解析

  • `borderRadius`: 使用 `BorderRadius.circular(16.0)` 为图片的四个角应用了 16.0 的圆角。
  • `child`: 包含了一张通过网络加载的图片。`ClipRRect` 将这张图片裁剪成带有圆角的矩形。

性能考虑

  • 裁剪性能:使用 `ClipRRect` 时,`clipBehavior` 会影响性能,尤其是在需要抗锯齿或使用 `saveLayer` 时。在性能敏感的场景中,尽可能选择 `Clip.hardEdge`,因为它是最快的选项。
  • 避免不必要的裁剪:如果可以通过布局或其他方式实现相同的效果,尽量避免使用 `ClipRRect`,因为裁剪操作会增加渲染开销。

总结

`ClipRRect` 是 Flutter 中用于实现圆角裁剪的强大工具。通过它,可以轻松地为任何组件添加圆角效果,使得应用的视觉效果更加美观和现代。在使用时,需要注意裁剪的性能影响,合理选择裁剪行为以达到最佳的性能和平滑度平衡。

AnimatedContainer

`AnimatedContainer` 是 Flutter 中一个非常有用的小部件,它在属性发生变化时可以自动动画化地过渡到新的属性。这使得创建动画效果和响应式界面变得非常简单,无需手动管理动画控制器或状态。

特性与属性

1.`duration`:

  • 类型:`Duration`
  • 指定动画过渡的持续时间。必须设置这个属性来定义动画的长短。
  • 示例:`Duration(seconds: 1)` 表示动画持续一秒。

2.`curve`:

  • 类型:`Curve`
  • 定义动画的曲线,决定了动画的加速度和减速度。常用曲线有 `Curves.easeIn`、`Curves.easeOut`、`Curves.bounceIn` 等。
  • 示例:`Curves.easeInOut` 表示动画以缓慢的速度开始和结束,但中间速度较快。

3.`alignment`:

  • 控制子组件的对齐方式。

4.`padding`:

  • 控制内部填充。

5.`color`:

  • 背景颜色。

6.`decoration` 和 `foregroundDecoration`:

  • `decoration` 用于背景装饰,例如边框、圆角、渐变等。
  • `foregroundDecoration` 用于在子组件前的装饰。

7.`width` 和 `height`:

  • 控制容器的宽度和高度。

8.`constraints`:

  • 设置容器的大小限制,例如最小和最大宽高。

9.`margin`:

  • 控制外部间距。

`10.transform`:

  • 应用于子组件的变换,例如旋转、缩放、平移等。

11.`child`:

  • 被包裹的子组件。

使用场景

  • 动画化尺寸变化:在用户交互后,动态调整组件的大小。
  • 背景颜色过渡:在状态改变时,平滑地过渡到新的背景颜色。
  • 位置和边距动画:在布局中通过调整位置和边距来创建动画效果。

示例代码

Dart 复制代码
import 'package:flutter/material.dart';

class AnimatedContainerExample extends StatefulWidget {
  const AnimatedContainerExample({super.key});

  @override
  _AnimatedContainerExampleState createState() {
    return _AnimatedContainerExampleState();
  }
}

class _AnimatedContainerExampleState extends State<AnimatedContainerExample> {
  double _width = 100.0;
  double _height = 100.0;
  Color _color = Colors.blue;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('AnimatedContainer Example'),
      ),
      body: Center(
        child: GestureDetector(
          onTap: () {
            setState(() {
              _width = _width == 100.0 ? 200.0 : 100.0;
              _height = _height == 100.0 ? 200.0 : 100.0;
              _color = _color == Colors.blue ? Colors.red : Colors.blue;
            });
          },
          child: AnimatedContainer(
            width: _width,
            height: _height,
            color: _color,
            alignment: Alignment.center,
            duration: const Duration(seconds: 1),
            curve: Curves.easeInOut,
            child: const Text(
              'Tap me!',
              style: TextStyle(color: Colors.white, fontSize: 20.0),
            ),
          ),
        ),
      ),
    );
  }
}

BoxDecoration

`BoxDecoration` 是 Flutter 中用于装饰容器(如 `Container`)的一个类。它提供了一系列属性,允许你为容器添加背景颜色、渐变、图像、边框、阴影等装饰效果。`BoxDecoration` 结合 `Container` 使用,可以打造出丰富的视觉效果。

主要属性

1.`color`:

  • 类型:`Color`
  • 设置容器的背景颜色。

2.`image`:

  • 类型:`DecorationImage`
  • 用于在容器背景中绘制图像。
  • 可以指定图像的对齐方式、填充方式等。

3.`border`:

  • 类型:`Border`
  • 为容器添加边框。可以指定每条边的宽度和颜色。

4.`borderRadius`:

  • 类型:`BorderRadius`
  • 设置容器的圆角半径。

5.`boxShadow`:

  • 类型:`List`
  • 为容器添加阴影效果。可以指定阴影的颜色、偏移、模糊半径等。

6.`gradient`:

  • 类型:`Gradient`
  • 为容器添加渐变背景。支持线性渐变、径向渐变等。

7.`backgroundBlendMode`:

  • 类型:`BlendMode`
  • 设置背景颜色和图像的混合模式。

8.`shape`:

  • 类型:`BoxShape`
  • 指定容器的形状。可以是矩形(默认)或圆形。

使用场景

  • 背景颜色和图像:为容器添加背景颜色或图像。
  • 边框和圆角:为容器添加边框和圆角效果。
  • 阴影效果:为容器添加阴影,提升视觉层次感。
  • 渐变背景:使用渐变色为背景,打造现代化的视觉效果。

示例代码

以下是一个使用 `BoxDecoration` 的示例,展示了如何为容器添加背景颜色、圆角、边框和阴影:

Dart 复制代码
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('BoxDecoration Example'),
      ),
      body: Container(
        width: 200.0,
        height: 200.0,
        decoration: BoxDecoration(
          color: Colors.blue[200],
          borderRadius: BorderRadius.circular(20.0),
          border: Border.all(
            color: Colors.blue,
            width: 3.0,
          ),
          boxShadow: [
            BoxShadow(
              color: Colors.grey.withOpacity(0.5),
              spreadRadius: 5,
              blurRadius: 7,
              offset: const Offset(0, 3), // changes position of shadow
            ),
          ],
        ),
        child: const Center(
          child: Text(
            'Hello, Flutter!',
            style: TextStyle(color: Colors.white, fontSize: 20.0),
          ),
        ),
      ),
    );
  }
}

TextInputFormatter

`TextInputFormatter` 是 Flutter 中用于在用户输入文本时进行格式化和验证的一个抽象类。它允许你在文本被输入到文本字段之前对其进行修改或限制,这对于实现自定义的输入行为(如限制输入类型或长度)非常有用。

主要属性和方法

  • `formatEditUpdate`:
  • 方法:`TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue)`
  • 这是 `TextInputFormatter` 的核心方法,必须被重写。它接收旧的文本值和新的文本值,返回一个格式化后的文本值。
  • `TextEditingValue` 包含文本字段的当前状态,包括文本内容、光标位置和选择范围。

常用子类

1.`LengthLimitingTextInputFormatter`:

  • 用于限制输入文本的最大长度。
  • 构造时传入一个整数表示最大长度。
  • 示例:`LengthLimitingTextInputFormatter(10)` 限制输入长度为 10 个字符。

2.`FilteringTextInputFormatter`:

  • 用于过滤输入的文本,只允许符合特定模式的输入。
  • 提供了 `FilteringTextInputFormatter.digitsOnly` 来限制输入仅为数字。
  • 可以通过自定义正则表达式来实现更复杂的过滤。

示例代码

以下是如何使用 `LengthLimitingTextInputFormatter` 和 `FilteringTextInputFormatter` 的示例:

Dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('TextInputFormatter Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              decoration: const InputDecoration(
                labelText: 'Limited Length (Max 10)',
              ),
              inputFormatters: [
                LengthLimitingTextInputFormatter(10),
              ],
            ),
            const SizedBox(
              height: 20,
            ),
            TextField(
              decoration: const InputDecoration(
                labelText: 'Digits Only',
              ),
              keyboardType: TextInputType.number,
              inputFormatters: [
                FilteringTextInputFormatter.digitsOnly,
              ],
            )
          ],
        ),
      ),
    );
  }
}

代码解析

  • 限制输入长度:第一个文本字段使用 `LengthLimitingTextInputFormatter(10)` 限制输入最大长度为 10 个字符。
  • 限制输入内容:第二个文本字段使用 `FilteringTextInputFormatter.digitsOnly` 限制输入仅为数字。

自定义格式化器

你可以通过继承 `TextInputFormatter` 并重写 `formatEditUpdate` 方法来创建自定义的输入格式化器。例如,创建一个格式化器来限制输入只能是大写字母:

Dart 复制代码
class UpperCaseTextInputFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(
      TextEditingValue oldValue, TextEditingValue newValue) {
    return TextEditingValue(
      text: newValue.text.toUpperCase(),
      selection: newValue.selection,
    );
  }
}

使用场景

  • 限制输入的类型和格式(例如,只允许字母、数字或特定符号)。
  • 验证用户输入的格式是否符合特定规则(如电子邮件、电话号码)。
  • 动态调整用户输入,使其符合特定格式。

TextField

`TextField` 是 Flutter 中用于获取用户文本输入的一个重要组件。它提供了强大的功能和高度的可定制性,使开发者能够轻松地集成和管理用户输入。下面是对 `TextField` 的详细解析。

基本特性

1.文本输入:`TextField` 是用于用户输入文本的基本组件。

2.键盘类型:可以通过 `keyboardType` 属性设置输入时弹出的键盘类型,例如数字键盘、邮件键盘等。

3.样式和装饰:通过 `decoration` 属性可以自定义 `TextField` 的外观,包括提示文本、边框、标签等。

4.控制器:使用 `TextEditingController` 来管理和监听文本输入的变化。

5.输入格式化:使用 `inputFormatters` 来限制和格式化用户输入,例如限制输入长度或只允许数字输入。

6.焦点控制:通过 `FocusNode` 可以控制和监听 `TextField` 的焦点状态。

主要属性

  • controller: `TextEditingController` 用于读取、设置和监听 `TextField` 的文本。
  • focusNode: `FocusNode` 用于管理和监听焦点状态。
  • decoration: `InputDecoration` 用于定义 `TextField` 的外观和样式。
  • keyboardType: `TextInputType` 确定输入时使用的键盘类型。
  • textInputAction: `TextInputAction` 确定操作按钮的类型,如"下一步"或"完成"。
  • onChanged: 输入内容更改时的回调函数。
  • onSubmitted: 用户提交(按下"完成"键)时的回调函数。
  • inputFormatters: `TextInputFormatter` 用于格式化输入文本。
  • obscureText: 用于密码输入,隐藏输入的字符。
  • maxLength: 限制最大输入长度。

示例代码

以下是一个基本的 `TextField` 示例,展示了如何使用控制器、装饰器和输入格式化器:

Dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class TextFieldExample extends StatefulWidget {
  const TextFieldExample({super.key});

  @override
  _TextFieldExampleState createState() {
    return _TextFieldExampleState();
  }
}

class _TextFieldExampleState extends State<TextFieldExample> {
  final TextEditingController _controller = TextEditingController();
  final FocusNode _focusNode = FocusNode();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('TextField Example'),
      ),
      body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            children: [
              TextField(
                controller: _controller,
                focusNode: _focusNode,
                decoration: const InputDecoration(
                  labelText: 'Enter your text',
                  border: OutlineInputBorder(),
                ),
                keyboardType: TextInputType.text,
                textInputAction: TextInputAction.done,
                onChanged: (text) {
                  print('Current text: $text');
                },
                onSubmitted: (text) {
                  print('Submitted text: $text');
                },
                inputFormatters: [
                  LengthLimitingTextInputFormatter(20),
                  FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9]')),
                ],
              ),
              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  // 打印当前文本框中的文本
                  print('Final text: ${_controller.text}');
                  // 移除焦点,使键盘消失
                  _focusNode.unfocus();
                },
                child: const Text('Print Text and Unfocus'),
              ),
            ],
          )),
    );
  }
}

代码解析

  • `TextEditingController`: 用于管理 `TextField` 的文本。可以通过 `_controller.text` 获取或设置输入文本。
  • `FocusNode`: 管理 `TextField` 的焦点状态。使用 `_focusNode.unfocus()` 可以移除焦点,通常用于隐藏键盘。
  • `InputDecoration`: 自定义 `TextField` 的外观,例如标签文本和边框。
  • `inputFormatters`: 使用 `LengthLimitingTextInputFormatter` 限制输入长度,以及 `FilteringTextInputFormatter.allow` 限制输入内容为字母和数字。
  • `keyboardType`: 设置为 `TextInputType.text`,用于一般文本输入。
  • `textInputAction`: 设置为 `TextInputAction.done`,表示用户完成输入时的操作。
  • 事件回调: `onChanged` 和 `onSubmitted` 允许在用户输入或提交时执行操作。

附加功能和用法

  • 密码输入: 设置 `obscureText: true` 可以隐藏输入的字符,用于密码输入框。
  • 多行输入: 通过设置 `maxLines` 属性,可以允许 `TextField` 输入多行文本。
  • 文本样式: 使用 `style` 属性自定义文本的外观,例如字体大小和颜色。
  • 错误信息: 使用 `errorText` 属性在 `InputDecoration` 中显示错误信息。
  • 自动填充: 在 Android 和 iOS 上,`TextField` 支持自动填充功能,可以通过 `autofillHints` 配置。

Border

在 Flutter 中,`Border` 是一个非常重要的类,用于定义组件(如容器、按钮或输入框)周围的边框。它提供了多种方式来自定义边框的样式、颜色和宽度。理解 `Border` 的各种属性和用法有助于创建更具吸引力和功能性的 UI。

`Border` 类的基本概念

`Border` 类通常用于定义一个组件的四个边的样式。它通常与 `BoxDecoration` 一起使用,以应用于 `Container` 或其他需要边框的组件。

主要属性

  • `top`、`bottom`、`left`、`right`: 每个边框的样式,通过 `BorderSide` 定义,可以设置颜色、宽度和样式。
  • `all`: 使用同样的边框值应用到所有边。
  • `symmetric`: 为水平和垂直方向的边设置对称的边框。

`BorderSide` 类

`BorderSide` 是用来描述边框的样式的类,包含以下属性:

  • `color`: 边框的颜色。
  • `width`: 边框的宽度,默认为 1.0。
  • `style`: 边框的样式,`BorderStyle.solid`(实线)或 `BorderStyle.none`(无边框)。''

使用示例

以下是如何使用 `Border` 和 `BorderSide` 来自定义 `Container` 的边框:

Dart 复制代码
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Border Example'),
      ),
      body: Center(
        child: Container(
          width: 200,
          height: 200,
          decoration: BoxDecoration(
              color: Colors.blue[50],
              border: const Border(
                top: BorderSide(
                  color: Colors.blue,
                  width: 3.0,
                  style: BorderStyle.solid,
                ),
                bottom: BorderSide(
                  color: Colors.green,
                  width: 5.0,
                  style: BorderStyle.solid,
                ),
                left: BorderSide(
                  color: Colors.red,
                  width: 2.0,
                  style: BorderStyle.solid,
                ),
                right: BorderSide(
                  color: Colors.orange,
                  width: 4.0,
                  style: BorderStyle.solid,
                ),
              )),
        ),
      ),
    );
  }
}

代码解析

  • `BoxDecoration`: 用于定义 `Container` 的视觉外观,包括颜色和边框。
  • `Border`: 使用 `Border` 类为 `Container` 的每一侧定义不同的 `BorderSide`。
  • `BorderSide`: 描述了每个边的颜色、宽度和样式。

常用方法

  • `Border.all()`: 创建一个统一的边框,所有边的样式相同。
Dart 复制代码
decoration: BoxDecoration(
  border: Border.all(
    color: Colors.black,
    width: 2.0,
  ),
),
  • `Border.symmetric()`: 为水平(`horizontal`)和垂直(`vertical`)方向定义对称的边框。
Dart 复制代码
   decoration: BoxDecoration(
            color: Colors.yellow[50],
            border: Border.symmetric(
              vertical: BorderSide(
                color: Colors.red,
                width: 4.0,
              ),
              horizontal: BorderSide(
                color: Colors.blue,
                width: 2.0,
              ),
            ),
          ),

BorderRadius

`BorderRadius` 是 Flutter 中用于定义圆角矩形的类。它可以用于将边框、容器和其他矩形形状的角变圆。`BorderRadius` 提供了多种方法来定义角的圆度,允许你为每个角指定不同的半径,也可以为所有角指定相同的半径。

主要构造方法

1.`BorderRadius.circular(double radius)`: 为所有角设置相同的圆角半径。这个方法是最常用的,适合需要统一圆角的情况。

2.`BorderRadius.all(Radius radius)`: 使用 `Radius` 对象为所有角设置相同的半径。

3.`BorderRadius.only({Radius topLeft, Radius topRight, Radius bottomLeft, Radius bottomRight})`: 分别为每个角设置不同的半径,提供了最大的灵活性。

4.`BorderRadius.horizontal({Radius left, Radius right})`: 仅为水平的两个角设置半径。

5.`BorderRadius.vertical({Radius top, Radius bottom})`: 仅为垂直的两个角设置半径。

示例代码

以下是如何使用 `BorderRadius` 的一些示例:

1. 使用 `BorderRadius.circular`
Dart 复制代码
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Circular Border Example')),
      body: Center(
        child: Container(
          width: 200,
          height: 200,
          decoration: BoxDecoration(
            color: Colors.blue[100],
            borderRadius: BorderRadius.circular(20.0), // 所有角为圆角 20.0
          ),
        ),
      ),
    );
  }
}
2. 使用 `BorderRadius.only`
Dart 复制代码
import 'package:flutter/material.dart';
class OnlyBorderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Only Border Example')),
      body: Center(
        child: Container(
          width: 200,
          height: 200,
          decoration: BoxDecoration(
            color: Colors.green[100],
            borderRadius: BorderRadius.only(
              topLeft: Radius.circular(30.0),
              bottomRight: Radius.circular(30.0),
            ),
          ),
        ),
      ),
    );
  }
}
3. 使用 `BorderRadius.horizontal`
Dart 复制代码
import 'package:flutter/material.dart';
class HorizontalBorderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Horizontal Border Example')),
      body: Center(
        child: Container(
          width: 200,
          height: 200,
          decoration: BoxDecoration(
            color: Colors.red[100],
            borderRadius: BorderRadius.horizontal(
              left: Radius.circular(20.0),
              right: Radius.circular(50.0),
            ),
          ),
        ),
      ),
    );
  }
}

EditableText

在 Flutter 中,`EditableText` 是文本输入框的核心控件,所有类似 `TextField` 和 `TextFormField` 的控件都是基于 `EditableText` 封装的。`EditableText` 提供了更底层的能力,允许开发者对文本输入框的行为进行高度自定义。

1. 什么是 `EditableText`?

`EditableText` 是一个可编辑的文本组件,用于实现完全可控的文本输入功能。它不像 `TextField` 那样提供一套高阶封装,而是提供了对文本输入的较底层控制,包括:

  • 文本内容的管理
  • 焦点的获取与丢失
  • 自定义光标、样式、输入法行为
  • 输入格式化器和验证规则

2. `EditableText` 的核心属性解析

以下是 `EditableText` 的关键属性及其作用:

必需的属性

|---------------------------|---------------------------|---------------------------------|
| 属性 | 类型 | 描述 |
| `controller` | `TextEditingController` | 管理输入框内容的核心控制器,可监听文本变化或更新文本。 |
| `focusNode` | `FocusNode` | 管理输入框焦点状态的节点。可判断输入框是否聚焦并控制焦点行为。 |
| `style` | `TextStyle` | 定义输入文本的样式(如字体大小、文本颜色等)。 |
| `cursorColor` | `Color` | 设置光标的颜色。 |
| `backgroundCursorColor` | `Color` | 光标的背景颜色,当光标不可见时使用该颜色。 |

常用的属性

|-----------------------|-------------------|-------------------------------------|
| 属性 | 类型 | 描述 |
| `obscureText` | `bool` | 是否隐藏输入的文本(常用于密码输入)。 |
| `autofocus` | `bool` | 是否在构建时自动获取焦点。 |
| `keyboardType` | `TextInputType` | 设置键盘类型(如文本、数字、email等)。 |
| `textAlign` | `TextAlign` | 文本的对齐方式(如:左对齐、右对齐、居中)。 |
| `maxLines` | `int` | 设置输入框的最大行数,默认为 1。 |
| `minLines` | `int` | 设置输入框的最小行数。 |
| `inputFormatters` | `List` | 输入格式化器,可用来限制或过滤非法输入(如限制字数、屏蔽特殊字符等)。 |
| onChanged` | `ValueChanged` | 当文本内容发生变化时触发的回调函数。 |
| `onEditingComplete` | `VoidCallback` | 当用户完成编辑时(如按下键盘的"完成"按钮)触发的回调。 |
| `onSubmitted` | `ValueChanged` | 用户提交输入内容时触发的回调。 |
| `readOnly` | `bool` | 是否设置为只读模式(用户无法编辑文本)。 |

光标和样式相关属性

|---------------------------|--------------------|-------------------------------------------------|
| 属性 | 类型 | 描述 |
| `cursorWidth` | `double` | 光标的宽度。默认值通常为 2.0 像素。 |
| `cursorHeight` | `double?` | 光标的高度。通常情况下,光标的高度与文本的高度一致,但你可以通过此属性自定义。 |
| `cursorRadius` | `Radius?` | 光标的圆角半径。如果需要圆角光标,可以在这里设置。 |
| `cursorOpacityAnimates` | `bool` | 如果为 true,光标的透明度会在显示和隐藏之间动画过渡。 |
| `selectionHeightStyle` | `BoxHeightStyle` | 控制文本选择时的高度样式。影响文本选择的外观。 |
| `selectionWidthStyle` | `BoxWidthStyle` | 控制文本选择时的宽度样式。通常与 `selectionHeightStyle` 一起使用。 |

其他重要属性

|--------------------------------|------------------------|--------------------------------------|
| 属性 | 类型 | 描述 |
| `toolbarOptions` | `ToolbarOptions` | 定义当用户长按输入框时,弹出工具栏中可用的选项(如剪切、复制、粘贴等)。 |
| `showCursor` | `bool?` | 控制光标是否显示。 |
| `enableInteractiveSelection` | `bool` | 控制是否允许用户交互式选择文本(如长按选择文本)。 |
| `textCapitalization` | `TextCapitalization` | 控制输入文本的自动大写行为(如每个单词首字母大写)。 |

使用 `EditableText` 的示例

以下是一个使用 `EditableText` 的完整示例,展示如何自定义一个简单的文本输入框:

Dart 复制代码
import 'package:flutter/material.dart';

class EditableTextWidgetDemo extends StatefulWidget {
  const EditableTextWidgetDemo({super.key});

  @override
  _EditableTextWidgetState createState() {
    return _EditableTextWidgetState();
  }
}

class _EditableTextWidgetState extends State<EditableTextWidgetDemo> {
  final TextEditingController _controller = TextEditingController();
  final FocusNode _focusNode = FocusNode();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: const Text('TextEditingController Example')),
        body: Center(
            child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: EditableText(
            controller: _controller,
            focusNode: _focusNode,
            style: const TextStyle(color: Colors.black, fontSize: 18.0),
            cursorColor: Colors.blue,
            backgroundCursorColor: Colors.grey,
            keyboardType: TextInputType.text,
            autofocus: true,
            maxLines: null,
            // 允许多行输入
            onChanged: (text) {
              print("Text changed: $text");
            },
          ),
        )));
  }

  @override
  void dispose() {
    _controller.dispose();
    _focusNode.dispose();
    super.dispose();
  }
}

总结

  • 灵活性: `EditableText` 提供了高度的灵活性和定制能力。虽然 `TextField` 和 `TextFormField` 已经满足了大多数日常需求,但 `EditableText` 允许开发者完全控制文本输入的细节。如果你需要实现一个非常定制化的文本输入体验,`EditableText` 是一个很好的起点。
  • 自定义控制: 通过 `EditableText`,你可以完全自定义光标的外观、文本样式、输入格式化、焦点行为、输入法行为等等。这使得它适合于需要特定文本输入行为的应用场景。
  • 底层实现: 作为 `TextField` 和 `TextFormField` 的底层实现,`EditableText` 需要开发者手动管理 `TextEditingController` 和 `FocusNode`,这意味着开发者必须更加关注资源的管理(例如确保在不需要时正确释放这些对象)。
  • 应用场景: 使用 `EditableText` 适用于需要精细控制用户输入的场景,比如自定义的文本编辑器、需要特殊输入验证的表单等。

通过了解和善用 `EditableText`,开发者可以创建更具个性化的用户输入界面,满足特定的应用需求。希望这个解析能够帮助你更好地理解和使用 `EditableText`。

GestureDetector

`GestureDetector` 是 Flutter 中一个非常重要的组件,用于检测用户的手势操作(如点击、双击、拖动、滑动等)。通过使用 `GestureDetector`,你可以捕获用户在屏幕上的各种手势,并对这些手势做出响应。这使得 `GestureDetector` 成为构建交互式用户界面的关键工具之一。

主要功能

`GestureDetector` 提供了一种简单而强大的方式来监听和响应用户的手势。以下是一些常用的手势检测功能:

点击手势:

  • `onTap`: 用户点击时触发。
  • `onDoubleTap`: 用户双击时触发。
  • `onLongPress`: 用户长按时触发。

拖动手势:

  • `onPanStart`: 用户开始拖动时触发。
  • `onPanUpdate`: 用户拖动过程中触发。
  • `onPanEnd`: 用户拖动结束时触发。

滑动手势:

  • `onHorizontalDragStart`, `onHorizontalDragUpdate`, `onHorizontalDragEnd`: 检测水平拖动。
  • `onVerticalDragStart`, `onVerticalDragUpdate`, `onVerticalDragEnd`: 检测垂直拖动。

缩放手势:

  • `onScaleStart`, `onScaleUpdate`, `onScaleEnd`: 用于检测缩放操作,通常用于实现捏合缩放功能。

基本用法

以下是一个简单的示例,展示如何使用 `GestureDetector` 来监听不同的手势:

Dart 复制代码
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('GestureDetector Example')),
      body: Center(
        child: GestureDetector(
          onTap: () {
            print('Tapped!');
          },
          onDoubleTap: () {
            print('Double Tapped!');
          },
          onLongPress: () {
            print('Long Pressed!');
          },
          onPanUpdate: (details) {
            print('Pan Updated: ${details.delta}');
          },
          child: Container(
            width: 200,
            height: 200,
            color: Colors.blue,
            alignment: Alignment.center,
            child: const Text(
              'Tap Me',
              style: TextStyle(color: Colors.white, fontSize: 24),
            ),
          ),
        ),
      ),
    );
  }
}

详细属性解析

  • `onTap`: 单次点击时的回调。
  • `onDoubleTap`: 双击时的回调。
  • `onLongPress`: 长按时的回调。
  • `onPanStart`, `onPanUpdate`, `onPanEnd`: 分别用于开始、更新和结束拖动时的回调。
  • `onHorizontalDragStart`, `onHorizontalDragUpdate`, `onHorizontalDragEnd`: 处理水平拖动。
  • `onVerticalDragStart`, `onVerticalDragUpdate`, `onVerticalDragEnd`: 处理垂直拖动。
  • `onScaleStart`, `onScaleUpdate`, `onScaleEnd`: 处理缩放手势。

注意事项

1. Hit Testing(命中测试)
  • `GestureDetector` 需要一个非空的子组件才能检测手势。
  • 如果 `GestureDetector` 的子组件是空的(比如没有子组件,或者子组件是 `Container` 且未设置 `color` 属性),手势检测将无法生效。这是因为 Flutter 默认不会为空组件进行命中测试。
  • 解决方案:可以为 `GestureDetector` 的子组件设置一个明确的背景颜色(即使是透明的),例如 `color: Colors.transparent`。
Dart 复制代码
GestureDetector(
  onTap: () {
    print('Tapped');
  },
  child: Container(
    width: 100,
    height: 100,
    color: Colors.transparent, // 必须设置颜色,否则点击事件可能无法检测
  ),
)
2. 手势冲突
  • 当多个手势识别器(如拖动和缩放)同时应用于同一个组件时,可能会发生手势冲突。
  • Flutter 提供了 手势竞技场机制(Gesture Arena) 来解决冲突。在默认情况下,多个手势识别器会竞争事件的优先权,只有一个手势识别器会胜出。
  • 如果你需要同时响应多个手势,可以使用 `RawGestureDetector` 或 `GestureRecognizer` 来自定义手势行为。

例如,通过 `onScaleUpdate` 实现拖动和缩放:

Dart 复制代码
GestureDetector(
  onScaleUpdate: (details) {
    print('Scale: ${details.scale}, Translation: ${details.focalPointDelta}');
  },
  child: Container(
    width: 200,
    height: 200,
    color: Colors.blue,
  ),
);
3. 嵌套手势
  • 当 `GestureDetector` 嵌套时,内部的手势可能会覆盖外部的手势,或者导致手势冲突。
  • Flutter 提供了 `Behavior` 属性来控制手势的传播方式:
  • `HitTestBehavior.deferToChild`(默认值):只有子组件能够响应手势时,`GestureDetector` 才会检测手势。
  • `HitTestBehavior.opaque`:即使子组件是透明的,父组件也会参与命中测试。
  • `HitTestBehavior.translucent`:父组件会响应手势,但透明区域的子组件仍然可以响应手势。
Dart 复制代码
GestureDetector(
  behavior: HitTestBehavior.translucent,
  onTap: () {
    print('Parent tapped!');
  },
  child: GestureDetector(
    onTap: () {
      print('Child tapped!');
    },
    child: Container(
      width: 100,
      height: 100,
      color: Colors.red,
    ),
  ),
);

Flutter实现输入验证码代码学习

Dart 复制代码
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

///验证码输入框
class VerificationCodeInputDemoPage extends StatelessWidget {
  const VerificationCodeInputDemoPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("VerificationCodeInputDemoPage"),
      ),
      body: GestureDetector(
        behavior: HitTestBehavior.translucent,
        onTap: () {
          FocusScope.of(context).requestFocus(FocusNode());
        },
        child: Center(
          child: VerCodeInput(
            ctx: context,
            length: 6,
            keyboardType: TextInputType.number,
            builder: staticRectangle(context),
            onChanged: (value) {
            },

            ///输入完成时
            onFilled: (value) {
              //print('Your input is $value.');
            },
          ),
        ),
      ),
    );
  }

  staticRectangle(BuildContext context) {
    var codeSize = 6;
    double padding = 16;
    double width = MediaQuery.sizeOf(context).width;
    double codeFullSize = ((width - 2 * padding) / codeSize);
    double codeNormalSize = codeFullSize - 20;
    return CodeInputBuilders.rectangle(
        totalSize: Size(codeFullSize, codeFullSize),
        emptySize: Size(codeNormalSize, codeNormalSize),
        filledSize: Size(codeNormalSize, codeNormalSize),
        borderRadius: BorderRadius.zero,
        border: Border.all(color: Theme.of(context).primaryColor, width: 1.0),
        color: Colors.transparent,
        textStyle: TextStyle(
            color: Theme.of(context).primaryColor,
            fontSize: 16.0,
            fontWeight: FontWeight.bold));
  }
}

///from https://github.com/tiny-express/flutter_verification_code_input/blob/master/lib/src/verification_code_input.dart

typedef CodeInputBuilder = Widget Function(bool hasFocus, String char);

class VerCodeInput extends StatefulWidget {
  const VerCodeInput._({
    super.key,
    required this.length,
    required this.keyboardType,
    required this.inputFormatters,
    required this.builder,
    required this.ctx,
    this.onChanged,
    this.onFilled,
  });

  factory VerCodeInput({
    Key? key,
    required int length,
    TextInputType keyboardType = TextInputType.text,
    List<TextInputFormatter>? inputFormatters,
    BuildContext? ctx,
    required CodeInputBuilder builder,
    void Function(String value)? onChanged,
    void Function(String value)? onFilled,
  }) {
    assert(length > 0, 'The length needs to be larger than zero.');
    assert(length.isFinite, 'The length needs to be finite.');

    inputFormatters ??= _createInputFormatters(length, keyboardType);

    return VerCodeInput._(
      key: key,
      length: length,
      keyboardType: keyboardType,
      inputFormatters: inputFormatters,
      builder: builder,
      ctx: ctx,
      onChanged: onChanged,
      onFilled: onFilled,
    );
  }

  /// The length of character entities to always display.
  ///
  /// ## Sample code
  ///
  /// A code input with 4 characters:
  ///
  /// ```dart
  /// CodeInput(length: 4)
  /// ```
  final int length;

  /// The type of thconstard which shows up.
  ///
  /// ## Sample codeconst
  ///
  /// ```dart
  /// CodeInput(keyboardType: TextInputType.number)
  /// ```
  final TextInputType keyboardType;

  /// A list of input formatters which can validate the text as it is being
  /// typed.
  ///
  /// If you specify this parameter, the default input formatters aren't used,
  /// so make sure you really check for everything (like length of the input).
  ///
  /// ## Sample code
  ///
  /// An code input that displays a normal keyboard but only allows for
  /// hexadecimal input:
  ///
  /// ```dart
  /// CodeInput(
  ///   inputFormatters: [
  ///     WhitelistingTextInputFormatter(RegExp('^[0-9a-fA-F]*\$'))
  ///   ]
  /// )
  /// ```
  final List<TextInputFormatter> inputFormatters;

  /// A builder for the character entities.
  ///
  /// See [CodeInputBuilders] for examples.
  final CodeInputBuilder builder;

  /// A callback for changes to the input.
  final void Function(String value)? onChanged;

  /// A callback for when the input is filled.
  final void Function(String value)? onFilled;

  /// context parent because of MediaQuery.of(widget.ctx)
  final BuildContext? ctx;

  /// A helping function that creates input formatters for a given length and
  /// keyboardType.
  static List<TextInputFormatter> _createInputFormatters(
      int length, TextInputType keyboardType) {
    final formatters = <TextInputFormatter>[
      LengthLimitingTextInputFormatter(length)
    ];

    // Add keyboard specific formatters.
    // For example, a code input with a number keyboard type probably doesn't
    // want to allow decimal separators or signs.
    if (keyboardType == TextInputType.number) {
      formatters.add(FilteringTextInputFormatter.digitsOnly);
    }

    return formatters;
  }

  @override
  _VerCodeInputState createState() => _VerCodeInputState();
}

class _VerCodeInputState extends State<VerCodeInput> {
  final node = FocusNode();
  final controller = TextEditingController();

  String get text => controller.text;

  @override
  Widget build(BuildContext context) {
    // We'll display the visual widget and a not shown EditableText for doing
    // the actual work on top of each other.
    return Stack(children: <Widget>[
      // This is the actual EditableText wrapped in a Container with zero
      // dimensions.
      SizedBox(
          width: 0.0,
          height: 0.0,
          child: EditableText(
            controller: controller,
            focusNode: node,
            inputFormatters: widget.inputFormatters,
            keyboardType: widget.keyboardType,
            backgroundCursorColor: Colors.black,
            style: const TextStyle(),
            // Doesn't really matter.
            cursorColor: Colors.black,
            // Doesn't really matter.
            onChanged: (value) => setState(() {
              widget.onChanged?.call(value);
              if (value.length == widget.length) {
                widget.onFilled?.call(value);
              }
            }),
          )),
      // These are the actual character widgets. A transparent container lies
      // right below the gesture detector, so all taps get collected, even
      // the ones between the character entities.
      GestureDetector(
          onTap: () {
            if (MediaQuery.viewInsetsOf(context).bottom == 0) {
              final focusScope = FocusScope.of(context);
              focusScope.requestFocus(FocusNode());
              Future.delayed(
                  Duration.zero, () => focusScope.requestFocus(node));
            }
          },
          child: Container(
            color: Colors.transparent,
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: List.generate(widget.length, (i) {
                final hasFocus = controller.selection.start == i;
                final char = i < text.length ? text[i] : '';
                final characterEntity = widget.builder(hasFocus, char);


                return characterEntity;
              }),
            ),
          )),
    ]);
  }
}

/// An abstract class that provides some commonly-used builders for the
/// character entities.
///
/// * [containerized]: A builder putting chars in an animated container.
/// * [circle]: A builder putting chars in circles.
/// * [rectangle]: A builder putting chars in rectangles.
/// * [lightCircle]: A builder putting chars in light circles.
/// * [darkCircle]: A builder putting chars in dark circles.
/// * [lightRectangle]: A builder putting chars in light rectangles.
/// * [darkRectangle]: A builder putting chars in dark rectangles.
abstract class CodeInputBuilders {
  /// Builds the input inside an animated container.
  static CodeInputBuilder containerized({
    Duration animationDuration = const Duration(milliseconds: 50),
    required Size totalSize,
    required Size emptySize,
    required Size filledSize,
    required BoxDecoration emptyDecoration,
    required BoxDecoration filledDecoration,
    required TextStyle emptyTextStyle,
    required TextStyle filledTextStyle,
  }) {
    return (bool hasFocus, String char) => Container(
        width: totalSize.width,
        height: totalSize.height,
        alignment: Alignment.center,
        child: AnimatedContainer(
          duration: const Duration(milliseconds: 100),
          decoration: char.isEmpty ? emptyDecoration : filledDecoration,
          width: char.isEmpty ? emptySize.width : filledSize.width,
          height: char.isEmpty ? emptySize.height : filledSize.height,
          alignment: Alignment.center,
          child: Text(char,
              style: char.isEmpty ? emptyTextStyle : filledTextStyle),
        ));
  }

  /// Builds the input inside a circle.
  static CodeInputBuilder circle(
      {double totalRadius = 30.0,
      double emptyRadius = 10.0,
      double filledRadius = 25.0,
      required Border border,
      required Color color,
      required TextStyle textStyle}) {
    final decoration = BoxDecoration(
      shape: BoxShape.circle,
      border: border,
      color: color,
    );

    return containerized(
        totalSize: Size.fromRadius(totalRadius),
        emptySize: Size.fromRadius(emptyRadius),
        filledSize: Size.fromRadius(filledRadius),
        emptyDecoration: decoration,
        filledDecoration: decoration,
        emptyTextStyle: textStyle.copyWith(fontSize: 0.0),
        filledTextStyle: textStyle);
  }

  /// Builds the input inside a rectangle.
  static CodeInputBuilder rectangle({
    Size totalSize = const Size(50.0, 60.0),
    Size emptySize = const Size(20.0, 20.0),
    Size filledSize = const Size(40.0, 60.0),
    BorderRadius borderRadius = BorderRadius.zero,
    required Border border,
    required Color color,
    required TextStyle textStyle,
  }) {
    final decoration = BoxDecoration(
      border: border,
      borderRadius: borderRadius,
      color: color,
    );

    return containerized(
        totalSize: totalSize,
        emptySize: emptySize,
        filledSize: filledSize,
        emptyDecoration: decoration,
        filledDecoration: decoration,
        emptyTextStyle: textStyle.copyWith(fontSize: 0.0),
        filledTextStyle: textStyle);
  }

  /// Builds the input inside a light circle.
  static CodeInputBuilder lightCircle({
    double totalRadius = 30.0,
    double emptyRadius = 10.0,
    double filledRadius = 25.0,
  }) {
    return circle(
        totalRadius: totalRadius,
        emptyRadius: emptyRadius,
        filledRadius: filledRadius,
        border: Border.all(color: Colors.white, width: 2.0),
        color: Colors.white10,
        textStyle: const TextStyle(
            color: Colors.white, fontSize: 20.0, fontWeight: FontWeight.bold));
  }

  /// Builds the input inside a light circle.
  static CodeInputBuilder darkCircle({
    double totalRadius = 30.0,
    double emptyRadius = 10.0,
    double filledRadius = 25.0,
  }) {
    return circle(
        totalRadius: totalRadius,
        emptyRadius: emptyRadius,
        filledRadius: filledRadius,
        border: Border.all(color: Colors.black, width: 2.0),
        color: Colors.black12,
        textStyle: const TextStyle(
            color: Colors.black, fontSize: 20.0, fontWeight: FontWeight.bold));
  }

  /// Builds the input inside a light rectangle.
  static CodeInputBuilder lightRectangle({
    Size totalSize = const Size(50.0, 60.0),
    Size emptySize = const Size(20.0, 20.0),
    Size filledSize = const Size(40.0, 60.0),
    BorderRadius borderRadius = BorderRadius.zero,
  }) {
    return rectangle(
        totalSize: totalSize,
        emptySize: emptySize,
        filledSize: filledSize,
        borderRadius: borderRadius,
        border: Border.all(color: Colors.white, width: 2.0),
        color: Colors.white10,
        textStyle: const TextStyle(
            color: Colors.white, fontSize: 20.0, fontWeight: FontWeight.bold));
  }

  static CodeInputBuilder staticRectangle({
    Size totalSize = const Size(60.0, 60.0),
    Size emptySize = const Size(40.0, 40.0),
    Size filledSize = const Size(40.0, 40.0),
    BorderRadius borderRadius = BorderRadius.zero,
  }) {
    return rectangle(
        totalSize: totalSize,
        emptySize: emptySize,
        filledSize: filledSize,
        borderRadius: borderRadius,
        border: Border.all(color: Colors.white, width: 1.0),
        color: Colors.transparent,
        textStyle: const TextStyle(
            color: Colors.white, fontSize: 20.0, fontWeight: FontWeight.bold));
  }

  /// Builds the input inside a dark rectangle.
  static CodeInputBuilder darkRectangle({
    Size totalSize = const Size(50.0, 60.0),
    Size emptySize = const Size(20.0, 20.0),
    Size filledSize = const Size(40.0, 60.0),
    BorderRadius borderRadius = BorderRadius.zero,
  }) {
    return rectangle(
        totalSize: totalSize,
        emptySize: emptySize,
        filledSize: filledSize,
        borderRadius: borderRadius,
        border: Border.all(color: Colors.black, width: 2.0),
        color: Colors.black12,
        textStyle: const TextStyle(
            color: Colors.black, fontSize: 20.0, fontWeight: FontWeight.bold));
  }
}
相关推荐
且听真言16 天前
Flutter 实现全局悬浮按钮学习
material·positioned·gesturedetector·boxdecoration·overlayentry
且听真言1 个月前
flutter实现如何 检测键盘的显示和隐藏状态
widgetsbinding·gesturedetector·detector·focusscope·hittestbehavior·showcallback
hudawei9964 个月前
flutter widget 设置GestureDetector点击无效
flutter·gesturedetector·点击事件
qq_289093871 年前
android 手机主界面侧滑退出app问题
android·kotlin·手势·gesturedetector