mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-08 17:11:14 +00:00
Merge pull request 'fix(ui): New app bar now properly supports long titles' (#132) from inex/selfprivacy.org.app:fix/app-bars into master
Reviewed-on: https://git.selfprivacy.org/kherel/selfprivacy.org.app/pulls/132 Reviewed-by: NaiJi ✨ <naiji@udongein.xyz>
This commit is contained in:
commit
c03e38ced1
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/ui/components/pre_styled_buttons/flash_fab.dart';
|
||||
import 'package:selfprivacy/ui/helpers/widget_size.dart';
|
||||
|
||||
class BrandHeroScreen extends StatelessWidget {
|
||||
const BrandHeroScreen({
|
||||
|
@ -32,44 +33,17 @@ class BrandHeroScreen extends StatelessWidget {
|
|||
color: Theme.of(context).colorScheme.onBackground,
|
||||
);
|
||||
final bool hasHeroIcon = heroIcon != null || this.heroIconWidget != null;
|
||||
const EdgeInsetsGeometry heroTitlePadding = EdgeInsets.only(
|
||||
bottom: 12.0,
|
||||
top: 16.0,
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
floatingActionButton: hasFlashButton ? const BrandFab() : null,
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
expandedHeight: hasHeroIcon ? 160.0 : 96.0,
|
||||
pinned: true,
|
||||
stretch: true,
|
||||
leading: hasBackButton
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: onBackButtonPressed ??
|
||||
() => Navigator.of(context).pop(),
|
||||
)
|
||||
: null,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
title: Text(
|
||||
heroTitle,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
expandedTitleScale: 1.2,
|
||||
centerTitle: true,
|
||||
collapseMode: CollapseMode.pin,
|
||||
titlePadding: heroTitlePadding,
|
||||
background: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (hasHeroIcon) heroIconWidget,
|
||||
],
|
||||
),
|
||||
),
|
||||
HeroSliverAppBar(
|
||||
heroTitle: heroTitle,
|
||||
hasHeroIcon: hasHeroIcon,
|
||||
hasBackButton: hasBackButton,
|
||||
onBackButtonPressed: onBackButtonPressed,
|
||||
heroIconWidget: heroIconWidget,
|
||||
),
|
||||
if (heroSubtitle != null)
|
||||
SliverPadding(
|
||||
|
@ -100,3 +74,74 @@ class BrandHeroScreen extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HeroSliverAppBar extends StatefulWidget {
|
||||
const HeroSliverAppBar({
|
||||
required this.heroTitle,
|
||||
required this.hasHeroIcon,
|
||||
required this.hasBackButton,
|
||||
required this.onBackButtonPressed,
|
||||
required this.heroIconWidget,
|
||||
final super.key,
|
||||
});
|
||||
|
||||
final String heroTitle;
|
||||
final bool hasHeroIcon;
|
||||
final bool hasBackButton;
|
||||
final VoidCallback? onBackButtonPressed;
|
||||
final Widget heroIconWidget;
|
||||
|
||||
@override
|
||||
State<HeroSliverAppBar> createState() => _HeroSliverAppBarState();
|
||||
}
|
||||
|
||||
class _HeroSliverAppBarState extends State<HeroSliverAppBar> {
|
||||
Size _size = Size.zero;
|
||||
@override
|
||||
Widget build(final BuildContext context) => SliverAppBar(
|
||||
expandedHeight:
|
||||
widget.hasHeroIcon ? 148.0 + _size.height : 72.0 + _size.height,
|
||||
primary: true,
|
||||
pinned: true,
|
||||
stretch: true,
|
||||
leading: widget.hasBackButton
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: widget.onBackButtonPressed ??
|
||||
() => Navigator.of(context).pop(),
|
||||
)
|
||||
: null,
|
||||
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,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
45
lib/ui/helpers/widget_size.dart
Normal file
45
lib/ui/helpers/widget_size.dart
Normal file
|
@ -0,0 +1,45 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
||||
class WidgetSize extends StatefulWidget {
|
||||
const WidgetSize({
|
||||
required this.onChange,
|
||||
required this.child,
|
||||
final super.key,
|
||||
});
|
||||
final Widget child;
|
||||
final Function onChange;
|
||||
|
||||
@override
|
||||
State<WidgetSize> createState() => _WidgetSizeState();
|
||||
}
|
||||
|
||||
class _WidgetSizeState extends State<WidgetSize> {
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
|
||||
return Container(
|
||||
key: widgetKey,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
|
||||
var widgetKey = GlobalKey();
|
||||
Size? oldSize;
|
||||
|
||||
void postFrameCallback(_) {
|
||||
final context = widgetKey.currentContext;
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
;
|
||||
|
||||
final newSize = context.size;
|
||||
if (oldSize == newSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
oldSize = newSize;
|
||||
widget.onChange(newSize);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue