jskit

Flutter 常用工具包,包含网络请求封装、格式化与节流工具,以及一组轻量 UI 组件。

当前主入口为 lib/jskit.dart,公开导出:

  • 网络层:JSCoreDioUtilJSBaseRequest
  • 工具类:JsToolsFunctionExt
  • 扩展能力:时间/数字/邮箱/密码强度格式化
  • 组件:VitrifyFrostedGlassTurnBoxGradientBorderAfterLayoutCustomBlurAppBarKeepAliveWrapper

安装

按你的接入方式选择一种:

本地路径引用

dependencies:
  jskit:
    path: ../jskit-flutter

Git 引用

dependencies:
  jskit:
    git:
      url: https://github.com/json1994/jskit-flutter.git

已发布到仓库时

dependencies:
  jskit: ^0.1.9

导入:

import 'package:jskit/jskit.dart';

功能概览

1. 网络请求封装

适合后端返回结构固定为下面这种场景:

{
  "code": 200,
  "message": "success",
  "total": 0,
  "data": {}
}

JSBaseRequest 默认按这个结构读取 codemessagetotaldata

2. 工具方法

  • JsTools.generateRandomColor
  • JsTools.generateRandomString
  • JsTools.getMd5()
  • JsTools.showSheetWidget()
  • JsTools.showDropDownWidget()
  • JsTools.textFieldConfig()
  • JsTools.calculateCacheSize()
  • JsTools.clearCache()
  • JsTools.writeFile()

3. 扩展能力

  • num?.countFormat
  • num?.durationFormat
  • num?.formatFileSize
  • DateTime?.toYMD
  • DateTime?.week
  • DateTime?.monthStr
  • DateTime?.messageTime()
  • String?.isEmail
  • String?.checkPasswordStrength()
  • Function.throttle()
  • Function.throttleWithTimeout()
  • Function.debounce()

4. UI 组件

  • Vitrify:毛玻璃容器,可配置颜色、透明度、圆角、blur 和动画
  • FrostedGlass:简单毛玻璃背景层
  • TurnBox:旋转动画组件
  • GradientBorder:渐变描边
  • AfterLayout:布局结束后拿到 render 信息
  • CustomBlurAppBar:滚动时带模糊效果的 AppBar
  • KeepAliveWrapper:列表/分页保活包装

快速开始

初始化网络层

await JSCore().init(
  baseUrl: 'https://api.example.com',
  hookRequest: (options) {
    options.headers['token'] = 'your-token';
  },
);

如果你已经有自己的 Dio 实例,也可以直接注入:

final dio = Dio(
  BaseOptions(
    baseUrl: 'https://api.example.com',
    connectTimeout: const Duration(seconds: 10),
    receiveTimeout: const Duration(seconds: 30),
  ),
);

await JSCore().init(dio: dio);

直接使用 DioUtil 发请求

final result = await DioUtil().request<Map<String, dynamic>>(
  '/user/profile',
  method: DioMethod.get,
  parameters: {'id': 1001},
);

if (result.response != null) {
  print(result.response);
}

自定义请求类

推荐业务中继承 JSBaseRequest,把接口定义收敛到请求对象里。

class UserEntity {
  final int id;
  final String name;

  UserEntity({
    required this.id,
    required this.name,
  });

  factory UserEntity.fromJson(Map<String, dynamic> json) {
    return UserEntity(
      id: json['id'] as int,
      name: json['name'] as String,
    );
  }
}

class UserDetailRequest
    extends JSBaseRequest<UserEntity, Map<String, dynamic>> {
  UserDetailRequest(int userId)
      : super(
          url: '/user/detail',
          method: DioMethod.get,
          parameters: {'id': userId},
          fromJson: UserEntity.fromJson,
        );
}

调用:

final response = await UserDetailRequest(1001).execute();

if (response.code == 200 && response.data != null) {
  print(response.data!.name);
} else {
  print(response.msg);
}

POST / PUT / PATCH 示例

class UpdateUserRequest
    extends JSBaseRequest<Map<String, dynamic>, Map<String, dynamic>> {
  UpdateUserRequest({
    required int id,
    required String name,
  }) : super(
          url: '/user/update',
          method: DioMethod.put,
          parameters: {'id': id},
          data: {'name': name},
          fromJson: (json) => json,
        );
}

工具示例

数字与时间格式化

final count = 12500.countFormat; // 1.3w
final duration = 95.durationFormat; // 01:35
final fileSize = 1024 * 1024 * 2.4.formatFileSize; // 2.40M

final now = DateTime.now();
print(now.toYMD);
print(now.week);
print(now.monthStr);

字符串校验

final email = 'demo@example.com'.isEmail;
final strength = 'abc123!@#'.checkPasswordStrength();

节流与防抖

final onTap = () async {
  print('submit');
};

final throttledTap = onTap.throttle();
final debouncedTap = onTap.debounce(timeout: 300);

弹窗工具

await JsTools.showSheetWidget(
  context,
  position: ModalPosition.bottom,
  builder: (_) {
    return Container(
      height: 240,
      color: Colors.white,
      child: const Center(child: Text('bottom sheet')),
    );
  },
);

输入框键盘工具条

final focus1 = FocusNode();
final focus2 = FocusNode();

final config = JsTools.textFieldConfig(
  nodes: [focus1, focus2],
  doneString: '完成',
);

组件示例

Vitrify

Vitrify(
  sigma: 12,
  opacity: 0.3,
  color: Colors.white,
  radius: BorderRadius.circular(16),
  child: Container(
    padding: const EdgeInsets.all(16),
    child: const Text('Glass Card'),
  ),
)

FrostedGlass

FrostedGlass(
  blurX: 12,
  blurY: 12,
  opacity: 0.2,
  child: Container(
    height: 160,
    alignment: Alignment.center,
    child: const Text('Blur Background'),
  ),
)

TurnBox

final controller = TurnBoxController(false);

TurnBox(
  turns: 3.1415926,
  speed: 300,
  controller: controller,
  child: const Icon(Icons.expand_more),
)

// 展开
controller.forward(from: 0);

// 收起
controller.reverse(from: 1);

GradientBorder

GradientBorder(
  isSelected: true,
  borderWidth: 2,
  borderRadius: 12,
  gradientColors: const [
    Colors.blue,
    Colors.cyan,
    Colors.green,
  ],
  child: Container(
    padding: const EdgeInsets.all(16),
    child: const Text('Gradient Border'),
  ),
)

AfterLayout

AfterLayout(
  callback: (render) {
    print(render.size);
    print(render.offset);
    print(render.rect);
  },
  child: Container(
    width: 100,
    height: 60,
    color: Colors.red,
  ),
)

KeepAliveWrapper

KeepAliveWrapper(
  keepAlive: true,
  child: ListView.builder(
    itemCount: 20,
    itemBuilder: (_, index) => ListTile(title: Text('$index')),
  ),
)

已知约定

  • JSBaseRequest 默认成功条件是 response['code'] == 200
  • 默认错误文案读取 response['message']
  • convert() 只有在 data is K 时才会调用 fromJson
  • TurnBox.turns 当前直接作为 Transform.rotate 的角度值使用,不是“圈数”
  • CustomBlurAppBar 适合放在滚动页面场景中使用

开发与验证

本仓库当前已验证:

flutter analyze
flutter test

发布

flutter packages pub publish --server=https://pub.dartlang.org