Skip to content

Commit

Permalink
Fix FlexibleSpaceBar.title doesn't respect the leading widget (#132…
Browse files Browse the repository at this point in the history
…573)

fixes [Long `FlexibleSpaceBar.title` doesn't respect the leading widget 
](flutter/flutter#132030)

### Description

- This adds `FlexibleSpaceBarSettings.hasLeading` for the `FlexibleSpaceBar`'s title to respect the leading widget.
- Use the new `FlexibleSpaceBarSettings.hasLeading` property in the  `SliverAppBar` for its `FlexibleSpaceBar`.

### Code sample

<details> 
<summary>expand to view the code sample</summary> 

```dart
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @OverRide
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        brightness: Brightness.dark,
      ),
      home: const Example(),
    );
  }
}

class Example extends StatelessWidget {
  const Example({super.key});

  @OverRide
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Text('TargetPlatform.Android'),
          Theme(
            data: Theme.of(context).copyWith(
              platform: TargetPlatform.android,
            ),
            child: Container(
              height: 250,
              padding: const EdgeInsets.all(8),
              decoration: BoxDecoration(
                border: Border.all(
                  color: Colors.amber,
                  width: 4,
                ),
              ),
              child: const AppBarLeading(
                showLeading: true,
                showTitle: false,
              ),
            ),
          ),
          const Text('TargetPlatform.iOS'),
          Theme(
            data: Theme.of(context).copyWith(
              platform: TargetPlatform.iOS,
            ),
            child: Container(
              height: 250,
              padding: const EdgeInsets.all(8),
              decoration: BoxDecoration(
                border: Border.all(
                  color: Colors.amber,
                  width: 2,
                ),
              ),
              child: const AppBarLeading(
                showLeading: true,
                showTitle: false,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class AppBarLeading extends StatelessWidget {
  const AppBarLeading({
    super.key,
    required this.showLeading,
    required this.showTitle,
  });

  final bool showLeading;
  final bool showTitle;

  @OverRide
  Widget build(BuildContext context) {
    return Scaffold(
      drawer: const Drawer(),
      body: CustomScrollView(
        slivers: [
          SliverAppBar(
            automaticallyImplyLeading: showLeading,
            iconTheme: const IconThemeData(
              color: Colors.amber,
            ),
            title: showTitle ? const Text('AppBar') : null,
            flexibleSpace: FlexibleSpaceBar(
              title: Text('Title ' * 15),
              // centerTitle: true,
            ),
            toolbarHeight: showTitle ? 170 : 100,
          ),
        ],
      ),
    );
  }
}
``` 
	
</details>

### Before 

![Screenshot 2023-08-15 at 18 11 34](https://github.com/flutter/flutter/assets/48603081/4b798998-8549-43aa-b564-933ea14f494c)

### After

![Screenshot 2023-08-15 at 18 11 45](https://github.com/flutter/flutter/assets/48603081/b085a33a-db7d-40d4-8a12-ee37197b5bd4)
  • Loading branch information
TahaTesser authored Aug 22, 2023
1 parent 0283d8a commit db89df5
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 2 deletions.
1 change: 1 addition & 0 deletions packages/flutter/lib/src/material/app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1261,6 +1261,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
currentExtent: math.max(minExtent, maxExtent - shrinkOffset),
toolbarOpacity: toolbarOpacity,
isScrolledUnder: isScrolledUnder,
hasLeading: leading != null || automaticallyImplyLeading,
child: AppBar(
clipBehavior: clipBehavior,
leading: leading,
Expand Down
18 changes: 16 additions & 2 deletions packages/flutter/lib/src/material/flexible_space_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ class FlexibleSpaceBar extends StatefulWidget {
double? minExtent,
double? maxExtent,
bool? isScrolledUnder,
bool? hasLeading,
required double currentExtent,
required Widget child,
}) {
Expand All @@ -164,6 +165,7 @@ class FlexibleSpaceBar extends StatefulWidget {
minExtent: minExtent ?? currentExtent,
maxExtent: maxExtent ?? currentExtent,
isScrolledUnder: isScrolledUnder,
hasLeading: hasLeading,
currentExtent: currentExtent,
child: child,
);
Expand Down Expand Up @@ -321,7 +323,7 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
final bool effectiveCenterTitle = _getEffectiveCenterTitle(theme);
final EdgeInsetsGeometry padding = widget.titlePadding ??
EdgeInsetsDirectional.only(
start: effectiveCenterTitle ? 0.0 : 72.0,
start: effectiveCenterTitle && !(settings.hasLeading ?? false) ? 0.0 : 72.0,
bottom: 16.0,
);
final double scaleValue = Tween<double>(begin: widget.expandedTitleScale, end: 1.0).transform(t);
Expand Down Expand Up @@ -380,6 +382,7 @@ class FlexibleSpaceBarSettings extends InheritedWidget {
required this.currentExtent,
required super.child,
this.isScrolledUnder,
this.hasLeading,
}) : assert(minExtent >= 0),
assert(maxExtent >= 0),
assert(currentExtent >= 0),
Expand Down Expand Up @@ -413,13 +416,24 @@ class FlexibleSpaceBarSettings extends InheritedWidget {
/// overlaps the primary scrollable's contents.
final bool? isScrolledUnder;

/// True if the FlexibleSpaceBar has a leading widget.
///
/// This value is used by the [FlexibleSpaceBar] to determine
/// if there should be a gap between the leading widget and
/// the title.
///
/// Null if the caller hasn't determined if the FlexibleSpaceBar
/// has a leading widget.
final bool? hasLeading;

@override
bool updateShouldNotify(FlexibleSpaceBarSettings oldWidget) {
return toolbarOpacity != oldWidget.toolbarOpacity
|| minExtent != oldWidget.minExtent
|| maxExtent != oldWidget.maxExtent
|| currentExtent != oldWidget.currentExtent
|| isScrolledUnder != oldWidget.isScrolledUnder;
|| isScrolledUnder != oldWidget.isScrolledUnder
|| hasLeading != oldWidget.hasLeading;
}
}

Expand Down
88 changes: 88 additions & 0 deletions packages/flutter/test/material/flexible_space_bar_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,94 @@ void main() {
expect(RenderRebuildTracker.count, greaterThan(1));
expect(tester.layers.whereType<OpacityLayer>(), isEmpty);
});

// This is a regression test for https://github.com/flutter/flutter/issues/132030.
testWidgetsWithLeakTracking('FlexibleSpaceBarSettings.hasLeading provides a gap between leading and title', (WidgetTester tester) async {
final FlexibleSpaceBarSettings customSettings = FlexibleSpaceBar.createSettings(
currentExtent: 200.0,
hasLeading: true,
child: AppBar(
leading: const Icon(Icons.menu),
flexibleSpace: FlexibleSpaceBar(
title: Text('title ' * 10),
centerTitle: true,
),
),
) as FlexibleSpaceBarSettings;

await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
floating: true,
pinned: true,
delegate: TestDelegate(settings: customSettings),
),
SliverToBoxAdapter(
child: Container(
height: 1200.0,
color: Colors.orange[400],
),
),
],
),
),
),
);

await tester.pumpAndSettle();
expect(tester.getTopLeft(find.byType(Text)).dx, closeTo(72.0, 0.01));
});

// This is a regression test for https://github.com/flutter/flutter/issues/132030.
testWidgetsWithLeakTracking('Long centered FlexibleSpaceBar.title respects leading widget', (WidgetTester tester) async {
// Test start position of a long title when the leading widget is
// shown by default and the long title is centered.
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
drawer: const Drawer(),
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
flexibleSpace: FlexibleSpaceBar(
title: Text('Title ' * 10),
centerTitle: true,
),
),
],
),
),
),
);

expect(tester.getTopLeft(find.byType(Text)).dx, 72.0);

// Test start position of a long title when the leading widget is provided
// and the long title is centered.
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
leading: const Icon(Icons.menu),
flexibleSpace: FlexibleSpaceBar(
title: Text('Title ' * 10),
centerTitle: true,
),
),
],
),
),
),
);

await tester.pumpAndSettle();
expect(tester.getTopLeft(find.byType(Text)).dx, 72.0);
});
}

class TestDelegate extends SliverPersistentHeaderDelegate {
Expand Down

0 comments on commit db89df5

Please sign in to comment.