创建完整的 Flutter 项目结构,包含 Android 和 iOS 平台配置、核心网络层、路由、状态管理及业务模块(订单、收益、认证),适配原 uni-app 项目功能逻辑
144 lines
4.4 KiB
Dart
144 lines
4.4 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../../core/app_keys.dart';
|
|
import '../../core/theme/app_colors.dart';
|
|
import '../../core/theme/app_dimens.dart';
|
|
import '../../core/theme/app_typography.dart';
|
|
|
|
/// `uni.showToast({ title, icon })` 的等价实现。
|
|
/// 不依赖调用方的 BuildContext,可在任意异步回调里安全使用。
|
|
void showAppToast(String title, {bool success = false}) {
|
|
final messenger = AppKeys.scaffoldMessenger.currentState;
|
|
if (messenger == null) return;
|
|
messenger
|
|
..clearSnackBars()
|
|
..showSnackBar(
|
|
SnackBar(
|
|
content: Row(
|
|
children: [
|
|
if (success) ...[
|
|
const Icon(
|
|
Icons.check_circle,
|
|
size: 18,
|
|
color: AppColors.success,
|
|
),
|
|
const SizedBox(width: 8),
|
|
],
|
|
Expanded(
|
|
child: Text(
|
|
title,
|
|
style: AppTypography.bodyMd.copyWith(color: AppColors.surface),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
backgroundColor: AppColors.inverseSurface,
|
|
behavior: SnackBarBehavior.floating,
|
|
margin: const EdgeInsets.all(AppSpacing.md),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(AppRadius.lg),
|
|
),
|
|
duration: Duration(milliseconds: success ? 1500 : 2200),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// `uni.showLoading({ title })` 的等价实现,返回关闭函数。
|
|
VoidCallback showAppLoading([String title = '加载中...']) {
|
|
final navigator = AppKeys.rootNavigator.currentState;
|
|
if (navigator == null) return () {};
|
|
var closed = false;
|
|
showDialog<void>(
|
|
context: navigator.context,
|
|
barrierDismissible: false,
|
|
builder: (_) => PopScope(
|
|
canPop: false,
|
|
child: Center(
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xCC000000),
|
|
borderRadius: BorderRadius.circular(AppRadius.lg),
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const SizedBox(
|
|
width: 26,
|
|
height: 26,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 2.4,
|
|
valueColor: AlwaysStoppedAnimation(Colors.white),
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
title,
|
|
style: AppTypography.bodyMd.copyWith(color: Colors.white),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
return () {
|
|
if (closed) return;
|
|
closed = true;
|
|
final current = AppKeys.rootNavigator.currentState;
|
|
if (current != null && current.canPop()) current.pop();
|
|
};
|
|
}
|
|
|
|
/// `uni.showModal({ title, content })` 的等价实现,返回用户是否点了确认。
|
|
Future<bool> showAppConfirm({
|
|
required String title,
|
|
required String content,
|
|
String confirmText = '确定',
|
|
String cancelText = '取消',
|
|
bool destructive = false,
|
|
}) async {
|
|
final navigator = AppKeys.rootNavigator.currentState;
|
|
if (navigator == null) return false;
|
|
final result = await showDialog<bool>(
|
|
context: navigator.context,
|
|
builder: (ctx) => AlertDialog(
|
|
backgroundColor: AppColors.surfaceContainerLowest,
|
|
surfaceTintColor: Colors.transparent,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(AppRadius.xl),
|
|
),
|
|
title: Text(
|
|
title,
|
|
textAlign: TextAlign.center,
|
|
style: AppTypography.bodyLg.copyWith(fontWeight: FontWeight.w700),
|
|
),
|
|
content: Text(
|
|
content,
|
|
textAlign: TextAlign.center,
|
|
style: AppTypography.bodyMd.copyWith(color: AppColors.textMuted),
|
|
),
|
|
actionsAlignment: MainAxisAlignment.spaceEvenly,
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(ctx).pop(false),
|
|
child: Text(
|
|
cancelText,
|
|
style: AppTypography.bodyLg.copyWith(color: AppColors.textMuted),
|
|
),
|
|
),
|
|
TextButton(
|
|
onPressed: () => Navigator.of(ctx).pop(true),
|
|
child: Text(
|
|
confirmText,
|
|
style: AppTypography.bodyLg.copyWith(
|
|
color: destructive ? AppColors.tertiaryContainer : AppColors.primary,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
return result ?? false;
|
|
}
|