任何具有PreferredSize的widget都可以出现在AppBar的底部。

    Android screenshot

    通常,AppBar的底部widget是TabBar,但是可以使用具有PreferredSize的任何widget。在这个应用程序中,应用程序栏的底部widget是一个TabPageSelector,用于显示应用程序的TabBarView中所选页面的相对位置。 可以通过工具栏部中的箭头按钮,选择上一页或下一页。

    通过flutter create命令创建一个新项目,并用下面的代码替换lib/main.dart的内容来尝试运行一下。

    1. // Copyright 2017 The Chromium Authors. All rights reserved.
    2. // Use of this source code is governed by a BSD-style license that can be
    3. // found in the LICENSE file.
    4. import 'package:flutter/material.dart';
    5. class AppBarBottomSample extends StatefulWidget {
    6. @override
    7. _AppBarBottomSampleState createState() => new _AppBarBottomSampleState();
    8. }
    9. class _AppBarBottomSampleState extends State<AppBarBottomSample> with SingleTickerProviderStateMixin {
    10. TabController _tabController;
    11. @override
    12. void initState() {
    13. super.initState();
    14. _tabController = new TabController(vsync: this, length: choices.length);
    15. }
    16. @override
    17. void dispose() {
    18. _tabController.dispose();
    19. super.dispose();
    20. }
    21. void _nextPage(int delta) {
    22. final int newIndex = _tabController.index + delta;
    23. if (newIndex < 0 || newIndex >= _tabController.length)
    24. return;
    25. _tabController.animateTo(newIndex);
    26. }
    27. @override
    28. Widget build(BuildContext context) {
    29. return new MaterialApp(
    30. home: new Scaffold(
    31. appBar: new AppBar(
    32. title: const Text('AppBar Bottom Widget'),
    33. leading: new IconButton(
    34. tooltip: 'Previous choice',
    35. icon: const Icon(Icons.arrow_back),
    36. onPressed: () { _nextPage(-1); },
    37. ),
    38. actions: <Widget>[
    39. new IconButton(
    40. icon: const Icon(Icons.arrow_forward),
    41. tooltip: 'Next choice',
    42. onPressed: () { _nextPage(1); },
    43. ),
    44. ],
    45. bottom: new PreferredSize(
    46. preferredSize: const Size.fromHeight(48.0),
    47. child: new Theme(
    48. data: Theme.of(context).copyWith(accentColor: Colors.white),
    49. child: new Container(
    50. height: 48.0,
    51. alignment: Alignment.center,
    52. child: new TabPageSelector(controller: _tabController),
    53. ),
    54. ),
    55. ),
    56. ),
    57. body: new TabBarView(
    58. controller: _tabController,
    59. children: choices.map((Choice choice) {
    60. return new Padding(
    61. padding: const EdgeInsets.all(16.0),
    62. child: new ChoiceCard(choice: choice),
    63. );
    64. }).toList(),
    65. ),
    66. ),
    67. );
    68. }
    69. }
    70. class Choice {
    71. const Choice({ this.title, this.icon });
    72. final String title;
    73. final IconData icon;
    74. }
    75. const List<Choice> choices = const <Choice>[
    76. const Choice(title: 'CAR', icon: Icons.directions_car),
    77. const Choice(title: 'BICYCLE', icon: Icons.directions_bike),
    78. const Choice(title: 'BOAT', icon: Icons.directions_boat),
    79. const Choice(title: 'BUS', icon: Icons.directions_bus),
    80. const Choice(title: 'TRAIN', icon: Icons.directions_railway),
    81. const Choice(title: 'WALK', icon: Icons.directions_walk),
    82. ];
    83. class ChoiceCard extends StatelessWidget {
    84. const ChoiceCard({ Key key, this.choice }) : super(key: key);
    85. final Choice choice;
    86. @override
    87. Widget build(BuildContext context) {
    88. final TextStyle textStyle = Theme.of(context).textTheme.display1;
    89. return new Card(
    90. color: Colors.white,
    91. child: new Center(
    92. child: new Column(
    93. mainAxisSize: MainAxisSize.min,
    94. crossAxisAlignment: CrossAxisAlignment.center,
    95. children: <Widget>[
    96. new Icon(choice.icon, size: 128.0, color: textStyle.color),
    97. new Text(choice.title, style: textStyle),
    98. ],
    99. ),
    100. ),
    101. );
    102. }
    103. }
    104. void main() {
    105. runApp(new AppBarBottomSample());
    106. }

    也可以看看: