Files
flisp_app/lib/widgets/common.dart
2025-11-21 14:58:11 +08:00

275 lines
7.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:toggle_switch/toggle_switch.dart';
OutlineInputBorder buildFormBoard() {
return OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey.shade200),
);
}
OutlineInputBorder buildFormEnabledBoard() {
return OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey.shade200),
);
}
OutlineInputBorder buildFormFocusedBoard(ColorScheme colors) {
return OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: colors.primary),
);
}
// 卡片
class BuildCard extends StatelessWidget {
final Widget? child;
const BuildCard({super.key, this.child});
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return Container(
decoration: BoxDecoration(
color: colors.surfaceContainer,
borderRadius: BorderRadius.circular(12),
// border: Border.all(color: colors.outline.withAlpha(50), width: 1),
),
padding: const EdgeInsets.all(16),
child: child,
);
}
}
// 底部弹出
void buildModalBottom({
required BuildContext context,
required String title,
required Widget body,
}) {
final colors = Theme.of(context).colorScheme;
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (BuildContext context) {
return GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
color: Color.fromRGBO(0, 0, 0, 0.001),
child: GestureDetector(
onTap: () {},
child: DraggableScrollableSheet(
initialChildSize: 0.6,
minChildSize: 0.4,
maxChildSize: 0.9,
builder: (_, controller) {
return Container(
decoration: BoxDecoration(
color: colors.surfaceContainer,
border: Border.all(
color: colors.outline.withAlpha(50),
width: 1,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
),
child: buildModalBottomBody(context, title, body),
);
},
),
),
),
);
},
);
}
Widget buildModalBottomTitle(BuildContext context, String title) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
title,
style: TextStyle(
fontSize: 20,
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.w700,
),
),
],
),
);
}
Widget buildModalBottomBody(BuildContext context, String title, Widget body) {
return Column(
children: [
// 拖拽指示条
Container(
margin: EdgeInsets.only(top: 12, bottom: 4),
width: 48,
height: 4,
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(2),
),
),
buildModalBottomTitle(context, title),
Divider(height: 1, thickness: 1),
Expanded(child: Padding(padding: EdgeInsets.all(12), child: body)),
],
);
}
// 滑动删除
Widget buildDismissible({
required BuildContext context,
required Object id,
required Widget child,
required VoidCallback onDelete,
}) {
return Dismissible(
key: ValueKey(id),
direction: DismissDirection.endToStart,
background: _buildDismissBackground(),
confirmDismiss: (direction) async {
// 这里实现二次确认
return await showConfirmDialog(context, '确定要删除这个项目吗?');
},
onDismissed: (direction) {
onDelete();
},
child: child,
);
}
// 确认对话框
Future<bool?> showConfirmDialog(BuildContext context, String text) async {
final colors = Theme.of(context).colorScheme;
return showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('确认'),
backgroundColor: colors.surfaceContainer,
content: Text(text),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(
'取消',
style: TextStyle(color: colors.secondary.withAlpha(150)),
),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text('确认', style: TextStyle(color: colors.primary)),
),
],
);
},
);
}
Widget _buildDismissBackground() {
return Container(
color: Colors.red,
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
child: const Icon(Icons.delete, color: Colors.white, size: 24),
);
}
// toggle切换
Widget buildToggleSwitch<T extends Enum>({
required BuildContext context,
required T currentTab,
required List<T> tabValues,
required List<String> labels,
required ValueChanged<T> onTabChanged,
}) {
final colors = Theme.of(context).colorScheme;
return ToggleSwitch(
minWidth: 90.0,
minHeight: 40.0,
initialLabelIndex: tabValues.indexOf(currentTab),
totalSwitches: tabValues.length,
labels: labels,
activeBgColor: [colors.primary],
activeFgColor: Colors.white,
inactiveBgColor: colors.surfaceContainer,
inactiveFgColor: colors.onSurface,
cornerRadius: 12.0,
customTextStyles: [TextStyle(fontSize: 12, fontWeight: FontWeight.w500)],
onToggle: (index) {
if (index != null && index < tabValues.length) {
onTabChanged(tabValues[index]);
}
},
);
}
ButtonStyle buildSegmentStyle(ColorScheme colors) {
return ButtonStyle(
side: WidgetStateProperty.all<BorderSide>(
BorderSide(color: colors.surface, width: 0),
),
iconColor: WidgetStateProperty.resolveWith<Color>((
Set<WidgetState> states,
) {
if (states.contains(WidgetState.selected)) {
return colors.onPrimary;
}
return Colors.grey;
}),
backgroundColor: WidgetStateProperty.resolveWith<Color>((
Set<WidgetState> states,
) {
if (states.contains(WidgetState.selected)) {
return colors.primary;
}
return colors.surfaceContainer;
}),
foregroundColor: WidgetStateProperty.resolveWith<Color>((
Set<WidgetState> states,
) {
if (states.contains(WidgetState.selected)) {
return colors.onPrimary;
}
return colors.onSurface;
}),
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
);
}
Widget circleIconButton({
required IconData icon,
required VoidCallback onPressed,
required BuildContext context,
}) {
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
shape: CircleBorder(),
elevation: 0,
backgroundColor: Theme.of(context).colorScheme.primary,
padding: EdgeInsets.zero,
minimumSize: const Size(0, 0),
),
child: Icon(icon, color: Colors.white),
);
}