flutter笔记(十一) Scaffold以及功能组件介绍

4,587 阅读11分钟

flutter笔记汇总

Scaffold这个组件在之前的笔记都出现过,但是没有详细的说明,这一篇笔记就来介绍一下。
先看constructor

const Scaffold({
    Key key,
    PreferredSizeWidget appBar,  // 页面顶部标题栏
    Widget body,        // 页面的主体
    Widget floatingActionBotton,  // 悬浮按钮
    FloatingActionButtonLocation floatingActionButtonLocation,  //悬浮按钮的位置
    FloatingActionButtonAnimator floatingActionButtonAnimator,  //悬浮按钮的动画
    List<Widget> persistentFooterButtons,   //底部按钮
    Widget drawer,      //左侧抽屉
    Widget endDrawer,   //右侧抽屉
    Widget bottomNavigationBar,     //底部导航
    Widget bottomSheet,     //底部滑出
    Color backgroundColor,      //背景色
    bool resizeToAvoidBottomPadding,    //已弃用,键盘弹出时是否重新绘制,以避免输入框被遮挡
    bool resizeToAvoidBottomInset,      //键盘弹出时是否重新绘制,以避免输入框被遮挡
    bool primary: true,     //是否计算手机顶部状态栏的高度
    DragStartBehavior drawerDragStartBehavior: DragStartBehavior.start,  //拖动的处理
    bool extendBody: false,     //是否延伸body至底部。
    bool extendBodyBehindAppBar: false,     //是否延伸body至顶部。
    Color drawerScrimColor,     //抽屉遮罩层背景色
    double drawerEdgeDragWidth  //滑动拉出抽屉的生效距离
})

appBar

AppBar({
    Key key,
    Widget leading,
    bool automaticallyImplyLeading: true,
    Widget title,
    List<Widget>actions,
    Widget flexibleSpace,
    PreferredSizeWidget bottom,
    double elevation,
    ShapeBorder shape,
    Color backgroundColor,
    Brightness brightness,
    IconThemeData iconTheme,
    IconThemeData actionsIconTheme,
    TextTheme textTheme,
    bool primary: true,
    bool centerTitle,
    double titleSpacing: NavigationToolbar.kMiddleSpacing,
    double toolbarOpacity: 1.0,
    double bottomOpacity: 1.0
})

先看一张官方文档AppBar的图

可以看见,之前只用到AppBartitle,其实这个Widget是很强大的,接下来详细看一下。

leading

这个之前用过,但是是默认的,就是路由跳转之后,在AppBar左上角有一个返回按钮,就是它。
看demo

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          leading: MaterialButton(
            child: Text('L'),
            onPressed: () {
              print('pressed leading');
            },
          )
        ),
      )
    );
  }
}

左上角的按钮,在这里用了一个MaterialButton,点击的时候会打印出pressed leading。 效果如图

就是左上角的那个L

automaticallyImplyLeading

这个就牛逼了,如果leading为空,automaticallyImplyLeading值为true的话,就自动推断出leading是什么并创建,比如之前说的返回按钮,再比如加了个Drawerleading会是一个操作Drawer的按钮,这个就不在这写demo了,看后边。

title

这个就不细说了,之前的笔记里用过很多次了。

actions

leading在左侧,这个action在右侧,与leading不同的是,action是一个List类型,可以有多个子Widget

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          actions: <Widget>[
            MaterialButton(
              child: Text('BTN1')
            ),
            MaterialButton(
              child: Text('BTN2')
            )
          ],
        ),
      )
    );
  }
}

flexibleSpace

一个高度可以自适应的Widget,被标题和工具栏覆盖,看一下demo。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text(
            'title',
            style: TextStyle(
              color: Colors.red
            )
          ),
          flexibleSpace: FlexibleSpaceBar(
            title:  Text('flexibleSpace title')
          ),
        ),
      )
    );
  }
}

可以看见两个标题重叠了,并且title覆盖在了flexibleSpace title之上。 flexibleSpace一般用在SliverAppBar里,SliverAppBar是一个可以随着内容一起滚动的头部,大多数都是在滚动页面改变头部高度时用。

bottom

布局上和title一行的toolbar同级,但是是底对齐,和toolbar同时出现时会把toolbar顶上去。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          leading: MaterialButton(
            child: Text('H'),
            onPressed: () {

            },
          ),
          title: Text(
            'title',
            style: TextStyle(
              color: Colors.red
            )
          ),
          bottom: PreferredSize(
            child: Text('bottom')
          )
        ),
      )
    );
  }
}

可以看到titleleading所在的toolbar被顶上去了。

elevation

appBar下方的阴影,默认值是4。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text(
            'title'
          ),
          elevation: 10
        ),
      )
    );
  }
}

shape

设置appBar的形状,常规来说应用场景不会太多。。。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text(
            'title'
          ),
          shape: StadiumBorder(),
        ),
      )
    );
  }
}

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text(
            'title'
          ),
          shape: CircleBorder(),
        ),
      )
    );
  }
}

backgroundColor

背景色,没啥说的。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text(
            'title'
          ),
          backgroundColor: Colors.red,
        ),
      )
    );
  }
}

brightness

主题色调,两个值Brightness.darkBrightness.light,没看出什么效果,关于brightness,这里有介绍flutter笔记(六)-----按钮 各种Button

iconTheme

flutter笔记(五)-----图标Icon这里有介绍,不重复了。

actionsIconTheme

同上。

textTheme

iconTheme类似。

primary

appBar是否计算手机顶部状态栏的高度,默认为true,为false时如图

centerTitle

title是否居中,默认为true,为false是居左。

titleSpacing

title和其他元素的距离。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          leading: MaterialButton(
            child: Text('L'),
            onPressed: () {

            },
          ),
          title: Text(
            'title',
          ),
          actions: <Widget>[
            MaterialButton(
              child: Text('A'),
              onPressed: () {

              },
            ),
            MaterialButton(
              child: Text('B'),
              onPressed: () {

              },
            )
          ],
          bottom: PreferredSize(
            child: Text('bottom')
          ),
          titleSpacing: 70,
        ),
      )
    );
  }
}

设置过大自己内容显示不全,出现了...

toolbarOpacity

toolbar透明度,默认是1.0

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          leading: MaterialButton(
            child: Text('L'),
            onPressed: () {

            },
          ),
          title: Text(
            'title',
          ),
          actions: <Widget>[
            MaterialButton(
              child: Text('A'),
              onPressed: () {

              },
            ),
            MaterialButton(
              child: Text('B'),
              onPressed: () {

              },
            )
          ],
          bottom: PreferredSize(
            child: Text('bottom')
          ),
          toolbarOpacity: 0.5
        ),
      )
    );
  }
}

貌似只对title生效。

bottomOpacity

bottom的透明度。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          leading: MaterialButton(
            child: Text('L'),
            onPressed: () {

            },
          ),
          title: Text(
            'title',
          ),
          actions: <Widget>[
            MaterialButton(
              child: Text('A'),
              onPressed: () {

              },
            ),
            MaterialButton(
              child: Text('B'),
              onPressed: () {

              },
            )
          ],
          bottom: PreferredSize(
            child: Text('bottom')
          ),
          bottomOpacity: 0.5
        ),
      )
    );
  }
}

body

值是一个Widget,在顶部工具栏和底部菜单栏之间,可以是Container,可以是Text,也可以是Center或者其他。。。

floatingActionBotton

对于这个Widgetflutter笔记(六) 按钮 各种Button中有介绍,这里就不详细说了。

floatingActionButtonLocation

悬浮按钮的位置
FloatingActionButtonLocation.endTop:右上角
FloatingActionButtonLocation.centerFloat:下居中,不贴边
FloatingActionButtonLocation.centerDocked:下居中贴边
FloatingActionButtonLocation.endDocked:右下角贴底边
FloatingActionButtonLocation.endFloat:默认值,右下角不贴边
FloatingActionButtonLocation.startTop:左上角
FloatingActionButtonLocation.miniStartTop:左上角,离左侧边框更近一点。
这里需要注意,不管左上角还是右上角,全都是悬浮按钮的中心线在顶部边框的位置,也就是有一半按钮在屏外

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        floatingActionButton: FloatingActionButton(
          child: Text('BTN'),
        ),
        floatingActionButtonLocation: FloatingActionButtonLocation.startTop
      )
    );
  }
}

再看一下在底部贴边的效果

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        floatingActionButton: FloatingActionButton(
          child: Text('BTN'),
        ),
        floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked
      )
    );
  }
}

floatingActionButtonAnimator

操作floatingActionButton的动画效果,例如旋转、缩放、偏移。
具体怎么用还不知道。。。

persistentFooterButtons

固定在页面下方的一组按钮(不止可以放按钮也可以放其他Widget,按钮比较常见),不随页面滚动,右对齐,新增的Widget会在最右侧,这个不是底部导航。 水平方向空间足够时,按钮会水平排列,空间不足时会竖直排列。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        persistentFooterButtons: <Widget>[
          MaterialButton(
            child: Text('BTN1'),
            onPressed: () {
              print('pressed btn1');
            },
          ),
          MaterialButton(
            child: Text('BTN2'),
            onPressed: () {
              print('pressed btn2');
            },
          ),
          MaterialButton(
            child: Text('BTN3'),
            onPressed: () {
              print('pressed btn3');
            },
          ),
          MaterialButton(
            child: Text('BTN4'),
            onPressed: () {
              print('pressed btn4');
            },
          ),
          Container(
            width: 50,
            height: 50,
            color: Colors.red
          )
        ],
      )
    );
  }
}

drawer

最常见的左抽屉,自带半透明遮罩层,值可以是Container也可以是其他,如ButtonText
这里加上appBar会自动生成一个leading,同时向右滑动也可以拉出抽屉。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('title')
        ),
        drawer: Container(
          width: 150,
          color: Colors.blue
        )
      )
    );
  }
}

如果不想用appBar自带的leading,也可以自定义,flutter提供了ScaffoldState.openDrawer函数。

endDrawer

右侧抽屉,和左侧一样,两个抽屉可同时存在。

drawerDragStartBehavior

触发拖拽的时机,有两个值,默认为DragStartBehavior.start,还有DragStartBehavior.down,文档推荐用默认值,试了一下,没感觉两个有明显差别。

drawerScrimColor

抽屉遮罩层背景色,默认为Colors.black54

drawerEdgeDragWidth

滑动拉出抽屉的生效距离,设置为0时不能滑动拉出。

bottomNavigationBar

这里需要注意的是,值不只可以是BottomNavigationBar,其他Widget也可以,试了一下ListView,不会显示在底部,居上显示,失去了底部导航的意义。 底部导航,先看一下constructor

BottomNavigationBar({
    Key key,
    @required List<BottomNavigationBarItem> items,
    ValueChanged<int> onTap,
    int currentIndex: 0,
    double elevation: 8.0,
    BottomNavigationBarType type,
    Color fixedColor,
    Color backgroundColor,
    double iconSize: 24.0,
    Color selectedItemColor,
    Color unselectedItemColor,
    IconThemeData selectedIconTheme: const IconThemeData(),
    IconThemeData unselectedIconTheme: const IconThemeData(),
    double selectedFontSize: 14.0,
    double unselectedFontSize: 12.0,
    TextStyle selectedLabelStyle,
    TextStyle unselectedLabelStyle,
    bool showSelectedLabels: true,
    bool showUnselectedLabels: true
})

items

菜单的按钮

BottomNavigationBarItem({
    @required Widget icon,  //菜单按钮的图标
    Widget title,   //菜单按钮的标题
    Widget activeIcon,  //active状态下的图标
    Color backgroundColor   //背景色,需要type为shifting,否则没有效果
})
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Icon(
                Icons.home
              ),
              title: Text('HOME'),
            ),
            BottomNavigationBarItem(
              icon: Icon(
                Icons.android
              ),
              title: Text('ANDROID')
            )
          ],
        )
      )
    );
  }
}

接下来给HOME加一个activeIcon

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Icon(
                Icons.home
              ),
              title: Text('HOME'),
              activeIcon: Icon(
                Icons.done
              )
            ),
            BottomNavigationBarItem(
              icon: Icon(
                Icons.android
              ),
              title: Text('ANDROID'),
            )
          ],
        )
      )
    );
  }
}

onTap

点击回调,有个int类型的参数,为点击的索引。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Icon(
                Icons.home
              ),
              title: Text('HOME'),
            ),
            BottomNavigationBarItem(
              icon: Icon(
                Icons.android
              ),
              title: Text('ANDROID'),
            )
          ],
          onTap: (index){
            print(index);
          }
        )
      )
    );
  }
}

elevation

导航栏的阴影,关于阴影前边有介绍,这里就不重复了。

type

flutter的这个底部的导航栏,不只提供了一种形式,默认为fixed,就是前边的那种,还有一种shifting,下面来看一下。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Icon(
                Icons.home
              ),
              title: Text('HOME'),
              backgroundColor: Colors.red
            ),
            BottomNavigationBarItem(
              icon: Icon(
                Icons.android
              ),
              title: Text('ANDROID'),
            )
          ],
          onTap: (index){
            print(index);
          },
          type: BottomNavigationBarType.shifting,
        )
      )
    );
  }
}

可以看到BottomNavigationBarItem的背景色起作用了,只需要在一个设置了,整个导航栏都加上了,尝试了一下分别设置不同的颜色,只有第一个起作用。
图标和标题默认为白色,并且不显示标题,active状态下图标会放大,同时标题也会显示出来。

fixedColor

icontitle的颜色。

backgroundColor

标题栏的背景色,只在typefixed时起作用。
感觉这有点乱,fixedshifting背景色设置不统一。

iconSize

icon的大小

selectedItemColor & unselectedItemColor

这两个放一起,选中和未选中的颜色。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Icon(
                Icons.home
              ),
              title: Text('HOME'),
            ),
            BottomNavigationBarItem(
              icon: Icon(
                Icons.android
              ),
              title: Text('ANDROID'),
            )
          ],
          onTap: (index){
            print(index);
          },
          selectedItemColor: Colors.red,
          unselectedItemColor: Colors.green
        )
      )
    );
  }
}

红配绿

selectedIconTheme & unselectedIconTheme

这个和前边那两个颜色类似,是主题。

selectedFontSize & unselectedFontSize

选中和未选中的字体大小

selectedLabelStyle & unselectedLabelStyle

两种状态下的文字样式,关于文字的样式看这里flutter笔记(二) hello world和文本组件Text、TextSpan

showSelectedLabels & showUnselectedLabels

是否显示选中 / 为选中的title默认都是truefalse时不显示。

currentIndex

选中的索引

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Icon(
                Icons.home
              ),
              title: Text('HOME'),
            ),
            BottomNavigationBarItem(
              icon: Icon(
                Icons.android
              ),
              title: Text('ANDROID'),
            )
          ],
          currentIndex: 1
        )
      )
    );
  }
}

bottomSheet

底部滑出的组件。 先看一个最简单的demo

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        bottomSheet:Container(
          height: 100,
          color: Colors.red,
          child: Center(
            child: Text('bottomSheet')
          )
        ),
      )
    );
  }
}

这就完成了,这种东西用在哪里?微信聊天界面底部,是不是有个输入框和一个按钮,就可以用这个实现。
有人会问,这是固定在底部的,也不是滑出来的啊,往下看。
想要弹出的话需要用showBottomSheet或者showModalBottomSheet方法,区别就是一个没有半透明遮罩层,一个有半透明遮罩层并且点击遮罩层会关闭。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        body: Builder(
          builder: (BuildContext context){
            return Center(
              child: ListView(
                children: <Widget>[
                  MaterialButton(
                    child: Text('showBottomSheet'),
                    onPressed: () {
                      showBottomSheet(
                        context: context,
                        builder: (context) {
                          return Container(
                            height: 100,
                            color: Colors.red,
                            child: Center(
                              child: Text('bottomSheet')
                            )
                          );
                        }
                      );
                    }
                  ),
                  MaterialButton(
                    child: Text('showModalBottomSheet'),
                    onPressed: () {
                      showModalBottomSheet(
                        context: context,
                        builder: (context) {
                          return Container(
                            height: 100,
                            color: Colors.blue,
                            child: Center(
                                child: Text('bottomSheet')
                            )
                          );
                        }
                      );
                    },
                  )
                ],
              )
            );
          }
        )
      )
    );
  }
}

这里需要注意一点就是body需要用Builder包一下,否则会找不到Scaffold

backgroundColor

背景色。

resizeToAvoidBottomPadding & resizeToAvoidBottomInset

当键盘弹出时是否重新绘制以避免输入框被遮挡,默认为trueresizeToAvoidBottomPadding已弃用,目前还可以用。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        resizeToAvoidBottomPadding: true,
        body: ListView(
          children: <Widget>[
            Container(
              height: 400,
              color: Colors.blue
            ),
            TextField(),
            Container(
              height: 100,
              color: Colors.red
            )
          ],
        )
      )
    );
  }
}

primary

appBarprimary类似。

extendBody

如果设为true,当有bottomNavigationBar或者bottomSheetbody会延伸到下方,也就是说有一部分内容可能会被遮挡。

extendBodyBehindAppBar

extendBody类似,是否延伸到appBar下方。