mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-09 17:39:42 +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:flutter/material.dart';
|
||||||
import 'package:selfprivacy/ui/components/pre_styled_buttons/flash_fab.dart';
|
import 'package:selfprivacy/ui/components/pre_styled_buttons/flash_fab.dart';
|
||||||
|
import 'package:selfprivacy/ui/helpers/widget_size.dart';
|
||||||
|
|
||||||
class BrandHeroScreen extends StatelessWidget {
|
class BrandHeroScreen extends StatelessWidget {
|
||||||
const BrandHeroScreen({
|
const BrandHeroScreen({
|
||||||
|
@ -32,44 +33,17 @@ class BrandHeroScreen extends StatelessWidget {
|
||||||
color: Theme.of(context).colorScheme.onBackground,
|
color: Theme.of(context).colorScheme.onBackground,
|
||||||
);
|
);
|
||||||
final bool hasHeroIcon = heroIcon != null || this.heroIconWidget != null;
|
final bool hasHeroIcon = heroIcon != null || this.heroIconWidget != null;
|
||||||
const EdgeInsetsGeometry heroTitlePadding = EdgeInsets.only(
|
|
||||||
bottom: 12.0,
|
|
||||||
top: 16.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
floatingActionButton: hasFlashButton ? const BrandFab() : null,
|
floatingActionButton: hasFlashButton ? const BrandFab() : null,
|
||||||
body: CustomScrollView(
|
body: CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverAppBar(
|
HeroSliverAppBar(
|
||||||
expandedHeight: hasHeroIcon ? 160.0 : 96.0,
|
heroTitle: heroTitle,
|
||||||
pinned: true,
|
hasHeroIcon: hasHeroIcon,
|
||||||
stretch: true,
|
hasBackButton: hasBackButton,
|
||||||
leading: hasBackButton
|
onBackButtonPressed: onBackButtonPressed,
|
||||||
? IconButton(
|
heroIconWidget: heroIconWidget,
|
||||||
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,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (heroSubtitle != null)
|
if (heroSubtitle != null)
|
||||||
SliverPadding(
|
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