Skip to content

Commit

Permalink
Merge pull request #64 from proformance/add-flexiblespace-animation-f…
Browse files Browse the repository at this point in the history
…ade-transition
  • Loading branch information
JonasWanke authored Aug 13, 2024
2 parents cf6295d + 88a2b7e commit f6968e6
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 1 deletion.
23 changes: 23 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,19 @@ class FirstPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: MorphingAppBar(
leading: Icon(Icons.menu),
title: const Text('🔙 swipeable_page_route example'),
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(255, 0, 92, 157),
Color(0xff0078C1),
Color.fromARGB(255, 9, 149, 224),
],
),
),
),
),
body: Center(
child: ElevatedButton(
Expand Down Expand Up @@ -90,6 +102,17 @@ class _SecondPageState extends State<SecondPage> {
return Scaffold(
appBar: MorphingAppBar(
title: const Text('Page 2'),
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Color(0xffFF5733),
Color(0xffC70039),
Color(0xff900C3F),
],
),
),
),
actions: [
IconButton(
key: const ValueKey('check'),
Expand Down
3 changes: 2 additions & 1 deletion lib/src/app_bar/app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:flutter/services.dart';

import 'actions.dart';
import 'bottom.dart';
import 'flexible.dart';
import 'leading.dart';
import 'state.dart';
import 'title.dart';
Expand Down Expand Up @@ -247,7 +248,7 @@ class _AnimatedAppBar extends AnimatedWidget {
automaticallyImplyLeading: false,
title: AnimatedTitle(state),
actions: [AnimatedActions(state)],
// TODO(JonasWanke): Animate `flexibleSpace`
flexibleSpace: AnimatedFlexibleSpace(state),
bottom: AnimatedBottom(state),
elevation: state.elevation,
scrolledUnderElevation: state.scrolledUnderElevation,
Expand Down
107 changes: 107 additions & 0 deletions lib/src/app_bar/flexible.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import 'dart:math' as math;
import 'dart:ui';

import 'package:black_hole_flutter/black_hole_flutter.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

import 'app_bar.dart';
import 'state.dart';

class AnimatedFlexibleSpace extends MultiChildRenderObjectWidget {
AnimatedFlexibleSpace(MorphingState state)
: t = state.t,
super(
children: [_createChild(state.parent), _createChild(state.child)],
);

final double t;

static Widget _createChild(EndState state) {
final flexibleSpace = state.appBar.flexibleSpace;
if (flexibleSpace == null) return const SizedBox();

return DefaultTextStyle.merge(child: flexibleSpace);
}

@override
RenderObject createRenderObject(BuildContext context) =>
_AnimatedFlexibleSpaceLayout(t: t);

@override
void updateRenderObject(
BuildContext context,
covariant RenderObject renderObject,
) =>
(renderObject as _AnimatedFlexibleSpaceLayout).t = t;
}

class _AnimatedFlexibleSpaceParentData
extends ContainerBoxParentData<RenderBox> {}

class _AnimatedFlexibleSpaceLayout
extends AnimatedAppBarLayout<_AnimatedFlexibleSpaceParentData> {
_AnimatedFlexibleSpaceLayout({super.t});

@override
void setupParentData(RenderObject child) {
if (child.parentData is! _AnimatedFlexibleSpaceParentData) {
child.parentData = _AnimatedFlexibleSpaceParentData();
}
}

@override
double computeMinIntrinsicWidth(double height) =>
children.map((c) => c.getMinIntrinsicWidth(height)).max.toDouble();
@override
double computeMaxIntrinsicWidth(double height) =>
children.map((c) => c.getMaxIntrinsicWidth(height)).max.toDouble();
@override
double computeMinIntrinsicHeight(double width) =>
children.map((c) => c.getMinIntrinsicHeight(width)).max.toDouble();
@override
double computeMaxIntrinsicHeight(double width) =>
children.map((c) => c.getMaxIntrinsicHeight(width)).max.toDouble();

@override
bool get alwaysNeedsCompositing => true;

@override
void performLayout() {
assert(!sizedByParent);

final parent = firstChild!;
final child = parent.data.nextSibling!;

parent.layout(constraints, parentUsesSize: true);
child.layout(constraints, parentUsesSize: true);
size = parent.size.coerceAtLeast(child.size);

parent.data.offset = Offset(0, (size.height - parent.size.height) / 2);
child.data.offset = Offset(0, (size.height - child.size.height) / 2);
}

@override
void paint(PaintingContext context, Offset offset) {
final parent = firstChild!;
final child = parent.data.nextSibling!;

context
..pushOpacity(
offset,
math.max<double>(0, 1 - t).opacityToAlpha,
(context, offset) => context.paintChild(parent, offset),
)
..pushOpacity(
offset,
math.max<double>(0, t).opacityToAlpha,
(context, offset) => context.paintChild(child, offset),
);
}
}

extension _ParentData on RenderBox {
_AnimatedFlexibleSpaceParentData get data =>
parentData! as _AnimatedFlexibleSpaceParentData;
}

0 comments on commit f6968e6

Please sign in to comment.