diff --git a/examples/cookbook/lists/floating_app_bar/lib/main_cupertino.dart b/examples/cookbook/lists/floating_app_bar/lib/main_cupertino.dart new file mode 100644 index 0000000000..5513df9580 --- /dev/null +++ b/examples/cookbook/lists/floating_app_bar/lib/main_cupertino.dart @@ -0,0 +1,41 @@ +import 'package:flutter/cupertino.dart'; + +void main() => runApp(const MyApp()); + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + const title = 'Floating Navigation Bar'; + + return CupertinoApp( + title: title, + home: CupertinoPageScaffold( + // No navigation bar provided to CupertinoPageScaffold, + // only a body with a CustomScrollView. + child: CustomScrollView( + slivers: [ + // Add the navigation bar to the CustomScrollView. + const CupertinoSliverNavigationBar( + // Provide a standard title. + largeTitle: Text(title), + ), + // #docregion SliverList + // Next, create a SliverList + SliverList.builder( + // The builder function returns a CupertinoListTile with a title + // that displays the index of the current item. + itemBuilder: + (context, index) => + CupertinoListTile(title: Text('Item #$index')), + // Builds 50 CupertinoListTile + itemCount: 50, + ), + // #enddocregion SliverList + ], + ), + ), + ); + } +} diff --git a/examples/cookbook/lists/floating_app_bar/lib/main.dart b/examples/cookbook/lists/floating_app_bar/lib/main_material.dart similarity index 59% rename from examples/cookbook/lists/floating_app_bar/lib/main.dart rename to examples/cookbook/lists/floating_app_bar/lib/main_material.dart index 49aa9401c4..6ee2e8fabd 100644 --- a/examples/cookbook/lists/floating_app_bar/lib/main.dart +++ b/examples/cookbook/lists/floating_app_bar/lib/main_material.dart @@ -12,7 +12,7 @@ class MyApp extends StatelessWidget { return MaterialApp( title: title, home: Scaffold( - // No appbar provided to the Scaffold, only a body with a + // No app bar provided to Scaffold, only a body with a // CustomScrollView. body: CustomScrollView( slivers: [ @@ -20,9 +20,8 @@ class MyApp extends StatelessWidget { const SliverAppBar( // Provide a standard title. title: Text(title), - // Allows the user to reveal the app bar if they begin scrolling - // back up the list of items. - floating: true, + // Pin the app bar when scrolling + pinned: true, // Display a placeholder widget to visualize the shrinking size. flexibleSpace: Placeholder(), // Make the initial height of the SliverAppBar larger than normal. @@ -30,15 +29,13 @@ class MyApp extends StatelessWidget { ), // #docregion SliverList // Next, create a SliverList - SliverList( - // Use a delegate to build items as they're scrolled on screen. - delegate: SliverChildBuilderDelegate( - // The builder function returns a ListTile with a title that - // displays the index of the current item. - (context, index) => ListTile(title: Text('Item #$index')), - // Builds 1000 ListTiles - childCount: 1000, - ), + SliverList.builder( + // The builder function returns a ListTile with a title that + // displays the index of the current item. + itemBuilder: + (context, index) => ListTile(title: Text('Item #$index')), + // Builds 50 ListTiles + itemCount: 50, ), // #enddocregion SliverList ], diff --git a/examples/cookbook/lists/floating_app_bar/lib/starter.dart b/examples/cookbook/lists/floating_app_bar/lib/starter.dart deleted file mode 100644 index b4ca0e08e3..0000000000 --- a/examples/cookbook/lists/floating_app_bar/lib/starter.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:flutter/material.dart'; - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - // #docregion CustomScrollView - return const Scaffold( - // No appBar property provided, only the body. - body: CustomScrollView( - // Add the app bar and list of items as slivers in the next steps. - slivers: [], - ), - ); - // #enddocregion CustomScrollView - } -} diff --git a/examples/cookbook/lists/floating_app_bar/lib/starter_cupertino.dart b/examples/cookbook/lists/floating_app_bar/lib/starter_cupertino.dart new file mode 100644 index 0000000000..17074a6171 --- /dev/null +++ b/examples/cookbook/lists/floating_app_bar/lib/starter_cupertino.dart @@ -0,0 +1,21 @@ +import 'package:flutter/cupertino.dart'; + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + // #docregion CustomScrollView + return const CupertinoApp( + title: 'Floating Navigation Bar', + home: CupertinoPageScaffold( + // No navigation bar property provided yet. + child: CustomScrollView( + // Add the navigation bar and list of items as slivers in the next steps. + slivers: [], + ), + ), + ); + // #enddocregion CustomScrollView + } +} diff --git a/examples/cookbook/lists/floating_app_bar/lib/starter_material.dart b/examples/cookbook/lists/floating_app_bar/lib/starter_material.dart new file mode 100644 index 0000000000..ab08bbbb6d --- /dev/null +++ b/examples/cookbook/lists/floating_app_bar/lib/starter_material.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + // #docregion CustomScrollView + return const MaterialApp( + title: 'Floating App Bar', + home: Scaffold( + // No app bar property provided yet. + body: CustomScrollView( + // Add the app bar and list of items as slivers in the next steps. + slivers: [], + ), + ), + ); + // #enddocregion CustomScrollView + } +} diff --git a/examples/cookbook/lists/floating_app_bar/lib/step2_cupertino.dart b/examples/cookbook/lists/floating_app_bar/lib/step2_cupertino.dart new file mode 100644 index 0000000000..aeb1c8c9ae --- /dev/null +++ b/examples/cookbook/lists/floating_app_bar/lib/step2_cupertino.dart @@ -0,0 +1,29 @@ +import 'package:flutter/cupertino.dart'; + +void main() => runApp(const MyApp()); + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return const CupertinoApp( + title: 'Floating App Bar', + home: CupertinoPageScaffold( + // No navigation bar provided to CupertinoPageScaffold, + // only a body with a CustomScrollView. + child: CustomScrollView( + // #docregion SliverAppBar + slivers: [ + // Add the navigation bar to the CustomScrollView. + CupertinoSliverNavigationBar( + // Provide a standard title. + largeTitle: Text('Floating App Bar'), + ), + ], + // #enddocregion SliverAppBar + ), + ), + ); + } +} diff --git a/examples/cookbook/lists/floating_app_bar/lib/step2.dart b/examples/cookbook/lists/floating_app_bar/lib/step2_material.dart similarity index 72% rename from examples/cookbook/lists/floating_app_bar/lib/step2.dart rename to examples/cookbook/lists/floating_app_bar/lib/step2_material.dart index a70e7dbc7d..88b227aced 100644 --- a/examples/cookbook/lists/floating_app_bar/lib/step2.dart +++ b/examples/cookbook/lists/floating_app_bar/lib/step2_material.dart @@ -7,31 +7,28 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - const title = 'Floating App Bar'; - return const MaterialApp( - title: title, + title: 'Floating App Bar', home: Scaffold( // No appbar provided to the Scaffold, only a body with a // CustomScrollView. - // #docregion SliverAppBar body: CustomScrollView( + // #docregion SliverAppBar slivers: [ // Add the app bar to the CustomScrollView. SliverAppBar( // Provide a standard title. - title: Text(title), - // Allows the user to reveal the app bar if they begin scrolling - // back up the list of items. - floating: true, + title: Text('Floating App Bar'), + // Pin the app bar when scrolling. + pinned: true, // Display a placeholder widget to visualize the shrinking size. flexibleSpace: Placeholder(), // Make the initial height of the SliverAppBar larger than normal. expandedHeight: 200, ), ], + // #enddocregion SliverAppBar ), - // #enddocregion SliverAppBar ), ); } diff --git a/src/content/cookbook/lists/floating-app-bar.md b/src/content/cookbook/lists/floating-app-bar.md index 81bddeef35..a66501ef6a 100644 --- a/src/content/cookbook/lists/floating-app-bar.md +++ b/src/content/cookbook/lists/floating-app-bar.md @@ -1,6 +1,6 @@ --- title: Place a floating app bar above a list -description: How to place a floating app bar above a list. +description: How to place a floating app bar or navigation bar above a list. js: - defer: true url: /assets/js/inject_dartpad.js @@ -8,143 +8,227 @@ js: -To make it easier for users to view a list of items, -you might want to hide the app bar as the user scrolls down the list. -This is especially true if your app displays a "tall" -app bar that occupies a lot of vertical space. +This guide describes how to place a floating app bar or +navigation bar above a list in a Flutter app. + +## Overview -Typically, you create an app bar by providing an `appBar` property to the -`Scaffold` widget. This creates a fixed app bar that always remains above -the `body` of the `Scaffold`. +To make it easier for users to view a list of items, +you might want to minimize the app bar (navigation bar), as +the user scrolls down the list. -Moving the app bar from a `Scaffold` widget into a -[`CustomScrollView`][] allows you to create an app bar -that scrolls offscreen as you scroll through a -list of items contained inside the `CustomScrollView`. +Moving the app bar into a [`CustomScrollView`][] allows you +to create an app bar that can be minimized or scroll +offscreen as you scroll through a list of items contained +inside the `CustomScrollView`. -This recipe demonstrates how to use a `CustomScrollView` to display a list of -items with an app bar on top that scrolls offscreen as the user scrolls -down the list using the following steps: +This recipe demonstrates how to use a `CustomScrollView` to +display a list of items with an app bar on top that +minimizes as the user scrolls down the list using the +following steps: 1. Create a `CustomScrollView`. - 2. Use `SliverAppBar` to add a floating app bar. - 3. Add a list of items using a `SliverList`. + 2. Add a floating app bar to `CustomScrollView`. + 3. Add a list of items to `CustomScrollView`. ## 1. Create a `CustomScrollView` To create a floating app bar, place the app bar inside a `CustomScrollView` that also contains the list of items. -This synchronizes the scroll position of the app bar and the list of items. -You might think of the `CustomScrollView` widget as a `ListView` -that allows you to mix and match different types of scrollable lists -and widgets together. +This synchronizes the scroll position of the app bar and the +list of items. You might think of the `CustomScrollView` +widget as a `ListView` that allows you to mix and match +different types of scrollable lists and widgets together. The scrollable lists and widgets provided to the -`CustomScrollView` are known as _slivers_. There are several types -of slivers, such as `SliverList`, `SliverGrid`, and `SliverAppBar`. -In fact, the `ListView` and `GridView` widgets use the `SliverList` and -`SliverGrid` widgets to implement scrolling. +`CustomScrollView` are known as _slivers_. There are several +types of slivers, such as `SliverList`, `SliverGrid`, and +`SliverAppBar`. In fact, the `ListView` and `GridView` +widgets use the `SliverList` and `SliverGrid` widgets to +implement scrolling. + +For this example, create a `CustomScrollView` that contains +a `SliverList`. Also, remove the app bar property from your +code if it exists. + +{% tabs "device-type-tabs" %} -For this example, create a `CustomScrollView` that contains a -`SliverAppBar` and a `SliverList`. In addition, remove any app bars -that you provide to the `Scaffold` widget. +{% tab "Material widgets" %} - + ```dart -Scaffold( - // No appBar property provided, only the body. - body: CustomScrollView( - // Add the app bar and list of items as slivers in the next steps. - slivers: [], +MaterialApp( + title: 'Floating App Bar', + home: Scaffold( + // No app bar property provided yet. + body: CustomScrollView( + // Add the app bar and list of items as slivers in the next steps. + slivers: [], + ), ), ); ``` -### 2. Use `SliverAppBar` to add a floating app bar +{% endtab %} + +{% tab "Cupertino widgets" %} + + +```dart +CupertinoApp( + title: 'Floating Navigation Bar', + home: CupertinoPageScaffold( + // No navigation bar property provided yet. + child: CustomScrollView( + // Add the navigation bar and list of items as slivers in the next steps. + slivers: [], + ), + ), +); +``` + +{% endtab %} + +{% endtabs %} + + +## 2. Add a floating app bar Next, add an app bar to the [`CustomScrollView`][]. + +{% tabs "device-type-tabs" %} + +{% tab "Material widgets" %} + Flutter provides the [`SliverAppBar`][] widget which, much like the normal `AppBar` widget, uses the `SliverAppBar` to display a title, tabs, images and more. -However, the `SliverAppBar` also gives you the ability to create a "floating" -app bar that scrolls offscreen as the user scrolls down the list. -Furthermore, you can configure the `SliverAppBar` to shrink and -expand as the user scrolls. +However, the `SliverAppBar` also gives you the ability to +create a "floating" app bar that shrinks and floats when +you're not at the top of the page. To create this effect: 1. Start with an app bar that displays only a title. - 2. Set the `floating` property to `true`. - This allows users to quickly reveal the app bar when - they scroll up the list. + 2. Set the `pinned` property to `true`. 3. Add a `flexibleSpace` widget that fills the available `expandedHeight`. - + ```dart -CustomScrollView( - slivers: [ - // Add the app bar to the CustomScrollView. - SliverAppBar( - // Provide a standard title. - title: Text(title), - // Allows the user to reveal the app bar if they begin scrolling - // back up the list of items. - floating: true, - // Display a placeholder widget to visualize the shrinking size. - flexibleSpace: Placeholder(), - // Make the initial height of the SliverAppBar larger than normal. - expandedHeight: 200, - ), - ], -) +slivers: [ + // Add the app bar to the CustomScrollView. + SliverAppBar( + // Provide a standard title. + title: Text('Floating App Bar'), + // Pin the app bar when scrolling. + pinned: true, + // Display a placeholder widget to visualize the shrinking size. + flexibleSpace: Placeholder(), + // Make the initial height of the SliverAppBar larger than normal. + expandedHeight: 200, + ), +], ``` :::tip Play around with the [various properties you can pass to the `SliverAppBar` widget][], -and use hot reload to see the results. For example, use an `Image` -widget for the `flexibleSpace` property to create a background image that -shrinks in size as it's scrolled offscreen. +and use hot reload to see the results. For example, use an +`Image` widget for the `flexibleSpace` property to create a +background image that shrinks in size as it's scrolled +offscreen. ::: +{% endtab %} + +{% tab "Cupertino widgets" %} -### 3. Add a list of items using a `SliverList` +Flutter provides the [`CupertinoSliverNavigationBar`][] +widget, which lets you have a "floating" navigation +bar that shrinks when you scroll down and floats when +you're not at the top of the page. -Now that you have the app bar in place, add a list of items to the -`CustomScrollView`. You have two options: a [`SliverList`][] -or a [`SliverGrid`][]. If you need to display a list of items one after the other, -use the `SliverList` widget. -If you need to display a grid list, use the `SliverGrid` widget. +To create this effect: -The `SliverList` and `SliverGrid` widgets take one required parameter: a -[`SliverChildDelegate`][], which provides a list -of widgets to `SliverList` or `SliverGrid`. -For example, the [`SliverChildBuilderDelegate`][] -allows you to create a list of items that are built lazily as you scroll, -just like the `ListView.builder` widget. + 1. Add `CupertinoSliverNavigationBar` to + `CustomScrollView`. + 2. Start with an app bar that displays only a title. - + ```dart -// Next, create a SliverList -SliverList( - // Use a delegate to build items as they're scrolled on screen. - delegate: SliverChildBuilderDelegate( - // The builder function returns a ListTile with a title that - // displays the index of the current item. - (context, index) => ListTile(title: Text('Item #$index')), - // Builds 1000 ListTiles - childCount: 1000, +slivers: [ + // Add the navigation bar to the CustomScrollView. + CupertinoSliverNavigationBar( + // Provide a standard title. + largeTitle: Text('Floating App Bar'), ), +], +``` + +{% endtab %} + +{% endtabs %} + + +## 3. Add a list of items + +Now that you have the app bar in place, add a list of items +to the `CustomScrollView`. You have two options: a +[`SliverList`][] or a [`SliverGrid`][]. If you need to +display a list of items one after the other, use the +`SliverList` widget. If you need to display a grid list, use +the `SliverGrid` widget. + +{% tabs "device-type-tabs" %} + +{% tab "Material widgets" %} + + +```dart +// Next, create a SliverList +SliverList.builder( + // The builder function returns a ListTile with a title that + // displays the index of the current item. + itemBuilder: + (context, index) => ListTile(title: Text('Item #$index')), + // Builds 50 ListTiles + itemCount: 50, +) +``` + +{% endtab %} + +{% tab "Cupertino widgets" %} + + +```dart +// Next, create a SliverList +SliverList.builder( + // The builder function returns a CupertinoListTile with a title + // that displays the index of the current item. + itemBuilder: + (context, index) => + CupertinoListTile(title: Text('Item #$index')), + // Builds 50 CupertinoListTile + itemCount: 50, ) ``` +{% endtab %} + +{% endtabs %} + ## Interactive example - -```dartpad title="Flutter Floating AppBar hands-on example in DartPad" run="true" +{% tabs "device-type-tabs" %} + +{% tab "Material widgets" %} + + +```dartpad title="Flutter floating app bar hands-on example in DartPad" run="false" import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); @@ -159,7 +243,7 @@ class MyApp extends StatelessWidget { return MaterialApp( title: title, home: Scaffold( - // No appbar provided to the Scaffold, only a body with a + // No app bar provided to Scaffold, only a body with a // CustomScrollView. body: CustomScrollView( slivers: [ @@ -167,24 +251,72 @@ class MyApp extends StatelessWidget { const SliverAppBar( // Provide a standard title. title: Text(title), - // Allows the user to reveal the app bar if they begin scrolling - // back up the list of items. - floating: true, + // Pin the app bar when scrolling + pinned: true, // Display a placeholder widget to visualize the shrinking size. flexibleSpace: Placeholder(), // Make the initial height of the SliverAppBar larger than normal. expandedHeight: 200, ), // Next, create a SliverList - SliverList( - // Use a delegate to build items as they're scrolled on screen. - delegate: SliverChildBuilderDelegate( - // The builder function returns a ListTile with a title that - // displays the index of the current item. - (context, index) => ListTile(title: Text('Item #$index')), - // Builds 1000 ListTiles - childCount: 1000, - ), + SliverList.builder( + // The builder function returns a ListTile with a title that + // displays the index of the current item. + itemBuilder: + (context, index) => ListTile(title: Text('Item #$index')), + // Builds 50 ListTiles + itemCount: 50, + ), + ], + ), + ), + ); + } +} +``` + + + +{% endtab %} + +{% tab "Cupertino widgets" %} + + +```dartpad title="Flutter floating navigation bar hands-on example in DartPad" run="false" +import 'package:flutter/cupertino.dart'; + +void main() => runApp(const MyApp()); + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + const title = 'Floating Navigation Bar'; + + return CupertinoApp( + title: title, + home: CupertinoPageScaffold( + // No navigation bar provided to CupertinoPageScaffold, + // only a body with a CustomScrollView. + child: CustomScrollView( + slivers: [ + // Add the navigation bar to the CustomScrollView. + const CupertinoSliverNavigationBar( + // Provide a standard title. + largeTitle: Text(title), + ), + // Next, create a SliverList + SliverList.builder( + // The builder function returns a CupertinoListTile with a title + // that displays the index of the current item. + itemBuilder: + (context, index) => + CupertinoListTile(title: Text('Item #$index')), + // Builds 50 CupertinoListTile + itemCount: 50, ), ], ), @@ -195,10 +327,14 @@ class MyApp extends StatelessWidget { ``` +{% endtab %} + +{% endtabs %} +[`CupertinoSliverNavigationBar`]: {{site.api}}/flutter/cupertino/CupertinoSliverNavigationBar-class.html [`CustomScrollView`]: {{site.api}}/flutter/widgets/CustomScrollView-class.html [`SliverAppBar`]: {{site.api}}/flutter/material/SliverAppBar-class.html [`SliverChildBuilderDelegate`]: {{site.api}}/flutter/widgets/SliverChildBuilderDelegate-class.html