flutter架 构项目基础

This commit is contained in:
崔宝新
2026-09-04 15:22:20 +08:00
parent 70967229d0
commit 0c5aee59c0
265 changed files with 35484 additions and 0 deletions
+187
View File
@@ -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),
),
),
],
],
),
),
),
);
}
}