Files
swift_companion/swiftyCompanion/swifty_companion/lib/theme/swifty_ui.dart
2026-09-30 08:24:20 +02:00

110 lines
2.7 KiB
Dart

import 'package:flutter/material.dart';
/// Constantes UI partagées (étape 5 — layout).
abstract final class SwiftyUi {
static const Color brand = Color(0xFF00BABC);
/// Largeur max du contenu sur tablette / paysage.
static const double contentMaxWidth = 960;
/// Largeur max vue recherche (formulaire centré).
static const double searchMaxWidth = 480;
static const double wideBreakpoint = 720;
static EdgeInsets pagePadding(BoxConstraints constraints) {
final horizontal = constraints.maxWidth >= 600 ? 32.0 : 16.0;
return EdgeInsets.fromLTRB(horizontal, 24, horizontal, 24);
}
static PreferredSizeWidget appBar({
required String title,
List<Widget>? actions,
bool showBack = false,
}) {
return AppBar(
title: Text(title),
backgroundColor: brand,
foregroundColor: Colors.black,
automaticallyImplyLeading: showBack,
actions: actions,
);
}
}
/// Corps scrollable centré avec largeur max (clavier + petits écrans).
class ResponsiveScrollBody extends StatelessWidget {
const ResponsiveScrollBody({
super.key,
required this.child,
this.maxWidth = SwiftyUi.contentMaxWidth,
this.center = true,
});
final Widget child;
final double maxWidth;
final bool center;
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final content = ConstrainedBox(
constraints: BoxConstraints(maxWidth: maxWidth),
child: child,
);
return SingleChildScrollView(
padding: SwiftyUi.pagePadding(constraints),
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
child: center
? Align(alignment: Alignment.topCenter, child: content)
: content,
);
},
);
}
}
class ErrorMessage extends StatelessWidget {
const ErrorMessage({super.key, required this.message});
final String message;
@override
Widget build(BuildContext context) {
return SelectableText(
message,
textAlign: TextAlign.center,
style: TextStyle(color: Theme.of(context).colorScheme.error),
);
}
}
class LoadingFilledButton extends StatelessWidget {
const LoadingFilledButton({
super.key,
required this.label,
required this.loading,
required this.onPressed,
});
final String label;
final bool loading;
final VoidCallback? onPressed;
@override
Widget build(BuildContext context) {
return FilledButton(
onPressed: loading ? null : onPressed,
child: loading
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Text(label),
);
}
}