mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2024-09-06 12:06:57 +00:00
196 lines
6.3 KiB
Dart
196 lines
6.3 KiB
Dart
import 'package:auto_route/auto_route.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:ionicons/ionicons.dart';
|
|
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
|
|
import 'package:selfprivacy/ui/components/jobs_content/jobs_content.dart';
|
|
import 'package:selfprivacy/ui/components/drawers/support_drawer.dart';
|
|
import 'package:selfprivacy/ui/helpers/widget_size.dart';
|
|
import 'package:selfprivacy/utils/breakpoints.dart';
|
|
|
|
class BrandHeroScreen extends StatelessWidget {
|
|
const BrandHeroScreen({
|
|
required this.children,
|
|
super.key,
|
|
this.hasBackButton = true,
|
|
this.hasFlashButton = false,
|
|
this.heroIcon,
|
|
this.heroIconWidget,
|
|
this.heroTitle = '',
|
|
this.heroSubtitle,
|
|
this.onBackButtonPressed,
|
|
this.bodyPadding = const EdgeInsets.all(16.0),
|
|
this.ignoreBreakpoints = false,
|
|
this.hasSupportDrawer = false,
|
|
});
|
|
|
|
final List<Widget> children;
|
|
final bool hasBackButton;
|
|
final bool hasFlashButton;
|
|
final IconData? heroIcon;
|
|
final Widget? heroIconWidget;
|
|
final String heroTitle;
|
|
final String? heroSubtitle;
|
|
final VoidCallback? onBackButtonPressed;
|
|
final EdgeInsetsGeometry bodyPadding;
|
|
|
|
/// On non-mobile screens the buttons of the app bar are hidden.
|
|
/// This is because this widget implies that it is nested inside a bigger layout.
|
|
/// If it is not nested, set this to true.
|
|
final bool ignoreBreakpoints;
|
|
|
|
/// Usually support drawer is provided by the parent layout.
|
|
/// If it is not provided, set this to true.
|
|
final bool hasSupportDrawer;
|
|
|
|
@override
|
|
Widget build(final BuildContext context) {
|
|
final Widget heroIconWidget = this.heroIconWidget ??
|
|
Icon(
|
|
heroIcon ?? Icons.help,
|
|
size: 48.0,
|
|
color: Theme.of(context).colorScheme.onBackground,
|
|
);
|
|
final bool hasHeroIcon = heroIcon != null || this.heroIconWidget != null;
|
|
|
|
return Scaffold(
|
|
endDrawerEnableOpenDragGesture: false,
|
|
endDrawer: hasSupportDrawer ? const SupportDrawer() : null,
|
|
body: CustomScrollView(
|
|
slivers: [
|
|
HeroSliverAppBar(
|
|
heroTitle: heroTitle,
|
|
hasHeroIcon: hasHeroIcon,
|
|
hasBackButton: hasBackButton,
|
|
onBackButtonPressed: onBackButtonPressed,
|
|
heroIconWidget: heroIconWidget,
|
|
hasFlashButton: hasFlashButton,
|
|
ignoreBreakpoints: ignoreBreakpoints,
|
|
),
|
|
if (heroSubtitle != null)
|
|
SliverPadding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 16.0,
|
|
vertical: 4.0,
|
|
),
|
|
sliver: SliverList(
|
|
delegate: SliverChildListDelegate([
|
|
Text(
|
|
heroSubtitle!,
|
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
|
color: Theme.of(context).colorScheme.onBackground,
|
|
),
|
|
textAlign: hasHeroIcon ? TextAlign.center : TextAlign.start,
|
|
),
|
|
]),
|
|
),
|
|
),
|
|
SliverPadding(
|
|
padding: bodyPadding,
|
|
sliver: SliverList(
|
|
delegate: SliverChildListDelegate(children),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class HeroSliverAppBar extends StatefulWidget {
|
|
const HeroSliverAppBar({
|
|
required this.heroTitle,
|
|
required this.hasHeroIcon,
|
|
required this.hasBackButton,
|
|
required this.onBackButtonPressed,
|
|
required this.heroIconWidget,
|
|
required this.hasFlashButton,
|
|
required this.ignoreBreakpoints,
|
|
super.key,
|
|
});
|
|
|
|
final String heroTitle;
|
|
final bool hasHeroIcon;
|
|
final bool hasBackButton;
|
|
final bool hasFlashButton;
|
|
final VoidCallback? onBackButtonPressed;
|
|
final Widget heroIconWidget;
|
|
final bool ignoreBreakpoints;
|
|
|
|
@override
|
|
State<HeroSliverAppBar> createState() => _HeroSliverAppBarState();
|
|
}
|
|
|
|
class _HeroSliverAppBarState extends State<HeroSliverAppBar> {
|
|
Size _size = Size.zero;
|
|
@override
|
|
Widget build(final BuildContext context) {
|
|
final isMobile =
|
|
widget.ignoreBreakpoints ? true : Breakpoints.small.isActive(context);
|
|
final isJobsListEmpty = context.watch<JobsCubit>().state is JobsStateEmpty;
|
|
return SliverAppBar(
|
|
expandedHeight:
|
|
widget.hasHeroIcon ? 148.0 + _size.height : 72.0 + _size.height,
|
|
primary: true,
|
|
pinned: isMobile,
|
|
stretch: true,
|
|
surfaceTintColor: isMobile ? null : Colors.transparent,
|
|
leading: (widget.hasBackButton && isMobile)
|
|
? const AutoLeadingButton()
|
|
: const SizedBox.shrink(),
|
|
actions: [
|
|
if (widget.hasFlashButton && isMobile)
|
|
AnimatedSwitcher(
|
|
duration: const Duration(milliseconds: 300),
|
|
child: IconButton(
|
|
onPressed: () {
|
|
showModalBottomSheet(
|
|
context: context,
|
|
builder: (final BuildContext context) => const JobsContent(),
|
|
);
|
|
},
|
|
icon: Icon(
|
|
isJobsListEmpty ? Ionicons.flash_outline : Ionicons.flash,
|
|
),
|
|
color: isJobsListEmpty
|
|
? Theme.of(context).colorScheme.onBackground
|
|
: Theme.of(context).colorScheme.primary,
|
|
),
|
|
),
|
|
const SizedBox.shrink(),
|
|
],
|
|
flexibleSpace: FlexibleSpaceBar(
|
|
title: LayoutBuilder(
|
|
builder: (final context, final constraints) => SizedBox(
|
|
width: constraints.maxWidth - 72.0,
|
|
child: WidgetSize(
|
|
onChange: (final Size size) => setState(() => _size = size),
|
|
child: Text(
|
|
widget.heroTitle,
|
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
|
color: Theme.of(context).colorScheme.onSurface,
|
|
),
|
|
overflow: TextOverflow.fade,
|
|
textAlign: TextAlign.center,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
expandedTitleScale: 1.2,
|
|
centerTitle: true,
|
|
collapseMode: CollapseMode.pin,
|
|
titlePadding: const EdgeInsets.only(
|
|
bottom: 12.0,
|
|
top: 16.0,
|
|
),
|
|
background: Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
const SizedBox(height: 72.0),
|
|
if (widget.hasHeroIcon) widget.heroIconWidget,
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|