我写了个 Flutter 组件 ducafe_ui_core
视频
前言
许多群友询问我关于 ducafe_ui_core 的使用方法。这个组件是我将之前项目中频繁重复使用的功能独立抽取出来,以便在新项目中引入,从而提升开发效率。如果你想系统地学习 Flutter,欢迎关注我的新课程 woo2025。在本文中,我将介绍 ducafe_ui_core 包含的各种功能。
参考
https://ducafecat.com/course/flutter-woo-2025
https://pub.dev/packages/ducafe_ui_core
功能介绍
添加依赖
https://pub.dev/packages/ducafe_ui_core
pubspec.yaml
dependencies:
...
ducafe_ui_core: ^1.0.6
1 设计稿适配
编辑 lib/main.dart ,初始屏幕适配
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(375, 812),
builder: (context, child) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
},
);
}
}
包裹 ScreenUtilInit
编写 lib/page1.dart
Widget _buildView() {
return Container(
color: Colors.blue,
width: 300.w,
height: 300.h,
child: Column(
children: [
Text('Page One'),
],
),
);
}
通过 .w .h 动态适配计算
编辑 lib/main.dart ,跳转到新页面
void _incrementCounter() {
// 上下文导航操作
context.navigator.pushMaterial(const PageOne());
// 原始写法
// Navigator.push(
// context, MaterialPageRoute(builder: (context) => const PageOne()));
}
用 web 方式启动,方便测试内容区域尺寸变化
方正屏幕
又长又矮屏幕
2 视图层打薄
打薄后代码
Widget _buildView() {
return Text("ducafecat")
.fontSize(22)
.alignment(Alignment.center)
.borderRadius(all: 15)
.ripple()
.backgroundColor(Colors.white, animate: true)
.clipRRect(all: 25) // clip ripple
.borderRadius(all: 25, animate: true)
.elevation(
pressed ? 0 : 1,
borderRadius: BorderRadius.circular(25),
shadowColor: Color(0x30000000),
) // shadow borderRadius
.padding(vertical: 12) // margin
.onTap(
() => print('onTap'),
)
.scale(all: pressed ? 0.95 : 1.0, animate: true)
.animate(Duration(milliseconds: 150), Curves.easeOut)
.tight(
height: 80,
width: 200,
)
.center();
}
原始方式代码
Widget _buildView() {
return Center(
child: GestureDetector(
onTapDown: (_) => setState(() => pressed = true),
onTapUp: (_) => setState(() => pressed = false),
onTapCancel: () => setState(() => pressed = false),
child: AnimatedContainer(
duration: Duration(milliseconds: 150),
curve: Curves.easeOut,
height: 80,
width: 200,
transform: Matrix4.identity()..scale(pressed ? 0.95 : 1.0),
child: Material(
color: Colors.white,
elevation: pressed ? 0 : 1,
shadowColor: Color(0x30000000),
borderRadius: BorderRadius.circular(25),
child: InkWell(
onTap: () => print('onTap'),
borderRadius: BorderRadius.circular(25),
child: Container(
padding: EdgeInsets.symmetric(vertical: 12),
alignment: Alignment.center,
child: Text(
"ducafecat",
style: TextStyle(
fontSize: 22,
),
),
),
),
),
),
),
);
}
扩展方法:
- animate: 为组件添加动画效果
- align: 设置组件对齐方式
- alignCenter: 居中对齐组件
- alignLeft: 左对齐组件
- alignRight: 右对齐组件
- alignTop: 顶部对齐组件
- alignBottom: 底部对齐组件
- parent: 应用父组件
- padding: 为组件添加内边距
- paddingBottom: 添加底部内边距
- paddingHorizontal: 添加水平内边距
- paddingLeft: 添加左侧内边距
- paddingRight: 添加右侧内边距
- paddingTop: 添加顶部内边距
- paddingVertical: 添加垂直内边距
- paddingDirectional: 添加方向性内边距
- sliverPadding: 为 Sliver 组件添加内边距
- opacity: 设置组件透明度
- offstage: 控制组件的可见性
- neumorphism: 添加凹凸效果
- boxShadow: 添加阴影效果
- ripple: 添加涟漪效果
- rotate: 旋转组件
- scale: 缩放组件
- translate: 平移组件
- transform: 应用变换
- overflow: 控制组件溢出行为
- scrollable: 使组件可滚动
- expanded: 在 Flex 容器中扩展组件
- flexible: 在 Flex 容器中使组件灵活
- positioned: 在 Stack 中定位组件
- positionedDirectional: 在 Stack 中按方向定位组件
- inkWell: 添加墨水效果和点击事件
- safeArea: 将组件放置在安全区域
- semanticsLabel: 添加语义标签
- gestures: 添加手势识别
- center: 居中组件
- fittedBox: 使组件适应父组件
- fractionallySizedBox: 根据父组件比例设置大小
- card: 将组件包装在卡片中
- limitedBox: 限制组件的最大大小
- material: 将组件包装在 Material 中
- mouseRegion: 添加鼠标交互区域
3 主题样式
快速写一个依据当前主题的主按钮。
通过 context.colors.scheme
方式读取样式配置。
Widget _buildView() {
var scheme = context.colors.scheme;
return Text("ducafecat")
.fontSize(22)
.fontWeight(FontWeight.bold)
.textStyle(TextStyle(color: scheme.onPrimary))
.alignment(Alignment.center)
.borderRadius(all: 15)
.ripple()
.backgroundColor(scheme.primary, animate: true)
.clipRRect(all: 25) // clip ripple
.borderRadius(all: 25, animate: true)
.elevation(
pressed ? 0 : 1,
borderRadius: BorderRadius.circular(25),
shadowColor: Color(0x30000000),
) // shadow borderRadius
.padding(vertical: 12) // margin
.onTap(
() => print('onTap'),
)
.scale(all: pressed ? 0.95 : 1.0, animate: true)
.animate(Duration(milliseconds: 150), Curves.easeOut)
.tight(
height: 80,
width: 200,
)
.center();
}
扩展:
primary
: 主要颜色primaryLight
: 主要颜色的亮色版本primaryDark
: 主要颜色的暗色版本canvas
: 画布颜色scaffoldBackground
: 脚手架背景色card
: 卡片颜色divider
: 分割线颜色focus
: 焦点颜色hover
: 悬停颜色highlight
: 高亮颜色splash
: 水波纹颜色unselectedWidget
: 未选中部件的颜色disabled
: 禁用状态颜色secondaryHeader
: 次要标题颜色dialogBackground
: 对话框背景色indicator
: 指示器颜色hint
: 提示文字颜色scheme
: 完整的颜色方案shadow
: 阴影颜色
4 工具类
4.1 屏幕尺寸信息
示例:
context.screenWidth
扩展:
screenWidth
: 获取屏幕宽度screenHeight
: 获取屏幕高度bottomBarHeight
: 获取底部导航栏高度statusBarHeight
: 获取状态栏高度textScaleFactor
: 获取文本缩放因子scaleWidth
: 获取宽度的缩放因子scaleHeight
: 获取高度的缩放因子
4.2 时间差
示例:
// 创建一个 5 秒的 Duration
Duration fiveSeconds = 5.seconds;
扩展:
- microseconds: 将数字转换为微秒的 Duration
- milliseconds: 将数字转换为毫秒的 Duration
- seconds: 将数字转换为秒的 Duration
- minutes: 将数字转换为分钟的 Duration
- hours: 将数字转换为小时的 Duration
- days: 将数字转换为天的 Duration
- months: 将数字转换为月的 Duration(假设一个月为 30 天)
- quarters: 将数字转换为季度的 Duration(假设一个季度为 90 天)
- quadrimesters: 将数字转换为四个月的 Duration(假设四个月为 120 天)
- years: 将数字转换为年的 Duration(假设一年为 365 天)
- future: 启动一个延迟 delayed
4.3 Icon 扩展
// 创建一个原始图标
Icon originalIcon = Icon(Icons.star, size: 24, color: Colors.yellow);
// 使用 copyWith() 创建一个新图标,修改大小和颜色
Icon newIcon = originalIcon.copyWith(size: 32, color: Colors.red);
// 仅修改图标大小
Icon largerIcon = originalIcon.iconSize(48);
// 仅修改图标颜色
Icon blueIcon = originalIcon.iconColor(Colors.blue);
4.4 List 集合
示例:
List<Widget> widgets = [Widget1(), Widget2(), Widget3()];
Widget column = widgets.toColumn(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
separator: Divider(),
);
扩展:
- toColumn(): 将 Widget 列表转换为 Column
- toColumnSpace(): 将 Widget 列表转换为带有间距的 Column
- toRow(): 将 Widget 列表转换为 Row
- toRowSpace(): 将 Widget 列表转换为带有间距的 Row
- toStack(): 将 Widget 列表转换为 Stack
- toListView(): 将 Widget 列表转换为 ListView
- toWrap(): 将 Widget 列表转换为 Wrap
4.5 媒体查询
示例:
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 获取屏幕宽度
double screenWidth = context.sizes.width;
// 获取屏幕高度
double screenHeight = context.sizes.height;
// 获取屏幕边距
EdgeInsets padding = context.sizes.padding;
// 获取设备像素比
double pixelRatio = context.sizes.devicePixelRatio;
// 使用可能为空的属性
double? maybeWidth = context.sizes.maybeWidth;
return Container(
width: screenWidth * 0.8,
height: screenHeight * 0.5,
padding: padding,
child: Text(
'Device Pixel Ratio: $pixelRatio',
style: TextStyle(fontSize: 16 * context.sizes.textScaler.scale(1.0)),
),
);
}
}
扩展:
- width: 屏幕宽度
- height: 屏幕高度
- padding: 屏幕边距
- viewInsets: 视图插入
- systemGestureInsets: 系统手势插入
- viewPadding: 视图边距
- devicePixelRatio: 设备像素比
- textScaler: 文本缩放器
4.6 导航扩展
示例:
context.navigator.pushMaterial(NewPage());
扩展:
- canPop: 检查是否可以弹出当前路由
- maybePop: 尝试弹出当前路由
- pop: 弹出当前路由
- popUntil: 弹出路由直到满足条件
- push: 推送新路由
- pushMaterial: 使用 MaterialPageRoute 推送新页面
- pushCupertino: 使用 CupertinoPageRoute 推送新页面
- popAndPushNamed: 弹出当前路由并推送命名路由
- pushAndRemoveUntil: 推送新路由并删除之前的路由直到满足条件
- pushNamed: 推送命名路由
- pushNamedAndRemoveUntil: 推送命名路由并删除之前的路由直到满足条件
- pushReplacement: 推送新路由并替换当前路由
- pushReplacementMaterial: 使用 MaterialPageRoute 推送新页面并替换当前路由
- pushReplacementCupertino: 使用 CupertinoPageRoute 推送新页面并替换当前路由
- pushReplacementNamed: 推送命名路由并替换当前路由
- removeRoute: 移除指定路由
- removeRouteBelow: 移除指定路由下方的路由
- replace: 替换指定路由
- replaceRouteBelow: 替换指定路由下方的路由
4.7 数字转内边距
示例:
// 创建所有方向上偏移量为 16 的内边距
EdgeInsets padding1 = 16.paddingAll();
// 创建水平方向上偏移量为 20 的内边距
EdgeInsets padding2 = 20.paddingHorizontal();
// 创建垂直方向上偏移量为 10 的内边距
EdgeInsets padding3 = 10.paddingVertical();
// 创建只有顶部偏移量为 8 的内边距
EdgeInsets padding4 = 8.paddingTop();
// 创建只有左侧偏移量为 12 的内边距
EdgeInsets padding5 = 12.paddingLeft();
// 创建只有右侧偏移量为 15 的内边距
EdgeInsets padding6 = 15.paddingRight();
// 创建只有底部偏移量为 5 的内边距
EdgeInsets padding7 = 5.paddingBottom();
扩展:
- paddingAll: 创建所有方向上偏移量相等的内边距
- paddingHorizontal: 创建水平方向上对称的内边距
- paddingVertical: 创建垂直方向上对称的内边距
- paddingTop: 创建只有顶部内边距的 EdgeInsets
- paddingLeft: 创建只有左侧内边距的 EdgeInsets
- paddingRight: 创建只有右侧内边距的 EdgeInsets
- paddingBottom: 创建只有底部内边距的 EdgeInsets
4.8 平台查询
示例:
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (context.isAndroid) {
return Text('This is an Android device');
} else if (context.isIOS) {
return Text('This is an iOS device');
} else if (context.isMacOS) {
return Text('This is a MacOS device');
} else if (context.isWindows) {
return Text('This is a Windows device');
} else if (context.isLinux) {
return Text('This is a Linux device');
} else if (context.isFuchsia) {
return Text('This is a Fuchsia device');
} else {
return Text('Unknown platform');
}
}
}
扩展:
- platform: 获取当前平台
- isAndroid: 检查当前系统是否为 Android
- isIOS: 检查当前系统是否为 iOS
- isMacOS: 检查当前系统是否为 MacOS
- isWindows: 检查当前系统是否为 Windows
- isFuchsia: 检查当前系统是否为 Fuchsia
- isLinux: 检查当前系统是否为 Linux
4.9 数字转 SizedBox
示例:
// 创建一个宽度为 10 的水平间距
SizedBox horizontalGap = 10.horizontalSpace;
// 创建一个高度为 20 的垂直间距
SizedBox verticalGap = 20.verticalSpace;
// 在 Widget 树中使用
Column(
children: [
Text('First item'),
10.verticalSpace, // 添加 10 单位的垂直间距
Text('Second item'),
20.verticalSpace, // 添加 20 单位的垂直间距
Text('Third item'),
],
);
Row(
children: [
Text('Left'),
15.horizontalSpace, // 添加 15 单位的水平间距
Text('Right'),
],
);
扩展:
- horizontalSpace: 创建具有指定宽度的 SizedBox
- verticalSpace: 创建具有指定高度的 SizedBox
4.10 字符串
示例:
// 大写转换
print('hello world'.capitalize()); // 输出: Hello World
print('hello world'.capitalizeFirst()); // 输出: Hello world
// 布尔值转换
print('true'.isBool()); // 输出: true
print('false'.toBool()); // 输出: false
// 数字转换
print('123'.isNum()); // 输出: true
print('3.14'.toDouble()); // 输出: 3.14
print('42'.toInt()); // 输出: 42
// 颜色转换
Color color = 'FF5733'.toColor;
MaterialColor materialColor = 'FF5733'.toMaterialColor;
// 字符串处理
print('Hello World'.removeAllWhitespace()); // 输出: HelloWorld
print('abc123'.hasMatch(r'\d+')); // 输出: true
print('<p>Hello</p>'.clearHtml); // 输出: Hello
扩展:
- capitalize: 将每个单词的首字母大写
- capitalizeFirst: 将字符串的第一个字母大写
- isBool: 检查字符串是否可以转换为布尔值
- toBool: 将字符串转换为布尔值
- isNum: 检查字符串是否可以转换为数字
- toNum: 将字符串转换为数字
- isDouble: 检查字符串是否可以转换为双精度浮点数
- toDouble: 将字符串转换为双精度浮点数
- isInt: 检查字符串是否可以转换为整数
- toInt: 将字符串转换为整数
- toColor: 将十六进制颜色字符串转换为 Color 对象
- toMaterialColor: 将十六进制颜色字符串转换为 MaterialColor 对象
- removeAllWhitespace: 移除字符串中的所有空格
- hasMatch: 检查字符串是否匹配指定的正则表达式模式
- clearHtml: 移除字符串中的所有 HTML 标签
4.11 Text 文本
示例:
Text myText = Text('Hello, World!');
// 修改文本样式
myText = myText.textStyle(TextStyle(color: Colors.blue));
// 设置文本为粗体
myText = myText.bold();
// 设置文本为斜体
myText = myText.italic();
// 设置字体大小
myText = myText.fontSize(18);
// 设置字体系列
myText = myText.fontFamily('Roboto');
// 添加文本阴影
myText = myText.textShadow(color: Colors.grey, blurRadius: 2);
// 设置文本颜色
myText = myText.textColor(Colors.red);
// 设置文本对齐方式
myText = myText.textAlignment(TextAlign.center);
// 组合多个样式
myText = myText
.bold()
.fontSize(20)
.textColor(Colors.green)
.textAlignment(TextAlign.right);
扩展:
- copyWith: 创建一个新的 Text 对象,可选择性地修改其属性
- textStyle: 应用新的 TextStyle
- textScale: 设置文本缩放因子
- bold: 将文本设置为粗体
- italic: 将文本设置为斜体
- fontWeight: 设置文本的字体粗细
- fontSize: 设置文本的字体大小
- fontFamily: 设置文本的字体系列
- letterSpacing: 设置文本的字母间距
- wordSpacing: 设置文本的单词间距
- textShadow: 为文本添加阴影效果
- textElevation: 为文本添加立体效果
- textColor: 设置文本颜色
- textAlignment: 设置文本对齐方式
- textDirection: 设置文本方向
- textBaseline: 设置文本基线
- textWidthBasis: 设置文本宽度基准
4.12 TextSpan 文本
示例:
TextSpan myTextSpan = TextSpan(text: 'Hello, World!');
// 修改文本样式
myTextSpan = myTextSpan.textStyle(TextStyle(color: Colors.blue));
// 设置文本为粗体
myTextSpan = myTextSpan.bold();
// 设置文本为斜体
myTextSpan = myTextSpan.italic();
// 设置字体大小
myTextSpan = myTextSpan.fontSize(18);
// 设置字体系列
myTextSpan = myTextSpan.fontFamily('Roboto');
// 添加文本阴影
myTextSpan = myTextSpan.textShadow(color: Colors.grey, blurRadius: 2);
// 设置文本颜色
myTextSpan = myTextSpan.textColor(Colors.red);
// 组合多个样式
myTextSpan = myTextSpan
.bold()
.fontSize(20)
.textColor(Colors.green);
扩展:
- copyWith: 创建一个新的 TextSpan 对象,可选择性地修改其属性
- textStyle: 应用新的 TextStyle
- bold: 将文本设置为粗体
- italic: 将文本设置为斜体
- fontWeight: 设置文本的字体粗细
- fontSize: 设置文本的字体大小
- fontFamily: 设置文本的字体系列
- letterSpacing: 设置文本的字母间距
- wordSpacing: 设置文本的单词间距
- textShadow: 为文本添加阴影效果
- textElevation: 为文本添加立体效果
- textColor: 设置文本颜色
- textBaseline: 设置文本基线
代码
https://github.com/ducafecat/flutter_package_ducafe_ui_core
小结
本文详细介绍了 Flutter 组件库 ducafe_ui_core 的功能与优势,重点强调了设计稿适配、视图层打薄和多种工具类的应用。通过使用 ducafe_ui_core,开发者能够有效提升开发效率,简化 Flutter 应用的设计与开发过程。该组件库旨在为开发者提供全面的支持,让他们在构建高质量移动应用时更加得心应手。希望本文能为您在 Flutter 开发中提供实用的参考和指导。
感谢阅读本文
如果有什么建议,请在评论中让我知道。我很乐意改进。
猫哥 APP
flutter 学习路径
- Flutter 优秀插件推荐
- Flutter 基础篇1 - Dart 语言学习
- Flutter 基础篇2 - 快速上手
- Flutter 实战1 - Getx Woo 电商APP
- Flutter 实战2 - 上架指南 Apple Store、Google Play
- Flutter 基础篇3 - 仿微信朋友圈
- Flutter 实战3 - 腾讯即时通讯 第一篇
- Flutter 实战4 - 腾讯即时通讯 第二篇
© 猫哥 ducafecat.com
end