我们使用了哪些Flutter 三方库(二)

一、背景

前面写了《我们使用了哪些 Flutter 三方库》,但是仅介绍了工程使用的一部分Flutter三方库,本篇继续介绍工程中使用Flutter 三方库。

二、Hive、hive_flutter

轻量级高性能键值数据库,提供比SQLite更快的本地存储方案

Pub地址:pub.dev/packages/hi...

  • ✅ 高性能键值存储:基于 Dart 原生实现,比 SQLite 更快
  • ✅ 零依赖:不依赖平台原生代码,纯 Dart 实现
  • ✅ 类型安全:通过泛型支持自动序列化/反序列化
  • ✅ 跨平台支持:iOS/Android/Web/Desktop(全平台兼容)
  • ✅ 加密支持:内置 AES-256 加密(需 hive_flutter 扩展)

初始化:

dart 复制代码
wait Hive.initFlutter();

获取box对象

csharp 复制代码
await Hive.openBox('xxx')

读取、写入

dart 复制代码
// 写入数据
box.put('name', 'Alice');
box.put('age', 25);

// 读取数据
String name = box.get('name');
int age = box.get('age', defaultValue: 0);

三、auto_size_text

智能文本尺寸调节组件,自动缩放文字以适应可用空间

  • ✅ 智能缩放:自动调整字体大小填满容器
  • ✅ 多行支持:完美处理文本换行与省略号
  • ✅ 性能优化:内置文本缓存避免重复计算
  • ✅ 精细控制:支持最小/最大字体尺寸限制
  • ✅ 无缝集成:完全兼容所有Text组件的属性

主要是使用其 AutoSizeText 这个Widget,工程中直接拿来用了没啥封装。

四、url_launcher

跨平台URL启动器,一键调用系统浏览器/地图/电话/邮件等原生功能

  • ✅ 多协议支持:
    • http(s):// → 打开网页
    • tel:// → 拨打电话
    • sms:// → 发送短信
    • mailto:// → 调用邮件客户端
    • geo:// → 打开地图应用
  • ✅ 启动模式控制:
    • 应用内WebView(WebView插件配合使用)
    • 系统默认浏览器
    • 强制使用Safari/Chrome
  • ✅ 结果回调:检测功能是否可用及执行结果

核心方法只有一个:launchUrl

其他方法还有:

  • canLaunchUrl
  • closeInAppWebView
  • supportsLaunchMode
  • supportsCloseForLaunchMode

需要主要的是在不同的平台可能存在表现差异

五、flutter_linkify

智能文本链接解析器,自动识别文本中的URL/邮箱/电话并转为可点击链接

  • ✅ 支持正则表达式自定义匹配规则
  • ✅ 内置人类可读URL展示优化(如显示"google.com"而非"www.google.com")
  • ✅ 完全兼容Text的所有样式属性

基本用法

dart 复制代码
Linkify(
  text: "访问 https://flutter.dev 或联系 [email protected]",
  onOpen: (link) => launchUrl(Uri.parse(link.url)), // 配合url_launcher使用
  options: LinkifyOptions(humanize: true), // 自动简化URL显示
)

可以继承 Linkifier ,来自定义协议解析规则,如我们工程中定义的解析规则:

dart 复制代码
final _urlRegex = RegExp(
  r'^(.*?)((?:https?:\/\/|www\.)[^\s/$.?#].[^\s]*)',
  caseSensitive: false,
  dotAll: true,
);

final _looseUrlRegex = RegExp(
  r'''^(.*?)((https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&//="'`]*))''',
  caseSensitive: false,
  dotAll: true,
);

final _protocolIdentifierRegex = RegExp(
  r'^(https?:\/\/)',
  caseSensitive: false,
);

class UrlLinkifier extends Linkifier {
  const UrlLinkifier();

  @override
  List<LinkifyElement> parse(elements, options) {
    final list = <LinkifyElement>[];

    for (var element in elements) {
      if (element is TextElement) {
        var match = options.looseUrl
            ? _looseUrlRegex.firstMatch(element.text)
            : _urlRegex.firstMatch(element.text);

        if (match == null) {
          list.add(element);
        } else {
          final text = element.text.replaceFirst(match.group(0)!, '');

          if (match.group(1)?.isNotEmpty == true) {
            list.add(TextElement(match.group(1)!));
          }

          if (match.group(2)?.isNotEmpty == true) {
            var originalUrl = match.group(2)!;
            var originText = originalUrl;
            String? end;

            if ((options.excludeLastPeriod) &&
                originalUrl[originalUrl.length - 1] == ".") {
              end = ".";
              originText = originText.substring(0, originText.length - 1);
              originalUrl = originalUrl.substring(0, originalUrl.length - 1);
            }

            var url = originalUrl;

            if (!originalUrl.startsWith(_protocolIdentifierRegex)) {
              originalUrl = (options.defaultToHttps ? "https://" : "http://") +
                  originalUrl;
            }

            if ((options.humanize) || (options.removeWww)) {
              if (options.humanize) {
                url = url.replaceFirst(RegExp(r'https?://'), '');
              }
              if (options.removeWww) {
                url = url.replaceFirst(RegExp(r'www\.'), '');
              }

              list.add(UrlElement(
                originalUrl,
                url,
                originText,
              ));
            } else {
              list.add(UrlElement(originalUrl, null, originText));
            }

            if (end != null) {
              list.add(TextElement(end));
            }
          }

          if (text.isNotEmpty) {
            list.addAll(parse([TextElement(text)], options));
          }
        }
      } else {
        list.add(element);
      }
    }

    return list;
  }
}

/// Represents an element containing a link
class UrlElement extends LinkableElement {
  UrlElement(String url, [String? text, String? originText])
      : super(text, url, originText);

  @override
  String toString() {
    return "LinkElement: '$url' ($text)";
  }

  @override
  bool operator ==(other) => equals(other);

  @override
  int get hashCode => Object.hash(text, originText, url);

  @override
  bool equals(other) => other is UrlElement && super.equals(other);
}

六、flutter_slidable

强大的滑动操作组件,为列表项添加左/右滑出菜单,支持多种交互手势和动画效果

  • ✅ 支持左右双向滑动操作
  • ✅ 内置6种动作面板动画效果
  • ✅ 可自定义滑动阈值和灵敏度
  • ✅ 完美兼容ScrollView各种场景

主要就是使用 Slidable 组件。

示例代码(工程中比较负责,这里贴一个示例)

less 复制代码
Slidable(
  endActionPane: ActionPane(
    motion: const ScrollMotion(), // 滑动动画类型
    children: [
      SlidableAction(
        icon: Icons.delete,
        backgroundColor: Colors.red,
        onPressed: (_) => _deleteItem(context),
      ),
      SlidableAction(
        icon: Icons.share,
        backgroundColor: Colors.blue,
        onPressed: (_) => _shareItem(context),
      ),
    ],
  ),
  child: ListTile(title: Text('可滑动的列表项')),
)

同时Slidable还可以通过自定义ActionPane实现弧形菜单、动态宽度等高级效果。

七、time_machine

强大的日期时间处理库,提供时区、日历、周期计算等高级功能

  • ✅ 完整的时区数据库(IANA TZDB)
  • ✅ 人类友好的时间差计算(如"2小时前")
  • ✅ 支持多种日历系统(ISO、Gregorian等)
  • ✅ 不可变类型设计(线程安全)
dart 复制代码
final now = LocalClock().today(); // 获取当前日期
final birthday = LocalDate(2025, 5, 25);

// 计算时间差
final period = now.periodUntil(birthday); 
print('距离生日还有: ${period.months}月${period.days}天');

在我们的工程中主要是使用了:DayOfWeek

八、flutter_keyboard_visibility

实时监听软键盘显示/隐藏状态,完美解决键盘遮挡输入框问题

  • ✅ 全平台支持(Android/iOS/Web/Desktop)
  • ✅ 毫秒级响应键盘状态变化
  • ✅ 支持多监听器订阅模式
  • ✅ 完美兼容单页面多表单场景

在我们工程中使用的是 KeyboardVisibilityBuilder ,组件级别的监听方案,示例代码:

less 复制代码
KeyboardVisibilityBuilder(
  builder: (context, isKeyboardVisible) {
    return AnimatedContainer(
      height: isKeyboardVisible ? 60 : 200,
      duration: Duration(milliseconds: 300),
      child: TextField(),
    );
  },
)

九、extended_text_field

高度可定制的富文本输入框,支持@提及、话题标签、自定义背景等高级功能

  • ✅ 原生TextField的所有功能+扩展能力
  • ✅ 支持富文本特殊内容高亮(如#话题、@用户)
  • ✅ 自定义文本背景/下划线样式
  • ✅ 精确控制光标位置和选择范围

示例代码

dart 复制代码
ExtendedTextField(
  specialTextSpanBuilder: MySpecialTextSpanBuilder(), // 自定义解析器
  controller: _controller,
  decoration: InputDecoration(hintText: "输入@提及用户或#话题"),
)

十、总结

以上在上一篇的基础上又介绍了几个三方库,希望能够给读者带来一些额外的思考。

相关推荐
二流小码农36 分钟前
鸿蒙开发:loading动画的几种实现方式
android·ios·harmonyos
wsxlgg1 小时前
IOS 打包账号发布上传和IOS Xcode证书配置
ios
爱吃西红柿!1 小时前
fastadmin fildList 动态下拉框默认选中
android·前端·javascript
Digitally1 小时前
iTunes 无法备份 iPhone:10 种解决方法
ios·gradle·iphone
悠哉清闲2 小时前
工厂模式与多态结合
android·java
大耳猫3 小时前
Android SharedFlow 详解
android·kotlin·sharedflow
火柴就是我3 小时前
升级 Android Studio 后报错 Error loading build artifacts from redirect.txt
android
androidwork5 小时前
掌握 MotionLayout:交互动画开发
android·kotlin·交互
胖虎15 小时前
SwiftUI 数据绑定与视图更新(@State、@ObservedObject、@EnvironmentObject)
ios·swiftui·swift
奔跑吧 android5 小时前
【android bluetooth 协议分析 14】【HFP详解 1】【案例一: 手机侧显示来电,但车机侧没有显示来电: 讲解AT+CLCC命令】
android·hfp·aosp13·telecom·ag·hf·headsetclient