This commit is contained in:
2025-09-26 18:48:14 +07:00
parent 382a0e7909
commit 30ed6b39b5
85 changed files with 20722 additions and 112 deletions

View File

@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
const CustomAppBar({
super.key,
required this.title,
this.actions,
this.leading,
this.backgroundColor,
this.foregroundColor,
this.elevation = 0,
});
final String title;
final List<Widget>? actions;
final Widget? leading;
final Color? backgroundColor;
final Color? foregroundColor;
final double elevation;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return AppBar(
title: Text(
title,
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w600,
),
),
leading: leading,
actions: actions,
elevation: elevation,
backgroundColor: backgroundColor ?? colorScheme.surfaceVariant,
foregroundColor: foregroundColor ?? colorScheme.onSurfaceVariant,
centerTitle: true,
);
}
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}

View File

@@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
class EmptyStateWidget extends StatelessWidget {
const EmptyStateWidget({
super.key,
required this.icon,
required this.title,
required this.subtitle,
this.actionButton,
this.iconSize = 64.0,
});
final IconData icon;
final String title;
final String subtitle;
final Widget? actionButton;
final double iconSize;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return Center(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
size: iconSize,
color: colorScheme.onSurfaceVariant.withOpacity(0.6),
),
const SizedBox(height: 24),
Text(
title,
style: theme.textTheme.headlineSmall?.copyWith(
color: colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w600,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
subtitle,
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant.withOpacity(0.7),
),
textAlign: TextAlign.center,
),
if (actionButton != null) ...[
const SizedBox(height: 32),
actionButton!,
],
],
),
),
);
}
}

View File

@@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
class LoadingWidget extends StatelessWidget {
const LoadingWidget({
super.key,
this.message,
this.size = 24.0,
});
final String? message;
final double size;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: size,
height: size,
child: CircularProgressIndicator(
strokeWidth: 2.0,
color: colorScheme.primary,
),
),
if (message != null) ...[
const SizedBox(height: 16),
Text(
message!,
style: theme.textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurfaceVariant,
),
textAlign: TextAlign.center,
),
],
],
),
);
}
}
class LoadingOverlay extends StatelessWidget {
const LoadingOverlay({
super.key,
required this.isLoading,
required this.child,
this.message,
});
final bool isLoading;
final Widget child;
final String? message;
@override
Widget build(BuildContext context) {
return Stack(
children: [
child,
if (isLoading)
Container(
color: Colors.black.withOpacity(0.3),
child: LoadingWidget(message: message),
),
],
);
}
}