Flutter发布组件

439 阅读6分钟

前言

学习Flutter有一段时间了,一直想尝试开发一个组件,最近正好结合项目所需,准备自己开发一个条件联动选择器的功能,并将它封装成组件发布到pub.dev

开发组件

需求描述

点击某个功能按钮,实现底部弹窗,数据填充后,item纵向滚动,可以支持单选和多选,以及多选联动的效果。弹窗顶部展示取消和完成按钮,点击完成后,拿到选中的数据。

功能梳理

1.底部弹窗实现

2.定义实体数据

3.列表滚动实现

4.组件封装实现

功能实现

底部弹窗实现

说起底部弹窗,那首先会想到的是showModalBottomSheet,它可以很方便的帮我们实现底部弹窗

Future<T?> showModalBottomSheet<T>({
  required BuildContext context,
  required WidgetBuilder builder,
  Color? backgroundColor,
  double? elevation,
  ShapeBorder? shape,
  Clip? clipBehavior,
  BoxConstraints? constraints,
  Color? barrierColor,
  bool isScrollControlled = false,
  bool useRootNavigator = false,
  bool isDismissible = true,
  bool enableDrag = true,
  RouteSettings? routeSettings,
  AnimationController? transitionAnimationController,
  Offset? anchorPoint,
})

常规属性说明:

  • context:上下文,必传参数
  • builder:构建内容,必传参数
  • backgroundColor:背景颜色
  • elevation:阴影高度,没有实际效果
  • shape:边框形状,例如设置圆角
  • barrierColor:蒙层颜色
  • isDismissible:点击外部区域是否关闭弹窗
  • enableDrag:拖拽是否支持关闭弹窗

定义实体数据

我们需要定义这样一个实体类,它应该需要包含一个能识别的id、展示的名称、选中的状态、是否支持全选状态、是否支持需要隐藏下一级栏位、联动的子数据集合等参数,例如:

class SelectorItem<T> {
  String _id = '';
  String _name = '';
  bool _hideNext = false;
  List<SelectorItem>? _childList;
  T? _param;

  String get id => _id;

  String get name => _name;

  bool get hideNext => _hideNext;

  List<SelectorItem>? get childList => _childList;

  T? get param => _param;

  bool check = false;
  bool supportSelectAll = false;

  SelectorItem(
      {required String id,
      required String name,
      List<SelectorItem>? childList,
      bool hideNext = false,
      bool isCheck = false,
      bool isSupportSelectAll = false,
      T? param}) {
    _id = id;
    _name = name;
    _hideNext = hideNext;
    _childList = childList;
    check = isCheck;
    supportSelectAll = isSupportSelectAll;
    _param = param;
  }

  SelectorItem.init();

  @override
  String toString() {
    return 'id:$id name:$name';
  }
}

列表滚动实现

要说列表滚动,首先想到的可能是listview,但是选择器这种有没有像ios风格的齿轮滚动的选择器呢?答案肯定是有的,我们可以基于CupertinoPicker组件来实现

  CupertinoPicker({
    super.key,
    this.diameterRatio = _kDefaultDiameterRatio,
    this.backgroundColor,
    this.offAxisFraction = 0.0,
    this.useMagnifier = false,
    this.magnification = 1.0,
    this.scrollController,
    this.squeeze = _kSqueeze,
    required this.itemExtent,
    required this.onSelectedItemChanged,
    required List<Widget> children,
    this.selectionOverlay = const CupertinoPickerDefaultSelectionOverlay(),
    bool looping = false,
  })

常规属性说明:

  • diameterRatio:直径比,控制子widget上下偏移
  • backgroundColor:背景颜色
  • offAxisFraction:轴偏移,控制子widget左右偏移
  • useMagnifier:是否使用放大镜效果
  • magnification:放大镜倍数
  • scrollController:控制器,例如:可以用来初始化选中位置
  • squeeze:挤压,控制子widget上下偏移
  • itemExtent:子widget的高度范围
  • onSelectedItemChanged:滚动回调,会把当前选中的子widget的位置返回
  • children:子widget集合
  • selectionOverlay:选中样式,可以自定义widget实现
  • looping:是否支持循环

组件封装实现

现在底部弹窗和选择器滚动的实现我们已经了解的差不多了,接下来我们只需要实现里面的逻辑交互、数据联动、数据回传、参数自定义等功能即可,然后把它封装成一个功能组件出来。由于封装实现代码量较多,这里就不展开细述了,如需了解请前往flutter_selector,多级联动的核心实现:

    // 通过循环调用_addLevel函数,添加子widget
    List<Widget> children = [];
    _addLevel(List<SelectorItem>? selectorItems,
        FixedExtentScrollController? controller, int index) {
      // 如果数据不为空,才添加视图
      if (null != selectorItems) {
        children.add(Expanded(
            child: CupertinoPicker(
          backgroundColor: Colors.white,
          useMagnifier: true,
          scrollController: controller,
          diameterRatio: widget.diameterRatio ?? 1,
          offAxisFraction: widget.offAxisFraction ?? 0.0,
          magnification: widget.magnification ?? 1.2,
          squeeze: widget.squeeze ?? 1.45,
          selectionOverlay: widget.selectionOverlay ??
              getSelectionOverlayWidget(
                  index == 0 ? widget.padding : 0,
                  index == (widget.list.length - 1) ? widget.padding : 0,
                  widget.lineColor),
          itemExtent: widget.itemExtent,
          onSelectedItemChanged: (position) {
            for (int i = 0; i < 4; i++) {
              if (i == index) {
                _refreshData(i, position, false);
              } else if (i > index) {
                _refreshData(i, 0, true);
              }
            }
            setState(() {});
          },
          children:
              getChildren(selectorItems, widget.textSize, widget.textColor),
        )));
      }
    }  

  // 联动处理,刷新数据
  _refreshData(int level, int position, bool jump) {
    switch (level) {
      case 0:
        _position0 = position;
        _selectorItem0 = widget.list[position];
        if (jump) {
          _controller0?.jumpToItem(position);
        }
        break;
      case 1:
        _position1 = position;
        _selectorItem1 = _selectorItem0?.childList?[position];
        if (jump) {
          _controller1?.jumpToItem(position);
        }
        break;
      case 2:
        _position2 = position;
        _selectorItem2 = _selectorItem1?.childList?[position];
        if (jump) {
          _controller2?.jumpToItem(position);
        }
        break;
      case 3:
        _position3 = position;
        _selectorItem3 = _selectorItem2?.childList?[position];
        if (jump) {
          _controller3?.jumpToItem(position);
        }
        break;
    }
  }

发布组件

制作组件

File > New > New Flutter project > next > Project type: package

  • Flutter Application: 编写标准的Flutter App工程

  • Flutter Module : 用于混编,与原生混合开发

  • Flutter Package:纯Dart组件,仅包含Dart层的实现

  • Flutter Plugin:插件工程,包含Dart层与Native平台层的实现

创建好依赖库之后,就可以编写项目代码了。发布包时,遵循pubspec 格式包布局约定

必须包含一个LICENSE文件,推荐Dart 和 Flutter 团队通常使用的BSD 3-clause license

编写好readme.md文件,然后将项目发布到pub.dev/

发布组件

创建发布者

点击 Create publisher 创建发布者

使用经过验证的发布者的优势

您可以使用经过验证的发布者(推荐)或独立的 Google 帐户发布包。

使用经过验证的发布者具有以下优势:

  • 您的包的消费者知道发布者域已经过验证。
  • 您可以避免让 pub.dev 显示您的个人电子邮件地址。相反,pub.dev 显示发布者域和联系地址。
  • 在搜索页面和单个包页面上,经过验证的发布者徽章会显示在您的包名称旁边。

创建经过验证的发布者

要创建经过验证的发布者,请执行以下步骤:

  1. 转到pub.dev。
  2. 使用 Google 帐户登录 pub.dev。
  3. 在右上角的用户菜单中,选择Create Publisher
  4. 输入要与发布者关联的域名(例如 dart.dev),然后单击创建发布者。
  5. 在确认对话框中,选择OK
  6. 如果出现提示,请完成验证流程,这将打开Google Search Console。
    • 添加 DNS 记录时,Search Console 可能需要几个小时才能反映更改。
    • 验证流程完成后,返回步骤 4。

发布你的包

使用该dart pub publish命令首次发布您的包,或将其更新到新版本。

发布成功之后,可以登录账号查看已发布的插件

上传项目到GitHub

步骤 1: 在 GitHub 上创建一个新的仓库

  1. 登录到您的 GitHub 帐户。
  2. 在页面右上角,点击加号符号(+),然后选择“New repository”。
  3. 输入仓库名称、描述等信息,然后点击“Create repository”。

步骤 2: 在本地初始化 Git 仓库并关联到 GitHub 仓库

打开终端(Terminal)或命令行界面,进入本地项目文件夹,使用以下命令初始化本地 Git 仓库:

git init

把当前分支重命名为master

git branch -M master  

使用以下命令将本地 Git 仓库关联到 GitHub 上创建的远程仓库

git remote add origin <repository_URL>

步骤 3: 添加文件到本地仓库并提交更改

使用以下命令将所有文件添加到暂存区:

git add .

使用以下命令提交更改到本地仓库,并添加提交信息:

git commit -m "Initial commit"

步骤 4: 将本地更改推送到 GitHub

使用以下命令将本地更改推送到 GitHub 上的远程仓库:

git push -u origin master

总结

可以通过不断学习和积累,可以结合自身业务实现一些组件的封装,减少重复性工作。flutter_selector