flutter架 构项目基础
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// Wrap 自动换行布局演示页
|
||||
///
|
||||
/// 一、两种使用方式:
|
||||
/// 1. 直接运行本页面:runApp(const MaterialApp(home: WrapDemoPage()))
|
||||
/// 2. 复制 body 核心代码到任意 Scaffold 的 body 中(见下方)。
|
||||
///
|
||||
/// 二、作用:
|
||||
/// Row 一行排不下就直接溢出报错;Wrap 空间不够时会【自动换行】,
|
||||
/// 标签流、搜索历史标签这类数量不固定的场景必用。
|
||||
///
|
||||
/// 三、核心属性:
|
||||
/// direction : 排布方向。Axis.horizontal 水平排布(默认);Axis.vertical 垂直排布
|
||||
/// spacing : 同一行内 item 之间的水平间距
|
||||
/// runSpacing: 行与行之间的垂直间距(run 是"行/批"的意思)
|
||||
///
|
||||
/// 四、对比记忆:
|
||||
/// Row : 横向排列,溢出直接报错,不会换行
|
||||
/// Wrap : 横向排列,空间不足自动换行
|
||||
/// ============================================================
|
||||
class WrapDemoPage extends StatelessWidget {
|
||||
const WrapDemoPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Wrap 标签流 Demo')),
|
||||
// ---------- 下方即为核心代码,可直接复制到 Scaffold body ----------
|
||||
body: Padding(
|
||||
padding: EdgeInsets.all(16.w), // 不写const,每次build会新建对象,产生不必要开销
|
||||
child: Wrap(
|
||||
spacing: 24, // 同一行 item 水平间距
|
||||
runSpacing: 20, // 上下两行之间的距离
|
||||
children: [
|
||||
// ---- 最简单的 Chip:只有 label(必填)----
|
||||
const Chip(label: Text("Vue")),
|
||||
|
||||
// ---- 带头像 + 边框 + 圆角胶囊形状 ----
|
||||
Chip(
|
||||
label: const Text("React"),
|
||||
avatar: Icon(Icons.tag, size: 18.w), // 标签前面的小图标
|
||||
backgroundColor: const Color(0xFFE3F2FD), // 背景色
|
||||
labelStyle: TextStyle(color: Colors.black, fontSize: 14.sp), // 文字样式
|
||||
side: const BorderSide(color: Color(0xFF64B5F6)), // 边框
|
||||
shape: const StadiumBorder(), // 全圆角胶囊形状
|
||||
labelPadding: EdgeInsets.symmetric(horizontal: 6.w), // 文字内边距
|
||||
),
|
||||
|
||||
// ---- 可删除的 Chip:右上角出小叉号,点击触发 onDeleted ----
|
||||
Chip(
|
||||
label: const Text("Flutter"),
|
||||
avatar: Icon(Icons.rocket_launch, size: 18.w),
|
||||
deleteIcon: Icon(Icons.close, size: 16.w), // 自定义叉号图标
|
||||
backgroundColor: Colors.blue[100],
|
||||
labelStyle: const TextStyle(color: Colors.black),
|
||||
shape: StadiumBorder(),
|
||||
onDeleted: () => print("删除 Flutter"), // 点击叉号回调
|
||||
),
|
||||
|
||||
// ---- 可点击的 Chip:点击整个标签触发 onPressed ----
|
||||
// Flutter 3.x 注意:Chip 已移除 onPressed,可点击请用 ActionChip
|
||||
ActionChip(
|
||||
label: const Text("UniApp"),
|
||||
avatar: Icon(Icons.smartphone, size: 18.w),
|
||||
backgroundColor: const Color(0xFFFCE4EC),
|
||||
elevation: 2, // 阴影高度
|
||||
shadowColor: Colors.pink.shade100, // 阴影颜色
|
||||
padding: EdgeInsets.all(6.w), // Chip 整体内边距
|
||||
onPressed: () => print("点击 UniApp"), // 点击标签回调
|
||||
),
|
||||
|
||||
// ---- 选中态 Chip:selected 为 true 时显示对勾 ----
|
||||
// Flutter 3.x 注意:Chip 已移除 selected,选中态请用 FilterChip
|
||||
FilterChip(
|
||||
label: const Text("JavaScript"),
|
||||
selected: true, // 选中态
|
||||
selectedColor: const Color(0xFF4CAF50), // 选中时的背景色
|
||||
checkmarkColor: Colors.white, // 对勾颜色
|
||||
labelStyle: const TextStyle(color: Colors.white),
|
||||
shape: const StadiumBorder(),
|
||||
onSelected: (_) {}, // FilterChip 必填回调:选中状态变化时触发
|
||||
),
|
||||
|
||||
// ---- 深色背景 + 自定义删除图标颜色 ----
|
||||
Chip(
|
||||
label: const Text("Dart语言"),
|
||||
avatar: Icon(Icons.code, size: 18.w, color: Colors.white),
|
||||
backgroundColor: const Color(0xFF263238), // 深色背景
|
||||
deleteIcon: Icon(Icons.cancel, size: 16.w, color: Colors.white70),
|
||||
labelStyle: const TextStyle(color: Colors.white),
|
||||
shape: const StadiumBorder(),
|
||||
onDeleted: () => print("删除 Dart语言"),
|
||||
),
|
||||
|
||||
// ---- 使用 Material 3 默认样式的 Chip ----
|
||||
const Chip(label: Text("前端开发")),
|
||||
|
||||
// ===== 分割线:下面是像素级可控的自绘标签 CustomTag =====
|
||||
Divider(height: 32.h),
|
||||
|
||||
const CustomTag(
|
||||
label: "精准UI",
|
||||
icon: Icons.brush,
|
||||
iconColor: Color(0xFF3B82F6),
|
||||
onDeleted: null, // 无删除按钮的纯标签
|
||||
),
|
||||
CustomTag(
|
||||
label: "可删除标签",
|
||||
icon: Icons.rocket_launch,
|
||||
onDeleted: () => print("删除 可删除标签"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 像素级可控的自绘标签:高度/间距/圆角/边框/点击反馈全手写
|
||||
/// 比标准 Chip 灵活,适合设计稿要求精确(固定高度、精确间距)的场景
|
||||
class CustomTag extends StatelessWidget {
|
||||
const CustomTag({
|
||||
super.key,
|
||||
required this.label,
|
||||
this.icon,
|
||||
this.iconColor = const Color(0xFF3B82F6),
|
||||
this.onDeleted,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final IconData? icon; // 可选的标签前图标
|
||||
final Color iconColor;
|
||||
final VoidCallback? onDeleted; // 点击叉号回调;传 null 则不显示叉号
|
||||
final VoidCallback? onTap; // 点击整个标签回调
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Colors.transparent, // 消除默认白色背景
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(4.r), // 涟漪形状跟随圆角
|
||||
child: Container(
|
||||
height: 24.h, // 精确高度
|
||||
padding: EdgeInsets.only(left: 6.w, right: 6.w),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFEDF2FF),
|
||||
borderRadius: BorderRadius.circular(4.r),
|
||||
border: Border.all(color: const Color(0xFFD6E4FF)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (icon != null) ...[
|
||||
Icon(icon, size: 12.w, color: iconColor),
|
||||
SizedBox(width: 4.w), // 图标与文字精确 4px
|
||||
],
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Color(0xFF333333),
|
||||
height: 1, // 去掉文字默认行高,避免撑高容器
|
||||
),
|
||||
),
|
||||
if (onDeleted != null) ...[
|
||||
SizedBox(width: 2.w), // 文字与叉号精确 2px
|
||||
GestureDetector(
|
||||
onTap: onDeleted,
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
size: 12.w,
|
||||
color: Color(0xFF999999),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user