Flutter密码构建插件full_pass_builder的使用

Flutter密码构建插件full_pass_builder的使用

Full Pass Builder Library

此库旨在实现几乎所有的 <code>CustomMultiChildLayout</code> 功能,但代码更少。

如果你遇到以下问题,此库可能会对你有所帮助:

  • 需要在单个帧内基于兄弟节点的布局信息执行某些操作。
  • 需要自定义布局算法而不必编写自己的渲染对象。
  • 需要约束和尺寸来决定最终的布局。
  • 需要对传递给子节点的约束进行更多控制。

此库并未添加任何新功能。它只是通过几个API暴露了Flutter布局算法的一些阶段。有关更多信息,请参阅“为什么我创建了这个”部分。

FullPassBuilder 接受一个子节点列表,并暴露以下几点:

  1. (可选)每个子节点在其自身子节点/子节点之前的位置,您会收到当前 RenderBox 的子节点列表以及从父节点传递的约束条件,并决定每个子节点是否应遵循这些约束条件。
  2. 每个子节点已经使用来自第1点的可选约束条件布局其子节点之后的位置。此时,我们可以访问子节点的大小,并可以控制每个子节点的确切位置以及基于报告大小的容器最终大小。
  3. 当然,还有来自父节点的约束条件。

开始使用

创建一个带有子节点列表和自定义算法的 FullPassBuilder 小部件。

以下是一个粘性页脚布局的示例。

Widget build(BuildContext context) {
  return Container(
      child: Column(
          children: [
            Text("Photo Gallery"),
            SizedBox(height: 8),
            // 粘性页脚从这里开始。
            FullPassBuilder(
                childrenBuilder: (context, constraints) =>
                [
                  ...contentWidgets,
                  SizedBox(height: 8),
                  TextButton("I am the sticky button!", onPressed: () {}),
                ],
                // 看到了吗?仅此而已,无需大量代码。
                layouterVisitor: (context, layouter) {
                  double heightSoFar = 0;
                  layouter.forEachChild((constraints, size, offset, index) {
                    final mdof = MediaQuery.of(context);
                    // 是最后一个
                    if (layouter.childCount - 1 == index) {
                      final fixedBottom =
                          mdof.size.height - mdof.padding.top - size.height;
                      final contentBottom =
                          heightSoFar + mdof.padding.top - additonalBottomPadding;
                      offset.set = Offset(0, (max(fixedBottom, contentBottom)));
                    } else {
                      offset.set = Offset(0, heightSoFar);
                      heightSoFar += size.height;
                    }
                  });

                  return Size(
                      layouter.constraints.maxWidth,
                      max(
                          layouter.childrenParentData.last.offset.dy +
                              layouter.childrenSizes.last.height,
                          mdof.size.height));
                }
            )
          ]
      )
  );
}

该构建器接受三个参数。

  1. <code>childrenBuilder</code>,只是一个普通的多小部件生成器,签名是 <code>Widget Function(BuildContext, BoxConstraints)</code>
  2. <code>layouterVisitor</code>,一个回调函数,接收一个 <code>Layouter</code> 对象。Layouter 是一个帮助类,包含一些方法和属性,可以帮助你用更少的代码布局你的子节点。例如,maxRectangleminRectangle 描述了你的子节点在两个二维轴上的最小和最大尺寸,并且有一个 <code>forEachChild</code> 方法来帮助迭代子节点的双向链表。回调应该返回构建器决定的最终尺寸。
  3. <code>childrenConstrainer</code>,一个回调函数,接收一个子节点列表、原始约束条件,并返回每个子节点的新约束条件。

查看 full_pass_builder_examples.dart 文件夹中的 <code>full_pass_builder_examples.dart</code> 文件以获取更多代码片段。

为什么我创建了这个

虽然上述所有内容一直都是可能的,但组合使用 <code>GlobalKey</code><code>addPostFrameCallback</code> 始终是最快的,当然,这种速度伴随着自己的权衡——需要渲染两帧才能得到你想要的结果。它还使你的声明式小部件代码中充斥着不必要的命令式布局逻辑。

第二个选项是使用 <code>CustomMultiChildLayout</code>(或者稍微高级一点,自定义 <code>RenderBox</code> 子类),虽然更加灵活,但需要更深入地理解 Flutter 的内部机制,并且感觉非常模板化。有了这个库,所有重要的决策点都暴露给你,你只需要数学技能就能解决问题。 *该类在较低级别运行,因此库能做的一切,你都可以用 <code>CustomMultiChildLayout</code> 做到。

我相信所有库在尝试暴露 Flutter 的较低级别 API 时,都可以达到 <code>LayoutBuilder</code> 提供的 API 舒适度水平。<code>CustomMultiChildLayout</code> 提供的 API 在我需要快速移动时仍然需要太多模板代码。我希望有一组更简单的 API,允许与 <code>CustomMultiChildLayout</code> 相同的灵活性。

如果你已经熟悉创建自己的 <code>RenderBox</code> 和/或创建 <code>CustomMultiChildLayout</code>,那么这个库肯定不需要。

我可以用这个创建什么样的布局?

你可以想象的任何自定义布局……除非它与文本相关。在这种情况下,你可能想考虑 <code>CustomMultiChildLayout</code>(我选择的抽象的一个缺点,遗憾的是)。

我尝试的一些东西

  • 下面布局的代码可以在示例文件夹中找到

砖墙布局 #1

砖墙布局1

砖墙布局 #2

砖墙布局2

使用兄弟节点信息定位小部件

使用兄弟节点信息定位小部件

完整示例代码

以下是完整的示例代码,展示如何使用 FullPassBuilder 创建不同类型的布局。

import 'package:example/full_pass_builder_examples.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final List<Text> _textList = [
    const Text("Hello"),
  ];

  // 使用 FullPassBuilder 可创建的 API 示例
  late final List<Widget Function()> _examples = [
    () => FullPassBuilderExamples.masonryGrid(
          verticalGap: 8,
          horizontalGap: 8,
          maxColumn: 3,
          masonry: [
            MasonryGrid(
              rowUnitCount: 2,
              columnUnitCount: 2,
            ),
            MasonryGrid(
              rowUnitCount: 1,
              columnUnitCount: 1,
            ),
            MasonryGrid(
              rowUnitCount: 1,
              columnUnitCount: 1,
            ),
            MasonryGrid(
              rowUnitCount: 1,
              columnUnitCount: 1,
            ),
            MasonryGrid(
              rowUnitCount: 1,
              columnUnitCount: 1,
            ),
            MasonryGrid(
              rowUnitCount: 1,
              columnUnitCount: 2,
            ),
            MasonryGrid(
              rowUnitCount: 2,
              columnUnitCount: 1,
            ),
            MasonryGrid(
              rowUnitCount: 1,
              columnUnitCount: 1,
            ),
            MasonryGrid(
              rowUnitCount: 2,
              columnUnitCount: 2,
            ),
            MasonryGrid(
              rowUnitCount: 2,
              columnUnitCount: 1,
            ),
            MasonryGrid(
              rowUnitCount: 1,
              columnUnitCount: 1,
            ),
            MasonryGrid(
              rowUnitCount: 1,
              columnUnitCount: 1,
            ),
          ],
        ),
    () => FullPassBuilderExamples.verticalMasonry(
        verticalGap: 8,
        horizontalGap: 8,
        masonryBuilder: (context, constraints) {
          return [
            [
              Container(
                  width: constraints.maxWidth / 3,
                  height: 200,
                  color: Colors.black),
              Container(
                  width: constraints.maxWidth / 3,
                  height: 200,
                  color: Colors.pinkAccent),
              Container(
                  width: constraints.maxWidth / 3,
                  height: 300,
                  color: Colors.red),
              Container(
                  width: constraints.maxWidth / 3,
                  height: 100,
                  color: Colors.lightBlue),
              Container(
                  width: constraints.maxWidth / 3,
                  height: 100,
                  color: Colors.lightGreen),
            ],
            [
              Container(
                  width: constraints.maxWidth / 3,
                  height: 300,
                  color: Colors.red),
              Container(
                  width: constraints.maxWidth / 3,
                  height: 200,
                  color: Colors.pinkAccent),
              Container(
                  width: constraints.maxWidth / 3,
                  height: 300,
                  color: Colors.black12),
              Container(
                  width: constraints.maxWidth / 3,
                  height: 100,
                  color: Colors.lightBlue),
              Container(
                  width: constraints.maxWidth / 3,
                  height: 100,
                  color: Colors.black),
            ],
            [
              Container(
                  width: constraints.maxWidth / 3,
                  height: 255,
                  color: Colors.purpleAccent),
              Container(
                  width: constraints.maxWidth / 3,
                  height: 100,
                  color: Colors.pinkAccent),
              Container(
                  width: constraints.maxWidth / 3,
                  height: 100,
                  color: Colors.greenAccent),
              Container(
                  width: constraints.maxWidth / 3,
                  height: 50,
                  color: Colors.lightBlue),
              Container(
                  width: constraints.maxWidth / 3,
                  height: 110,
                  color: Colors.yellow),
              Container(
                  width: constraints.maxWidth / 3,
                  height: 330,
                  color: Colors.brown),
            ]
          ];
        }),
    () => FullPassBuilderExamples.stickyFooter(
          additonalBottomPadding: kBottomNavigationBarHeight,
          childrenBuilder: (context, constraints) => [
            ..._textList,
            ..._textList,
          ],
          stickyChildBuilder: (context, constraints) => ElevatedButton(
              onPressed: () {
                setState(() {
                  _textList.add(const Text("New text"));
                  _textList.add(const Text("New text"));
                  _textList.add(const Text("New text"));
                });
              },
              child: const Text("Sticky Button::Click to Add More Text")),
        ),
    () => FullPassBuilderExamples.siblingsInformation(
          space: 16,
          topLeft: Container(
            color: Colors.red,
            height: 50,
            width: 50,
          ),
          center: Container(
            color: Colors.green,
            height: 100,
            width: 100,
          ),
          bottomRight: Container(
            color: Colors.blue,
            height: 75,
            width: 25,
          ),
        ),
  ];

  int _exampleIndex = 0;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: Scaffold(
          bottomNavigationBar: BottomNavigationBar(
              items: const [
                BottomNavigationBarItem(
                  icon: Icon(Icons.abc),
                  label: 'Masonry Grid',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.abc),
                  label: 'Vertical Masonry',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.abc),
                  label: 'Sticky Footer',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.abc),
                  label: 'Siblings Information',
                ),
              ],
              currentIndex: _exampleIndex,
              unselectedFontSize: 16,
              unselectedLabelStyle: const TextStyle(color: Colors.black),
              unselectedIconTheme: const IconThemeData(
                color: Colors.greenAccent,
              ),
              selectedItemColor: Colors.amber[800],
              onTap: (index) {
                setState(() {
                  _exampleIndex = index;
                });
              }),
          body: Container(
            key: ObjectKey(_examples),
            color: Colors.red.withOpacity(0.2),
            child: SingleChildScrollView(
              scrollDirection: Axis.vertical,
              child: _examples[_exampleIndex](),
            ),
          ),
        )
        // 当小部件有所有子节点的信息时
        // bottomUp: (parentConstraints, childrenGeometries) => [])),
        );
  }
}

更多关于Flutter密码构建插件full_pass_builder的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter密码构建插件full_pass_builder的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


full_pass_builder 是一个 Flutter 插件,用于帮助开发者快速构建密码输入界面。它提供了多种自定义选项,可以根据需求定制密码输入框的外观和行为。以下是如何使用 full_pass_builder 插件的详细步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 full_pass_builder 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  full_pass_builder: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 导入包

在你的 Dart 文件中导入 full_pass_builder 包:

import 'package:full_pass_builder/full_pass_builder.dart';

3. 使用 FullPassBuilder

FullPassBuilder 是一个小部件,用于构建密码输入界面。你可以通过传递不同的参数来自定义它的外观和行为。

class PasswordInputPage extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Password Input'),
      ),
      body: Center(
        child: FullPassBuilder(
          length: 6, // 密码长度
          onCompleted: (String password) {
            // 当密码输入完成时的回调
            print('Password entered: $password');
          },
          obscureText: true, // 是否隐藏密码字符
          obscureTextSymbol: '*', // 隐藏密码字符的符号
          inputDecoration: InputDecoration(
            border: OutlineInputBorder(),
            labelText: 'Enter Password',
          ),
          spacing: 8.0, // 输入框之间的间距
        ),
      ),
    );
  }
}

4. 参数说明

以下是一些常用的参数及其说明:

  • length: 密码的长度,默认为 6。
  • onCompleted: 当密码输入完成时的回调函数,返回输入的密码。
  • obscureText: 是否隐藏密码字符,默认为 true
  • obscureTextSymbol: 隐藏密码字符时使用的符号,默认为 *
  • inputDecoration: 输入框的装饰,可以使用 InputDecoration 来自定义外观。
  • spacing: 输入框之间的间距,默认为 8.0

5. 自定义样式

你可以通过 inputDecoration 参数来自定义输入框的外观。例如,你可以设置输入框的边框、标签、提示文本等。

inputDecoration: InputDecoration(
  border: OutlineInputBorder(
    borderRadius: BorderRadius.circular(10.0),
  ),
  labelText: 'Password',
  hintText: 'Enter your password',
  filled: true,
  fillColor: Colors.grey[200],
),

6. 处理密码输入

当用户输入完密码后,onCompleted 回调函数会被触发,你可以在其中处理密码的逻辑。例如,验证密码是否正确,或者将密码发送到服务器。

onCompleted: (String password) {
  if (password == '123456') {
    print('Password is correct');
  } else {
    print('Password is incorrect');
  }
},

7. 完整示例

以下是一个完整的示例,展示了如何使用 FullPassBuilder 构建一个密码输入界面:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FullPassBuilder Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PasswordInputPage(),
    );
  }
}

class PasswordInputPage extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Password Input'),
      ),
      body: Center(
        child: FullPassBuilder(
          length: 6,
          onCompleted: (String password) {
            print('Password entered: $password');
          },
          obscureText: true,
          obscureTextSymbol: '*',
          inputDecoration: InputDecoration(
            border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(10.0),
            ),
            labelText: 'Enter Password',
            hintText: 'Enter your password',
            filled: true,
            fillColor: Colors.grey[200],
          ),
          spacing: 8.0,
        ),
      ),
    );
  }
}
回到顶部