feat: 完成智能电网电工端 Flutter 应用初始化

创建完整的 Flutter 项目结构,包含 Android 和 iOS 平台配置、核心网络层、路由、状态管理及业务模块(订单、收益、认证),适配原 uni-app 项目功能逻辑
This commit is contained in:
andyesfly committed 2026-10-06 11:54:10 +08:00
commit 56830e94ec
108 files changed
+9272

No files matched your search

+190
View File
@@ -0,0 +1,190 @@
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!],
);
}
}