Files
smart_grid_iot_app_flutter/lib/shared/widgets/x_common.dart
T
andyesfly 56830e94ec feat: 完成智能电网电工端 Flutter 应用初始化
创建完整的 Flutter 项目结构,包含 Android 和 iOS 平台配置、核心网络层、路由、状态管理及业务模块(订单、收益、认证),适配原 uni-app 项目功能逻辑
2026-10-06 11:54:10 +08:00

191 lines
5.0 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_dimens.dart';
import '../../core/theme/app_typography.dart';
/// 页面骨架:固定顶栏 + 内容区。对应源「根容器固定、滚动只发生在内容区」的布局。
class XPageShell extends StatelessWidget {
const XPageShell({
super.key,
required this.topBar,
required this.child,
this.backgroundColor = AppColors.bgPage,
});
final Widget topBar;
final Widget child;
final Color backgroundColor;
@override
Widget build(BuildContext context) {
return Container(
color: backgroundColor,
child: Column(
children: [topBar, Expanded(child: child)],
),
);
}
}
/// 页面大标题 + 副标题,对应 `.page-header` / `.section-title`(28px/w700)。
class XPageTitle extends StatelessWidget {
const XPageTitle({super.key, required this.title, this.subtitle});
final String title;
final String? subtitle;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: AppTypography.navLargeTitle.copyWith(
color: AppColors.onSurface,
),
),
if (subtitle != null) ...[
const SizedBox(height: 4),
Text(
subtitle!,
style: AppTypography.bodyMd.copyWith(
color: AppColors.onSurfaceVariant,
),
),
],
],
);
}
}
/// 空态 / 加载中占位。
class XPlaceholder extends StatelessWidget {
const XPlaceholder({
super.key,
required this.text,
this.padding = const EdgeInsets.symmetric(vertical: 60),
});
final String text;
final EdgeInsetsGeometry padding;
@override
Widget build(BuildContext context) {
return Padding(
padding: padding,
child: Center(
child: Text(
text,
textAlign: TextAlign.center,
style: AppTypography.bodyMd.copyWith(color: AppColors.textMuted),
),
),
);
}
}
/// 实心主按钮,对应 `.login-btn` / `.start-btn` 等。
class XPrimaryButton extends StatelessWidget {
const XPrimaryButton({
super.key,
required this.label,
required this.onPressed,
this.leading,
this.loading = false,
this.height = 48,
this.radius = AppRadius.lg,
this.background = AppColors.primary,
this.foreground = AppColors.onPrimary,
this.fontSize = 17,
this.shadows,
});
final String label;
final VoidCallback? onPressed;
final Widget? leading;
final bool loading;
final double height;
final double radius;
final Color background;
final Color foreground;
final double fontSize;
final List<BoxShadow>? shadows;
@override
Widget build(BuildContext context) {
final disabled = onPressed == null || loading;
return Opacity(
opacity: disabled && !loading ? 0.55 : 1,
child: SizedBox(
height: height,
width: double.infinity,
child: Material(
color: background,
borderRadius: BorderRadius.circular(radius),
child: InkWell(
onTap: disabled ? null : onPressed,
borderRadius: BorderRadius.circular(radius),
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(radius),
boxShadow: shadows,
),
child: loading
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation(foreground),
),
)
: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (leading != null) ...[
leading!,
const SizedBox(width: 8),
],
Text(
label,
style: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.w600,
color: foreground,
),
),
],
),
),
),
),
),
);
}
}
/// 区块小标题(20/w600),对应 `.chart-title` / `.section-title`。
class XSectionTitle extends StatelessWidget {
const XSectionTitle(this.text, {super.key, this.trailing});
final String text;
final Widget? trailing;
@override
Widget build(BuildContext context) {
final title = Text(
text,
style: AppTypography.headlineMd.copyWith(color: AppColors.onSurface),
);
if (trailing == null) return title;
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [title, trailing!],
);
}
}