美文网首页
Flutter 之 Scaffold (四十五)

Flutter 之 Scaffold (四十五)

作者: maskerII | 来源:发表于2022-05-02 21:59 被阅读0次

    1. Scaffold

    Scaffold 是一个路由页的骨架,我们可以使用它创建导航栏、抽屉菜单(Drawer)以及底部 Tab 导航菜单等

    Scaffold 定义

      const Scaffold({
        Key? key,
        this.appBar,
        this.body,
        this.floatingActionButton,
        this.floatingActionButtonLocation,
        this.floatingActionButtonAnimator,
        this.persistentFooterButtons,
        this.drawer,
        this.onDrawerChanged,
        this.endDrawer,
        this.onEndDrawerChanged,
        this.bottomNavigationBar,
        this.bottomSheet,
        this.backgroundColor,
        this.resizeToAvoidBottomInset,
        this.primary = true,
        this.drawerDragStartBehavior = DragStartBehavior.start,
        this.extendBody = false,
        this.extendBodyBehindAppBar = false,
        this.drawerScrimColor,
        this.drawerEdgeDragWidth,
        this.drawerEnableOpenDragGesture = true,
        this.endDrawerEnableOpenDragGesture = true,
        this.restorationId,
      })
    

    Scaffold 提供了比较常见的页面属性。

    Scaffold属性 介绍
    appBar 页面上方导航条
    body 页面容器
    floatingActionButton 悬浮按钮
    floatingActionButtonLocation 悬浮按钮位置
    floatingActionButtonAnimator 悬浮按钮动画
    persistentFooterButtons 显示在底部导航条上方的一组按钮
    drawer 左侧菜单
    onDrawerChanged 左侧菜单回调
    endDrawer 右侧菜单
    onEndDrawerChanged 右侧菜单回调
    bottomNavigationBar 底部导航条
    bottomSheet 一个持久停留在body下方,底部控件上方的控件
    backgroundColor 背景色
    resizeToAvoidBottomInset 默认为 true,防止一些小组件重复
    primary 是否在屏幕顶部显示Appbar, 默认为 true,Appbar 是否向上延伸到状态栏,如电池电量,时间那一栏
    drawerDragStartBehavior 控制 drawer 的一些特性
    extendBody 默认false body 是否延伸到底部控件
    extendBodyBehindAppBar 默认 false,为 true 时,body 会置顶到 appbar 后,如appbar 为半透明色,可以有毛玻璃效果
    drawerScrimColor 侧滑栏拉出来时,用来遮盖主页面的颜色
    drawerEdgeDragWidth 侧滑栏拉出来的宽度
    drawerEnableOpenDragGesture 默认 true 左侧侧滑栏是否可以滑动
    endDrawerEnableOpenDragGesture 默认 true 右侧侧滑栏是否可以滑动
    restorationId 恢复ID 用于保存和恢复Scaffold,不为空时,无论侧滑栏是否打开,Scaffold都将保存和恢复

    2. 基本使用

    class MSScaffoldDemo1 extends StatelessWidget {
      const MSScaffoldDemo1({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Scaffold Demo"),
            backgroundColor: Colors.orange,
          ),
          body: Center(child: Text("Scaffold")),
        );
      }
    }
    
    
    image.png

    3. FloatingActionButton

    class MSScaffoldDemo2 extends StatelessWidget {
      const MSScaffoldDemo2({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.pets),
            onPressed: () {},
          ),
          floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
        );
      }
    }
    
    
    image.png

    4. AppBar

    AppBar是一个Material风格的导航栏,通过它可以设置导航栏标题、导航栏菜单、导航栏底部的Tab标题等

    AppBar({
      Key? key,
      this.leading, //导航栏最左侧Widget,常见为抽屉菜单按钮或返回按钮。
      this.automaticallyImplyLeading = true, //如果leading为null,是否自动实现默认的leading按钮
      this.title,// 页面标题
      this.actions, // 导航栏右侧菜单
      this.bottom, // 导航栏底部菜单,通常为Tab按钮组
      this.elevation = 4.0, // 导航栏阴影
      this.centerTitle, //标题是否居中 
      this.backgroundColor,
      ...   //其它属性见源码注释
    })
    

    示例1

    
    class MSScaffoldDemo3 extends StatelessWidget {
      const MSScaffoldDemo3({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            leading: IconButton(
              icon: Icon(Icons.dashboard),
              onPressed: () {},
            ),
            title: Text("Scaffold Demo"),
            elevation: 10.0, // 导航栏阴影
            shadowColor: Colors.yellow,
            actions: [
              IconButton(
                icon: Icon(Icons.share),
                onPressed: () {},
              ),
            ],
            backgroundColor: Colors.pink,
          ),
        );
      }
    }
    
    
    image.png

    示例2

    
    class MSScaffoldDemo4 extends StatelessWidget {
      MSScaffoldDemo4({Key? key}) : super(key: key);
      List _tabs = ["新闻", "历史", "图片"];
      @override
      Widget build(BuildContext context) {
        return DefaultTabController(
          length: _tabs.length,
          child: Scaffold(
            appBar: AppBar(
              title: Text("Scaffold Demo"),
              bottom: TabBar(
                  tabs: _tabs.map((e) {
                return Container(height: 30, child: Text(e));
              }).toList()),
            ),
            body: TabBarView(
              children: _tabs.map((e) {
                return Center(
                  child: Text(e, textScaleFactor: 1.5),
                );
              }).toList(),
            ),
          ),
        );
      }
    }
    
    
    image.png

    5. 底部Tab导航栏 bottomNavigationBar

    示例1

    可以通过Scaffold的bottomNavigationBar属性来设置底部导航,如本节开始示例所示,我们通过Material组件库提供的BottomNavigationBar和BottomNavigationBarItem两种组件来实现Material风格的底部导航栏

    
    class MSScaffoldDemo5 extends StatefulWidget {
      const MSScaffoldDemo5({Key? key}) : super(key: key);
    
      @override
      State<MSScaffoldDemo5> createState() => _MSScaffoldDemo5State();
    }
    
    class _MSScaffoldDemo5State extends State<MSScaffoldDemo5> {
      var _currentIndex = 0;
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          bottomNavigationBar: BottomNavigationBar(
            currentIndex: _currentIndex,
            items: [
              BottomNavigationBarItem(icon: Icon(Icons.home), label: "首页"),
              BottomNavigationBarItem(icon: Icon(Icons.search), label: "搜索"),
            ],
            onTap: (value) {
              setState(() {
                _currentIndex = value;
              });
            },
          ),
          body: IndexedStack(
            index: _currentIndex,
            children: [
              MSHomePage(),
              MSSearchPage(),
            ],
          ),
        );
      }
    }
    
    class MSHomePage extends StatelessWidget {
      const MSHomePage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text("首页")),
          body: Center(
            child: Text("Home", textScaleFactor: 1.5),
          ),
        );
      }
    }
    
    class MSSearchPage extends StatelessWidget {
      const MSSearchPage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text("搜素")),
          body: Center(
            child: Text("Search", textScaleFactor: 1.5),
          ),
        );
      }
    }
    
    
    image.png

    示例2
    Material组件库中提供了一个BottomAppBar 组件,它可以和FloatingActionButton配合实现这种“打洞”效果

    
    class MSScaffoldDemo6 extends StatelessWidget {
      const MSScaffoldDemo6({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          bottomNavigationBar: BottomAppBar(
            color: Colors.white,
            shape: CircularNotchedRectangle(), // 底部导航栏打一个圆形的洞
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                IconButton(onPressed: () {}, icon: Icon(Icons.home)),
                SizedBox(), // 中间位置空出
                IconButton(onPressed: () {}, icon: Icon(Icons.search)),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {},
          ),
          floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
        );
      }
    }
    
    
    image.png

    打洞的位置取决于FloatingActionButton的位置
    BottomAppBar的shape属性决定洞的外形,CircularNotchedRectangle实现了一个圆形的外形

    6. persistentFooterButtons

    persistentFooterButtons 显示在底部导航条上方的一组按钮

    示例1

    
    class MSScaffoldDemo7 extends StatelessWidget {
      const MSScaffoldDemo7({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          persistentFooterButtons: [
            IconButton(onPressed: () {}, icon: Icon(Icons.music_note)),
            IconButton(onPressed: () {}, icon: Icon(Icons.list)),
            IconButton(onPressed: () {}, icon: Icon(Icons.movie)),
          ],
          bottomNavigationBar: BottomAppBar(
            color: Colors.orange,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                IconButton(
                    onPressed: () {},
                    icon: Icon(Icons.home),
                    padding: EdgeInsets.all(20)),
                IconButton(
                    onPressed: () {},
                    icon: Icon(Icons.search),
                    padding: EdgeInsets.all(20)),
              ],
            ),
          ),
        );
      }
    }
    
    
    image.png

    示例2

    
    class MSScaffoldDemo8 extends StatelessWidget {
      const MSScaffoldDemo8({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          persistentFooterButtons: [
            Row(
              children: [
                IconButton(onPressed: () {}, icon: Icon(Icons.music_note)),
                Expanded(child: SizedBox()),
                IconButton(onPressed: () {}, icon: Icon(Icons.movie)),
              ],
            )
          ],
          bottomNavigationBar: BottomAppBar(
            color: Colors.orange,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                IconButton(
                    onPressed: () {},
                    icon: Icon(Icons.home),
                    padding: EdgeInsets.all(20)),
                IconButton(
                    onPressed: () {},
                    icon: Icon(Icons.search),
                    padding: EdgeInsets.all(20)),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {},
          ),
          floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
        );
      }
    }
    
    
    image.png

    7. bottomSheet

    一个持久停留在body下方,底部控件上方的控件

    
    class MSScaffoldDemo9 extends StatelessWidget {
      const MSScaffoldDemo9({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
            color: Colors.white,
          ),
          bottomNavigationBar: BottomAppBar(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                IconButton(onPressed: () {}, icon: Icon(Icons.home)),
                IconButton(onPressed: () {}, icon: Icon(Icons.search)),
              ],
            ),
          ),
          bottomSheet: BottomSheet(
            enableDrag: false,
            elevation: 10.0,
            backgroundColor: Colors.yellow,
            builder: (ctx) {
              return Container(
                height: 60,
                color: Colors.cyan,
                child: Text('Bottom Sheet'),
                alignment: Alignment.center,
              );
            },
            onClosing: () {
              print("onClosing");
            },
          ),
        );
      }
    }
    
    
    image.png

    8. drawer / endDrawer

    drawer / endDrawer 可以通过点击左上角,右上角按键触发,也可以左滑,右滑触发。
    drawerEnableOpenDragGesture 默认为 true,设置 drawer 是否右滑触发
    endDrawerEnableOpenDragGesture 默认为 true,设置 endDrawer 是否左滑触发

    
    class MSScaffoldDemo10 extends StatelessWidget {
      const MSScaffoldDemo10({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Demo"),
            // 自定义 左侧按钮,弹出抽屉
            leading: Builder(builder: (ctx) {
              return IconButton(
                icon: Icon(Icons.people),
                onPressed: () {
                  Scaffold.of(ctx).openDrawer();
                },
              );
            }),
          ),
          body: Center(
            child: Text("Demo"),
          ),
          drawer: MSLeftDrawer(),
          endDrawer: MSRightDrawer(),
        );
      }
    }
    
    class MSLeftDrawer extends StatelessWidget {
      const MSLeftDrawer({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Drawer(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                children: [
                  Padding(
                    padding: EdgeInsets.all(15),
                    child: ClipOval(
                      child: Image.asset("assets/images/4.jpeg",
                          width: 50, height: 50, fit: BoxFit.cover),
                    ),
                  ),
                  Text("mshi", textScaleFactor: 1.5),
                ],
              ),
              Expanded(
                child: ListView(
                  children: [
                    ListTile(leading: Icon(Icons.account_box), title: Text("账号")),
                    ListTile(leading: Icon(Icons.settings), title: Text("设置")),
                  ],
                ),
              ),
            ],
          ),
        );
      }
    }
    
    class MSRightDrawer extends StatelessWidget {
      const MSRightDrawer({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Drawer(
          child: Column(
            children: [
              Padding(
                padding: const EdgeInsets.all(20.0),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Icon(Icons.favorite, color: Colors.red),
                    Text("我的收藏"),
                  ],
                ),
              ),
              ListView(
                shrinkWrap: true,
                physics: NeverScrollableScrollPhysics(),
                children: [
                  ListTile(leading: Icon(Icons.music_note), title: Text("音乐")),
                  ListTile(leading: Icon(Icons.movie_creation), title: Text("影视")),
                ],
              ),
            ],
          ),
        );
      }
    }
    
    
    76.gif

    注意

      1. 默认情况下,如果我们配置了AppBar和Drawer,AppBar左侧会显示一个默认按钮,点击按钮可以打开抽屉。但如果我们自己配置了AppBar的leading,就需要通过Scaffold.of(context).openDrawer()弹出抽屉。
      1. 直接把ListView 作为Column的子widget,会报错。hasSize。
        我们需要约束ListView的高度,有下面几种方式,1.使用Expanded,让ListView自己缩放。2. ListView的shrinkWrap 设置为true,让它包裹内容。3.将ListView 包裹在设定高度的Container中。

    参考:https://www.jianshu.com/p/a0fcb755a7b8
    https://book.flutterchina.club/chapter5/material_scaffold.html

    相关文章

      网友评论

          本文标题:Flutter 之 Scaffold (四十五)

          本文链接:https://www.haomeiwen.com/subject/bebpyrtx.html