mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Feat: Animate fill for material app bar (#163913)
Feat: Animate fill for material app bar fixes: #162988 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
This commit is contained in:
parent
708c0eb185
commit
b0f5c8ce03
@ -221,6 +221,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
|
|||||||
this.useDefaultSemanticsOrder = true,
|
this.useDefaultSemanticsOrder = true,
|
||||||
this.clipBehavior,
|
this.clipBehavior,
|
||||||
this.actionsPadding,
|
this.actionsPadding,
|
||||||
|
this.animateColor = false,
|
||||||
}) : assert(elevation == null || elevation >= 0.0),
|
}) : assert(elevation == null || elevation >= 0.0),
|
||||||
preferredSize = _PreferredAppBarSize(toolbarHeight, bottom?.preferredSize.height);
|
preferredSize = _PreferredAppBarSize(toolbarHeight, bottom?.preferredSize.height);
|
||||||
|
|
||||||
@ -773,6 +774,9 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
|
|||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
final EdgeInsetsGeometry? actionsPadding;
|
final EdgeInsetsGeometry? actionsPadding;
|
||||||
|
|
||||||
|
/// Whether the color should be animated.
|
||||||
|
final bool animateColor;
|
||||||
|
|
||||||
bool _getEffectiveCenterTitle(ThemeData theme) {
|
bool _getEffectiveCenterTitle(ThemeData theme) {
|
||||||
bool platformCenter() {
|
bool platformCenter() {
|
||||||
switch (theme.platform) {
|
switch (theme.platform) {
|
||||||
@ -1213,6 +1217,7 @@ class _AppBarState extends State<AppBar> {
|
|||||||
??
|
??
|
||||||
(theme.useMaterial3 ? theme.colorScheme.surfaceTint : null),
|
(theme.useMaterial3 ? theme.colorScheme.surfaceTint : null),
|
||||||
shape: widget.shape ?? appBarTheme.shape ?? defaults.shape,
|
shape: widget.shape ?? appBarTheme.shape ?? defaults.shape,
|
||||||
|
animateColor: widget.animateColor,
|
||||||
child: Semantics(explicitChildNodes: true, child: appBar),
|
child: Semantics(explicitChildNodes: true, child: appBar),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -204,6 +204,7 @@ class Material extends StatefulWidget {
|
|||||||
this.clipBehavior = Clip.none,
|
this.clipBehavior = Clip.none,
|
||||||
this.animationDuration = kThemeChangeDuration,
|
this.animationDuration = kThemeChangeDuration,
|
||||||
this.child,
|
this.child,
|
||||||
|
this.animateColor = false,
|
||||||
}) : assert(elevation >= 0.0),
|
}) : assert(elevation >= 0.0),
|
||||||
assert(!(shape != null && borderRadius != null)),
|
assert(!(shape != null && borderRadius != null)),
|
||||||
assert(!(identical(type, MaterialType.circle) && (borderRadius != null || shape != null)));
|
assert(!(identical(type, MaterialType.circle) && (borderRadius != null || shape != null)));
|
||||||
@ -218,6 +219,9 @@ class Material extends StatefulWidget {
|
|||||||
/// the shape is rectangular, and the default color.
|
/// the shape is rectangular, and the default color.
|
||||||
final MaterialType type;
|
final MaterialType type;
|
||||||
|
|
||||||
|
/// Whether the color should be animated.
|
||||||
|
final bool animateColor;
|
||||||
|
|
||||||
/// {@template flutter.material.material.elevation}
|
/// {@template flutter.material.material.elevation}
|
||||||
/// The z-coordinate at which to place this material relative to its parent.
|
/// The z-coordinate at which to place this material relative to its parent.
|
||||||
///
|
///
|
||||||
@ -522,7 +526,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
|
|||||||
elevation: widget.elevation,
|
elevation: widget.elevation,
|
||||||
color: color,
|
color: color,
|
||||||
shadowColor: modelShadowColor,
|
shadowColor: modelShadowColor,
|
||||||
animateColor: false,
|
animateColor: widget.animateColor,
|
||||||
child: contents,
|
child: contents,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2244,17 +2244,21 @@ void main() {
|
|||||||
required double contentHeight,
|
required double contentHeight,
|
||||||
bool reverse = false,
|
bool reverse = false,
|
||||||
bool includeFlexibleSpace = false,
|
bool includeFlexibleSpace = false,
|
||||||
|
bool animateColor = false,
|
||||||
|
double? scrolledUnderElevation,
|
||||||
}) {
|
}) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: scrolledUnderElevation,
|
||||||
backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
|
backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
|
||||||
return states.contains(MaterialState.scrolledUnder) ? scrolledColor : defaultColor;
|
return states.contains(MaterialState.scrolledUnder) ? scrolledColor : defaultColor;
|
||||||
}),
|
}),
|
||||||
title: const Text('AppBar'),
|
title: const Text('AppBar'),
|
||||||
flexibleSpace:
|
flexibleSpace:
|
||||||
includeFlexibleSpace ? const FlexibleSpaceBar(title: Text('FlexibleSpace')) : null,
|
includeFlexibleSpace ? const FlexibleSpaceBar(title: Text('FlexibleSpace')) : null,
|
||||||
|
animateColor: animateColor,
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
reverse: reverse,
|
reverse: reverse,
|
||||||
@ -2339,6 +2343,39 @@ void main() {
|
|||||||
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
|
expect(tester.getSize(findAppBarMaterial()).height, kToolbarHeight);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('backgroundColor animation', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
buildAppBar(contentHeight: 1200.0, scrolledUnderElevation: 0, animateColor: true),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getAppBarAnimatedBackgroundColor(tester), defaultColor);
|
||||||
|
|
||||||
|
TestGesture gesture = await tester.startGesture(const Offset(50.0, 400.0));
|
||||||
|
await gesture.moveBy(const Offset(0.0, -kToolbarHeight));
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(getAppBarAnimatedBackgroundColor(tester), defaultColor);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(getAppBarAnimatedBackgroundColor(tester), scrolledColor);
|
||||||
|
|
||||||
|
gesture = await tester.startGesture(const Offset(50.0, 300.0));
|
||||||
|
await gesture.moveBy(const Offset(0.0, kToolbarHeight));
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(getAppBarAnimatedBackgroundColor(tester), scrolledColor);
|
||||||
|
|
||||||
|
// Check intermediate color values.
|
||||||
|
await tester.pump(const Duration(milliseconds: 50));
|
||||||
|
expect(getAppBarAnimatedBackgroundColor(tester), isSameColorAs(const Color(0xFF00C33C)));
|
||||||
|
await tester.pump(const Duration(milliseconds: 50));
|
||||||
|
expect(getAppBarAnimatedBackgroundColor(tester), isSameColorAs(const Color(0xFF0039C6)));
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(getAppBarAnimatedBackgroundColor(tester), defaultColor);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('backgroundColor with FlexibleSpace', (WidgetTester tester) async {
|
testWidgets('backgroundColor with FlexibleSpace', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(buildAppBar(contentHeight: 1200.0, includeFlexibleSpace: true));
|
await tester.pumpWidget(buildAppBar(contentHeight: 1200.0, includeFlexibleSpace: true));
|
||||||
|
|
||||||
|
@ -5,6 +5,14 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
Finder findAppBarPhysicalModel() {
|
||||||
|
return find.descendant(of: find.byType(AppBar), matching: find.byType(PhysicalModel)).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color? getAppBarAnimatedBackgroundColor(WidgetTester tester) {
|
||||||
|
return tester.widget<PhysicalModel>(findAppBarPhysicalModel()).color;
|
||||||
|
}
|
||||||
|
|
||||||
Finder findAppBarMaterial() {
|
Finder findAppBarMaterial() {
|
||||||
return find.descendant(of: find.byType(AppBar), matching: find.byType(Material)).first;
|
return find.descendant(of: find.byType(AppBar), matching: find.byType(Material)).first;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user