Skip to content

Instantly share code, notes, and snippets.

@knaeckeKami
Created August 14, 2023 10:40
Show Gist options
  • Save knaeckeKami/0c07ec62c56e8f6cdae4489e36a38692 to your computer and use it in GitHub Desktop.
Save knaeckeKami/0c07ec62c56e8f6cdae4489e36a38692 to your computer and use it in GitHub Desktop.
silent-ritual-3131
import 'package:flutter/material.dart';
import 'dart:math' as math;
import 'dart:ui' as ui;
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
useMaterial3: true,
),
scrollBehavior: MaterialScrollBehavior().copyWith(
dragDevices: {
ui.PointerDeviceKind.mouse,
ui.PointerDeviceKind.touch,
ui.PointerDeviceKind.stylus,
ui.PointerDeviceKind.unknown
},
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: CustomScrollView(slivers: [
SliverAppBar(
stretch: true,
pinned: true,
flexibleSpace: MyFlexibleSpaceBar(
stretchModes: <StretchMode>[StretchMode.fadeTitle],
centerTitle: false,
titlePaddingTween: EdgeInsetsTween(
begin: const EdgeInsets.only(left: 16.0, bottom: 16),
end: const EdgeInsets.only(left: 72.0, bottom: 8)),
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("title"),
Text("subtitle",
style: Theme.of(context).primaryTextTheme.titleSmall)
],
),
),
leading: IconButton(icon: Icon(Icons.close), onPressed: () {}),
expandedHeight: 200,
collapsedHeight: 62,
actions: [
IconButton(icon: Icon(Icons.list), onPressed: () {}),
IconButton(icon: Icon(Icons.pause), onPressed: () {}),
IconButton(icon: Icon(Icons.stop), onPressed: () {}),
]),
SliverList(
delegate: SliverChildListDelegate(
List.generate(
100,
(i) => ListTile(
title: Text(i.toString()),
),
),
),
)
]),
),
);
}
}
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
'Hello, World!',
style: Theme.of(context).textTheme.headlineMedium,
);
}
}
class MyFlexibleSpaceBar extends StatefulWidget {
/// Creates a flexible space bar.
///
/// Most commonly used in the [AppBar.flexibleSpace] field.
const MyFlexibleSpaceBar({
Key? key,
required this.title,
this.foreground,
this.background,
required this.centerTitle,
required this.titlePaddingTween,
this.collapseMode = CollapseMode.parallax,
this.stretchModes = const <StretchMode>[StretchMode.zoomBackground],
}) : assert(collapseMode != null),
super(key: key);
/// The primary contents of the flexible space bar when expanded.
///
/// Typically a [Text] widget.
final Widget title;
final Widget? foreground;
/// Shown behind the [title] when expanded.
///
/// Typically an [Image] widget with [Image.fit] set to [BoxFit.cover].
final Widget? background;
/// Whether the title should be centered.
///
/// By default this property is true if the current target platform
/// is [TargetPlatform.iOS] or [TargetPlatform.macOS], false otherwise.
final bool centerTitle;
/// Collapse effect while scrolling.
///
/// Defaults to [CollapseMode.parallax].
final CollapseMode collapseMode;
/// Stretch effect while over-scrolling.
///
/// Defaults to include [StretchMode.zoomBackground].
final List<StretchMode> stretchModes;
final EdgeInsetsTween titlePaddingTween;
@override
_MyFlexibleSpaceBarState createState() => _MyFlexibleSpaceBarState();
}
class _MyFlexibleSpaceBarState extends State<MyFlexibleSpaceBar> {
bool _getEffectiveCenterTitle(ThemeData theme) {
if (widget.centerTitle != null) return widget.centerTitle;
assert(theme.platform != null);
switch (theme.platform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
return false;
case TargetPlatform.iOS:
case TargetPlatform.macOS:
return true;
}
}
Alignment _getTitleAlignment(bool effectiveCenterTitle) {
if (effectiveCenterTitle) return Alignment.bottomCenter;
final TextDirection textDirection = Directionality.of(context);
assert(textDirection != null);
switch (textDirection) {
case TextDirection.rtl:
return Alignment.bottomRight;
case TextDirection.ltr:
return Alignment.bottomLeft;
}
}
double _getCollapsePadding(double t, FlexibleSpaceBarSettings settings) {
switch (widget.collapseMode) {
case CollapseMode.pin:
return -(settings.maxExtent - settings.currentExtent);
case CollapseMode.none:
return 0.0;
case CollapseMode.parallax:
final double deltaExtent = settings.maxExtent - settings.minExtent;
return -Tween<double>(begin: 0.0, end: deltaExtent / 4.0).transform(t);
}
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final FlexibleSpaceBarSettings settings = context
.dependOnInheritedWidgetOfExactType<FlexibleSpaceBarSettings>()!;
final List<Widget> children = <Widget>[];
final double deltaExtent = settings.maxExtent - settings.minExtent;
// 0.0 -> Expanded
// 1.0 -> Collapsed to toolbar
final double t =
(1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent)
.clamp(0.0, 1.0);
// background
if (widget.background != null) {
final double fadeStart =
math.max(0.0, 1.0 - kToolbarHeight / deltaExtent);
const double fadeEnd = 1.0;
assert(fadeStart <= fadeEnd);
final double opacity = 1.0 - Interval(fadeStart, fadeEnd).transform(t);
double height = settings.maxExtent;
// StretchMode.zoomBackground
if (widget.stretchModes.contains(StretchMode.zoomBackground) &&
constraints.maxHeight > height) {
height = constraints.maxHeight;
}
children.add(Positioned(
top: _getCollapsePadding(t, settings),
left: 0.0,
right: 0.0,
height: height,
child: Opacity(
// IOS is relying on this semantics node to correctly traverse
// through the app bar when it is collapsed.
alwaysIncludeSemantics: true,
opacity: opacity,
child: widget.background,
),
));
// StretchMode.blurBackground
if (widget.stretchModes.contains(StretchMode.blurBackground) &&
constraints.maxHeight > settings.maxExtent) {
final double blurAmount =
(constraints.maxHeight - settings.maxExtent) / 10;
children.add(Positioned.fill(
child: BackdropFilter(
child: Container(
color: Colors.transparent,
),
filter: ui.ImageFilter.blur(
sigmaX: blurAmount,
sigmaY: blurAmount,
))));
}
}
// title
if (widget.title != null) {
final ThemeData theme = Theme.of(context);
Widget title;
switch (theme.platform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
title = widget.title;
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
title = Semantics(
namesRoute: true,
child: widget.title,
);
break;
}
// StretchMode.fadeTitle
if (widget.stretchModes.contains(StretchMode.fadeTitle) &&
constraints.maxHeight > settings.maxExtent) {
final double stretchOpacity = 1 -
(((constraints.maxHeight - settings.maxExtent) / 100)
.clamp(0.0, 1.0));
title = Opacity(
opacity: stretchOpacity,
child: title,
);
}
final double opacity = settings.toolbarOpacity;
if (opacity > 0.0) {
TextStyle titleStyle = theme.appBarTheme.titleTextStyle ??
theme.primaryTextTheme.headlineSmall!;
titleStyle = titleStyle.copyWith(
color: titleStyle.color!.withOpacity(opacity));
final bool effectiveCenterTitle = _getEffectiveCenterTitle(theme);
final padding = widget.titlePaddingTween.transform(t) ??
EdgeInsetsDirectional.only(
start: effectiveCenterTitle ? 0.0 : 72.0,
bottom: 16.0,
);
final double scaleValue =
Tween<double>(begin: 1.5, end: 1.0).transform(t);
final Matrix4 scaleTransform = Matrix4.identity()
..scale(scaleValue, scaleValue, 1.0);
final Alignment titleAlignment =
_getTitleAlignment(effectiveCenterTitle);
children.add(Container(
padding: padding,
child: Transform(
alignment: titleAlignment,
transform: scaleTransform,
child: Align(
alignment: titleAlignment,
child: DefaultTextStyle(
style: titleStyle,
child: LayoutBuilder(builder:
(BuildContext context, BoxConstraints constraints) {
return Container(
width: constraints.maxWidth / scaleValue,
alignment: titleAlignment,
child: title,
);
}),
),
),
),
));
}
}
//if(widget.foregrund != null) {
//children.add(widget.foreground);
//}
return ClipRect(child: Stack(children: children));
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment