105 lines
3.1 KiB
Dart
105 lines
3.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../design/tokens.dart';
|
|
|
|
/// هيكل التحميل — تأثير نبض على عناصر مستوحاة من الشاشة الفعلية.
|
|
/// docs/26 §7
|
|
class Skeleton extends StatelessWidget {
|
|
const Skeleton({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final cs = Theme.of(context).colorScheme;
|
|
final baseColor = cs.surfaceContainerHighest;
|
|
final highlightColor = cs.surfaceContainerHigh;
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.all(T.pagePadding),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_PulseBox(width: double.infinity, height: 200, base: baseColor, highlight: highlightColor),
|
|
const SizedBox(height: T.s16),
|
|
_PulseBox(width: 200, height: 20, base: baseColor, highlight: highlightColor),
|
|
const SizedBox(height: T.s12),
|
|
_PulseBox(width: 140, height: 16, base: baseColor, highlight: highlightColor),
|
|
const SizedBox(height: T.s24),
|
|
Row(
|
|
children: [
|
|
_PulseBox(width: 48, height: 48, base: baseColor, highlight: highlightColor, radius: T.rCircle),
|
|
const SizedBox(width: T.s12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_PulseBox(width: double.infinity, height: 16, base: baseColor, highlight: highlightColor),
|
|
const SizedBox(height: T.s8),
|
|
_PulseBox(width: 120, height: 14, base: baseColor, highlight: highlightColor),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _PulseBox extends StatefulWidget {
|
|
const _PulseBox({
|
|
required this.width,
|
|
required this.height,
|
|
required this.base,
|
|
required this.highlight,
|
|
this.radius = 8,
|
|
});
|
|
|
|
final double width;
|
|
final double height;
|
|
final Color base;
|
|
final Color highlight;
|
|
final double radius;
|
|
|
|
@override
|
|
State<_PulseBox> createState() => _PulseBoxState();
|
|
}
|
|
|
|
class _PulseBoxState extends State<_PulseBox>
|
|
with SingleTickerProviderStateMixin {
|
|
late final AnimationController _ctrl;
|
|
late final Animation<double> _anim;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 1200))
|
|
..repeat();
|
|
_anim = Tween<double>(begin: 0, end: 1).animate(
|
|
CurvedAnimation(parent: _ctrl, curve: Curves.easeInOut),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_ctrl.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AnimatedBuilder(
|
|
animation: _anim,
|
|
builder: (_, __) {
|
|
return Container(
|
|
width: widget.width,
|
|
height: widget.height,
|
|
decoration: BoxDecoration(
|
|
color: Color.lerp(widget.base, widget.highlight, _anim.value),
|
|
borderRadius: BorderRadius.circular(widget.radius),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|