Flutter (Material) - Scaffold

一个完整的路由页可能会包含导航栏、抽屉菜单(Drawer)以及底部 Tab 导航菜单等。如果每个路由页面都需要开发者自己手动去实现这些,这会是一件非常麻烦且无聊的事。幸运的是,Flutter Material 组件库提供了一些现成的组件来减少我们的开发任务。Scaffold 是一个路由页的骨架,我们使用它可以很容易地拼装出一个完整的页面。

1 Scaffold 的构成

const Scaffold({
    super.key,
    this.appBar,  // 顶部导航栏
    this.body,    // 内容也
    this.floatingActionButton,   // 一个悬浮的button
    this.floatingActionButtonLocation,  // 这个悬浮button 的定位 ,
    this.floatingActionButtonAnimator,  // 这个悬浮button 的动画 ,  
    this.persistentFooterButtons,  //纸墨设计中定义的固定在界面底部的常用按钮
    this.persistentFooterAlignment = AlignmentDirectional.centerEnd,//位置
    this.drawer,//抽屉  (左)
    this.onDrawerChanged,//抽屉  (左)切换的回调
    this.endDrawer,//抽屉  (右)
    this.onEndDrawerChanged,//抽屉  (右)切换的回调
    this.bottomNavigationBar, //底部导航栏
    this.bottomSheet,//位于底部的一个控件 ,返回Widget
    this.backgroundColor,//大约是 body的背景色
    this.resizeToAvoidBottomInset, // 弹出软键盘遮挡界面的相关操作
    //该脚手架是否显示在屏幕顶部。 
    // 如果为真,那么[appBar]的高度将被高度扩展  屏幕的状态栏,即[MediaQuery]的顶部填充
    this.primary = true,
    this.drawerDragStartBehavior = DragStartBehavior.start,
   	// 如果为true,和[bottomNavigationBar]或[persistentFooterButtons]
	//,然后[body]延伸到脚手架的底部,
	//而不是只延伸到[底部导航栏]的顶部
	//或者是[persistentFooterButtons]。
	//如果为true,则显示一个底部填充与高度匹配的[MediaQuery]小部件
	//[底部导航栏]将被添加到脚手架的[主体]上方。
	//当[bottomNavigationBar]有这个属性时,这个属性通常很有用
	//一种非矩形形状,如[CircularNotchedRectangle],其中
	//在工具条的上边缘添加一个[FloatingActionButton]大小的缺口。
	//在这种情况下,指定' extendBody: true '确保脚手架的
	//身体将通过底部导航栏的缺口可见。
    this.extendBody = false,
    //如果为true,并且指定了[appBar],则[body]的高度为 
	//扩展到包括应用程序栏的高度和身体的顶部 
	//与应用程序栏的顶部对齐。 
	//如果应用程序栏的[AppBar。不是 
	//完全不透明。 
	//此属性默认为false。它不能为空。
    this.extendBodyBehindAppBar = false,
    ///当抽屉打开时,用于模糊主要内容的棉布的颜色。 
	///如果这是null,则[DrawerThemeData. com]使用scrimColor]。如果这 
	///也是null,然后默认为[Colors.black54]。
    this.drawerScrimColor,
    ///横向滑动将打开的区域的宽度 
	///默认情况下,使用的值是20.0添加到的填充边 
	/// MediaQuery.of(上下文)。与周围环境相对应的填充 
	///[TextDirection]。这确保了带槽设备的拖动区域是 
	///不被遮蔽。例如,如果' TextDirection.of(context) '设置为 
	///[TextDirection。Ltr], 20.0将添加到 
	///' MediaQuery.of(上下文).padding.left”。
    this.drawerEdgeDragWidth,
    ///确定[脚手架。可以用拖动器打开的抽屉 
	///移动手势。 
	///在桌面平台上,抽屉是不可拖动的。 
	///默认情况下,拖动手势在移动端是启用的。
    this.drawerEnableOpenDragGesture = true,
    ///确定[脚手架。尾抽屉]可以用一个 
	///移动手势。 
	///在桌面平台上,抽屉是不可拖动的。 
	///默认情况下,拖动手势在移动端是启用的。
    this.endDrawerEnableOpenDragGesture = true,
    this.restorationId,
  }) :

2 AppBar
属性有很多 主要用到属性有下面这几个

AppBar({
    super.key,
    this.leading,  // 左边的按钮 , 返回 ,或 菜单栏按钮
    this.automaticallyImplyLeading = true, // 按钮相关(和drawer有关联)
    this.title,  // 标题
    this.actions, // 右边的按钮组  IconButton 或 PopupMenuButton
    this.flexibleSpace,   // Widget - 一个显示在 AppBar 下方的控件,高度和 AppBar 高度一样,可以实现一些特殊的效果,该属性通常在 SliverAppBar 中使用。
    this.bottom,   // AppBar的下方的 导航栏 ,(例 全部,未读,已读)
    this.elevation,// double - 控件的 z 坐标顺序,默认值为 4,对于可滚动的 SliverAppBar,当 SliverAppBar 和内容同级的时候,该值为 0, 当内容滚动 SliverAppBar 变为 Toolbar 的时候,修改 elevation 的值。
    this.scrolledUnderElevation,
    this.notificationPredicate = defaultScrollNotificationPredicate,
    this.shadowColor,//阴影颜色
    this.surfaceTintColor,
    this.shape,
     this.foregroundColor,
    this.backgroundColor,//Color - Appbar 的颜色,默认值为 ThemeData.primaryColor。改值通常和下面的三个属性一起使用。
    this.brightness,// Brightness - Appbar 的亮度,有白色和黑色两种主题,默认值为 ThemeData.primaryColorBrightness。
    this.iconTheme,// IconThemeData - Appbar 上图标的颜色、透明度、和尺寸信息。默认值为 ThemeData.primaryIconTheme。
    this.textTheme,//TextTheme - Appbar 上的文字样式。
    this.actionsIconTheme,
    this.primary = true,
    this.centerTitle,
    this.excludeHeaderSemantics = false,
    this.titleSpacing,
    this.toolbarOpacity = 1.0,
    this.bottomOpacity = 1.0,
    this.toolbarHeight,
    this.leadingWidth,
    this.backwardsCompatibility,
    this.toolbarTextStyle,
    this.titleTextStyle,
    this.systemOverlayStyle,
  }) :

AppBar 部分属性标注 及下方示例 转载自

作者:iwakevin
链接:https://www.jianshu.com/p/77f8b7ee8460
来源:简书

在这里插入图片描述
在这里插入图片描述

// 返回每个隐藏的菜单项
SelectView(IconData icon, String text, String id) {
    return new PopupMenuItem<String>(
        value: id,
        child: new Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
                new Icon(icon, color: Colors.blue),
                new Text(text),
            ],
        )
    );
}

appBar: new AppBar(
    title: new Text('首页'),
    leading: new Icon(Icons.home),
    backgroundColor: Colors.blue,
    centerTitle: true,
    actions: <Widget>[
        // 非隐藏的菜单
        new IconButton(
            icon: new Icon(Icons.add_alarm),
            tooltip: 'Add Alarm',
            onPressed: () {}
        ),
        // 隐藏的菜单
        new PopupMenuButton<String>(
            itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
                this.SelectView(Icons.message, '发起群聊', 'A'),
                this.SelectView(Icons.group_add, '添加服务', 'B'),
                this.SelectView(Icons.cast_connected, '扫一扫码', 'C'),
            ],
            onSelected: (String action) {
                // 点击选项的时候
                switch (action) {
                    case 'A': break;
                    case 'B': break;
                    case 'C': break;
                }
            },
        ),
    ],
),

3 drawer endDrawer 抽屉 ,或 菜单栏

上面这两个字段的返回是Widget 但一般与Drawer 关联使用

const Drawer({
    super.key,
    this.backgroundColor,
    this.elevation,
    this.shape,
    this.width,
    this.child,
    this.semanticLabel,
  }) : assert(elevation == null || elevation >= 0.0);

使用技巧

1 MediaQuery.removePaddin 

 factory MediaQuery.removePadding({
    Key? key,
    required BuildContext context,
    bool removeLeft = false,
    bool removeTop = false,
    bool removeRight = false,
    bool removeBottom = false,
    required Widget child,
  })  

        removeTop: true,//移除抽屉菜单顶部默认留白

在这里插入图片描述

class MyDrawer extends StatelessWidget {
  const MyDrawer({
    Key? key,
  }) : super(key: key);

  
  Widget build(BuildContext context) {
    return Drawer(
      child: MediaQuery.removePadding(
        context: context,
        //移除抽屉菜单顶部默认留白
        removeTop: true,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(top: 38.0),
              child: Row(
                children: <Widget>[
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16.0),
                    child: ClipOval(
                      child: Image.asset(
                        "assets/images/caver.webp",
                        width: 60,
                        height: 60,
                      ),
                    ),
                  ),
                  Text(
                    "名称",
                    style: TextStyle(fontWeight: FontWeight.bold),
                  )
                ],
              ),
            ),
            Expanded(
              child: ListView(
                children: <Widget>[
                  ListTile(
                    leading: const Icon(Icons.add),
                    title: const Text('Add account'),
                  ),
                  ListTile(
                    leading: const Icon(Icons.settings),
                    title: const Text('Manage accounts'),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

4 bottomNavigationBar 底部导航栏

在这里插入图片描述

BottomNavigationBar(
        items: <BottomNavigationBarItem>[
          BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
          BottomNavigationBarItem(icon: Icon(Icons.business), label: "Business"),
          BottomNavigationBarItem(icon: Icon(Icons.school), label: "School"),
        ],
        fixedColor: Colors.blue,
        currentIndex: _selectedIndex,
        onTap: _onItemTapped,
      ),
      // floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,

在这里插入图片描述

重点 
  floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
     shape: CircularNotchedRectangle(), // 底部导航栏打一个圆形的洞
  bottomNavigationBar:
      BottomAppBar(
        color: Colors.white,
        shape: CircularNotchedRectangle(), // 底部导航栏打一个圆形的洞

        child: Row(
          children: [
            IconButton(icon: Icon(Icons.home),onPressed: (){

            },),
            SizedBox(), //中间位置空出
            IconButton(icon: Icon(Icons.business),onPressed: (){},),
          ],
          mainAxisAlignment: MainAxisAlignment.spaceAround, //均分底部导航栏横向空间
        ),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,

在这里插入图片描述

 persistentFooterButtons: [//纸墨设计中定义的固定在界面底部的常用按钮
        IconButton(icon: Icon(Icons.business),onPressed: (){},),
        IconButton(icon: Icon(Icons.business),onPressed: (){},),
        IconButton(icon: Icon(Icons.business),onPressed: (){},),
      ],
      persistentFooterAlignment: AlignmentDirectional.topCenter,

在这里插入图片描述

红色的那一块是 
 bottomSheet: Container(
        width: double.infinity,
        height: 200,
        color: Colors.red,
      ),
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

class MyScaffoldState extends StatefulWidget {
  const MyScaffoldState({Key? key}) : super(key: key);

  
  State<MyScaffoldState> createState() => _MyState();
}

class _MyState extends State<MyScaffoldState> {
  int _selectedIndex = 1;

  
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        // leading: IconButton(icon: Icon(Icons.arrow_back_ios),onPressed: (){},),
        title: const Text("MyScaffoldState"),
        centerTitle: true,
        actions: <Widget>[
          IconButton(onPressed: (){}, icon: Icon(Icons.share)),
          IconButton(onPressed: (){}, icon: Icon(Icons.share)),
        ],
      ),
      drawer: MyDrawer(), //抽屉
      bottomNavigationBar:
      //
      BottomNavigationBar(
        items: <BottomNavigationBarItem>[
          BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
          BottomNavigationBarItem(icon: Icon(Icons.business), label: "Business"),
          BottomNavigationBarItem(icon: Icon(Icons.school), label: "School"),
        ],
        fixedColor: Colors.blue,
        currentIndex: _selectedIndex,
        onTap: _onItemTapped,
      ),
      // floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,

      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: _onAdd,

      ),
      primary: true,

      persistentFooterButtons: [//纸墨设计中定义的固定在界面底部的常用按钮
        IconButton(icon: Icon(Icons.business),onPressed: (){},),
        IconButton(icon: Icon(Icons.business),onPressed: (){},),
        IconButton(icon: Icon(Icons.business),onPressed: (){},),
      ],
      persistentFooterAlignment: AlignmentDirectional.topCenter,
      bottomSheet: Container(
        width: double.infinity,
        height: 200,
        color: Colors.red,
      ),
      backgroundColor: Colors.yellow[100],
      body: ConstrainedBox(
        constraints: BoxConstraints.tightFor(width: double.infinity),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text("XXXXX"),
          ],
        ),
      ),
    );

  }

  void _onItemTapped(int index){
    setState(() {
      _selectedIndex = index;
    });
  }
  void _onAdd(){

  }



}

class MyDrawer extends StatelessWidget {
  const MyDrawer({
    Key? key,
  }) : super(key: key);

  
  Widget build(BuildContext context) {
    return Drawer(
      child: MediaQuery.removePadding(
        context: context,
        //移除抽屉菜单顶部默认留白
        removeTop: true,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(top: 38.0),
              child: Row(
                children: <Widget>[
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16.0),
                    child: ClipOval(
                      child: Image.asset(
                        "assets/images/caver.webp",
                        width: 60,
                        height: 60,
                      ),
                    ),
                  ),
                  Text(
                    "名称",
                    style: TextStyle(fontWeight: FontWeight.bold),
                  )
                ],
              ),
            ),
            Expanded(
              child: ListView(
                children: <Widget>[
                  ListTile(
                    leading: const Icon(Icons.add),
                    title: const Text('Add account'),
                  ),
                  ListTile(
                    leading: const Icon(Icons.settings),
                    title: const Text('Manage accounts'),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

//
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值