feat(proj): init

This commit is contained in:
vl.arkhangelskii
2026-09-21 04:06:43 +03:00
commit c956b94983
1076 changed files with 50876 additions and 0 deletions
@@ -0,0 +1,87 @@
import 'package:flutter/cupertino.dart';
import 'package:please_pay_me/theme/tokens.dart';
import 'package:please_pay_me/ui/atoms/app_button.dart';
import 'package:please_pay_me/ui/atoms/app_icon.dart';
import 'package:please_pay_me/ui/atoms/app_text.dart';
import 'package:please_pay_me/ui/feedback/app_progress.dart';
/// Centered placeholder for empty collections.
class AppEmptyState extends StatelessWidget {
const AppEmptyState({
super.key,
required this.title,
this.message,
this.icon = CupertinoIcons.tray,
this.actionLabel,
this.onAction,
});
final String title;
final String? message;
final IconData icon;
final String? actionLabel;
final VoidCallback? onAction;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.s6,
vertical: AppSpacing.s7,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
AppIcon(icon, size: 44, color: AppColors.tertiaryLabel),
const SizedBox(height: AppSpacing.s3),
AppText.headline(title, textAlign: TextAlign.center),
if (message != null) ...[
const SizedBox(height: AppSpacing.s1),
AppText.subhead(message!, textAlign: TextAlign.center),
],
if (actionLabel != null && onAction != null) ...[
const SizedBox(height: AppSpacing.s4),
AppButton(
label: actionLabel!,
size: AppButtonSize.medium,
style: AppButtonStyle.tinted,
expanded: false,
onPressed: onAction,
),
],
],
),
);
}
}
/// Failure placeholder with a retry affordance.
class AppErrorView extends StatelessWidget {
const AppErrorView({super.key, required this.message, this.onRetry});
final String message;
final VoidCallback? onRetry;
@override
Widget build(BuildContext context) {
return AppEmptyState(
icon: CupertinoIcons.exclamationmark_triangle,
title: 'Не получилось загрузить',
message: message,
actionLabel: onRetry == null ? null : 'Повторить',
onAction: onRetry,
);
}
}
class AppLoadingView extends StatelessWidget {
const AppLoadingView({super.key});
@override
Widget build(BuildContext context) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: AppSpacing.s7),
child: Center(child: AppSpinner()),
);
}
}