Flutter容器(6):页面骨架(Scaffold)

news/2024/10/11 16:13:15

Material 组件库提供了丰富多样的组件,这里介绍一下最常用的 Scaffold 组件,其余的读者可以自行查看文档或 Flutter Gallery 中 Material 组件部分的示例。

注意:Flutter Gallery 是 Flutter 官方提供的 Flutter Demo,源码位于 flutter 源码中的 examples 目录下,笔者强烈建议用户将 Flutter Gallery 示例跑起来,它是一个很全面的 Flutter 示例应用,是非常好的参考 Demo,也是笔者学习 Flutter 的第一手资料。

一、Scaffold

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

案例

我们实现一个页面,它包含:

  1. 一个导航栏
  2. 导航栏右边有一个分享按钮
  3. 有一个抽屉菜单
  4. 有一个底部导航
  5. 右下角有一个悬浮的动作按钮

最终效果如下图所示:

Flutter_sacn_A.png


Flutter_sacn_B.png


实现代码如下:

// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(body: ScaffoldRoute(),),);}
}// 主页面
class MyHomeBody extends StatelessWidget {const MyHomeBody({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return ScaffoldRoute();}
}class ScaffoldRoute extends StatefulWidget {const ScaffoldRoute({Key? key}) : super(key: key);@overrideState<ScaffoldRoute> createState() => _ScaffoldRouteState();
}class _ScaffoldRouteState extends State<ScaffoldRoute> {int _selectedIndex = 1;@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(//导航栏title: Text("App Name"),actions: <Widget>[//导航栏右侧菜单IconButton(icon: Icon(Icons.share), onPressed: () {}),],),drawer: MyDrawer(), //抽屉bottomNavigationBar: BottomNavigationBar(// 底部导航items: const <BottomNavigationBarItem>[BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),BottomNavigationBarItem(icon: Icon(Icons.business), label: 'Business'),BottomNavigationBarItem(icon: Icon(Icons.school), label: 'School'),],currentIndex: _selectedIndex,fixedColor: Colors.blue,onTap: _onItemTapped,),floatingActionButton: FloatingActionButton(//悬浮按钮onPressed: _onAdd,child: Icon(Icons.add),),);}void _onItemTapped(int index) {setState(() {_selectedIndex = index;});}void _onAdd() {}
}class MyDrawer extends StatelessWidget {const MyDrawer({Key? key,}) : super(key: key);@overrideWidget 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/head.png",width: 80,),),),Text("Wendux",style: TextStyle(fontWeight: FontWeight.bold),)],),),Expanded(child: ListView(children: const <Widget>[ListTile(leading: Icon(Icons.add),title: Text('Add account'),),ListTile(leading: Icon(Icons.settings),title: Text('Manage accounts'),),],),),],),),);}
}

上面代码中我们用到了如下组件:

组件名称 解释
AppBar 一个导航栏骨架
MyDrawer 抽屉菜单
BottomNavigationBar 底部导航栏
FloatingActionButton 漂浮按钮

下面我们来分别介绍一下它们。

二、AppBar

AppBar是一个 Material 风格的导航栏,通过它可以设置导航栏标题、导航栏菜单、导航栏底部的 Tab 标题等。下面我们看看 AppBar 的定义:

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,...   //其他属性见源码注释
})

如果给Scaffold添加了抽屉菜单,默认情况下Scaffold会自动将AppBarleading设置为菜单按钮(如上图所示),点击它便可打开抽屉菜单。如果我们想自定义菜单图标,可以手动来设置leading,如:

Scaffold(appBar: AppBar(title: Text("App Name"),leading: Builder(builder: (context) {return IconButton(icon: Icon(Icons.dashboard, color: Colors.white), //自定义图标onPressed: () {// 打开抽屉菜单  Scaffold.of(context).openDrawer(); },);}),...  ) 

修改后的代码运行效果如下图所示:

Flutter_sacn_C.png


可以看到左侧菜单已经替换成功。

代码中打开抽屉菜单的方法在ScaffoldState中,通过Scaffold.of(context)可以获取父级最近的Scaffold 组件的State对象。

三、抽屉菜单Drawer

ScaffolddrawerendDrawer属性可以分别接受一个Widget来作为页面的左、右抽屉菜单。如果开发者提供了抽屉菜单,那么当用户手指从屏幕左(或右)侧向里滑动时便可打开抽屉菜单。本节开始部分的示例中实现了一个左抽屉菜单MyDrawer,它的源码如下:

class MyDrawer extends StatelessWidget {const MyDrawer({Key? key,}) : super(key: key);@overrideWidget 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/head.png",width: 80,),),),Text("Wendux",style: TextStyle(fontWeight: FontWeight.bold),)],),),Expanded(child: ListView(children: const <Widget>[ListTile(leading: Icon(Icons.add),title: Text('Add account'),),ListTile(leading: Icon(Icons.settings),title: Text('Manage accounts'),),],),),],),),);}
}

抽屉菜单通常将Drawer组件作为根节点,它实现了 Material 风格的菜单面板,MediaQuery.removePadding可以移除 Drawer 默认的一些留白(比如 Drawer 默认顶部会留和手机状态栏等高的留白),读者可以尝试传递不同的参数来看看实际效果。抽屉菜单页由顶部和底部组成,顶部由用户头像和昵称组成,底部是一个菜单列表,用 ListView 实现。

三、FloatingActionButton

FloatingActionButton是Material设计规范中的一种特殊Button,通常悬浮在页面的某一个位置作为某种常用动作的快捷入口,如本节示例中页面右下角的"➕"号按钮。我们可以通过ScaffoldfloatingActionButton属性来设置一个FloatingActionButton,同时通过floatingActionButtonLocation属性来指定其在页面中悬浮的位置,这个比较简单,不再赘述。

四、底部Tab导航栏

底部Tab导航栏

我们可以通过ScaffoldbottomNavigationBar属性来设置底部导航,如本节开始示例所示,我们通过 Material 组件库提供的BottomNavigationBarBottomNavigationBarItem两种组件来实现Material风格的底部导航栏。可以看到上面的实现代码非常简单,所以不再赘述,但是如果我们想实现如下图所示效果的底部导航栏应该怎么做呢?

Flutter_sacn_D.png


Material组件库中提供了一个BottomAppBar 组件,它可以和FloatingActionButton配合实现这种“打洞”效果,源码如下:

BottomAppBar(color: Colors.white,shape: CircularNotchedRectangle(), // 底部导航栏打一个圆形的洞child: Row(mainAxisAlignment: MainAxisAlignment.spaceAround, //均分底部导航栏横向空间children: const [IconButton(icon: Icon(Icons.home),onPressed: null,),SizedBox(), //中间位置空出IconButton(icon: Icon(Icons.business),onPressed: null,),],),
),

六、页面 body

最后就是页面的 Body 部分了,Scaffold 有一个 body 属性,接收一个 Widget,我们可以传任意的 Widget ,比如 TabBarView,它是一个可以进行页面切换的组件,在多 Tab 的 App 中,一般都会将 TabBarView 作为 Scaffold 的 Body。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/70260.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Golang上下文context

上篇内容我们主要讲解了net/http标准库的使用,其中包含如何创建POST请求、GET请求以及如何携带参数的请求。 Context介绍 context释义为上下文,在我们使用goroutine时一般使用context来进行元数据的传递,非元数据不建议使用context来进行传递。那么我们主要是用context用来做…

2024 最新 IntelliJ IDEA 2024.1.6 激活(亲测可用)

注意:接下来本文分享免费激活 IDEA 等Jetbrains全家桶工具,一直支持到最新版本2024.1.6。 1.下载安装IDEA (mac、window、linux都支持) 大家直接在官网下载最新版本,登陆官网,下载最新版本2024.1.4。一步一步确定安装,然后打开 这里提示输入激活码,先关闭应用!!!2.…

淘宝程序员没活硬整?在 Excel 和 VSCode 中购物!

你可以在 Excel 表格中挑选商品进行购物,还原度极高,这两个图表更是点睛之笔。哪个天才想出来的,把特么广告都整成了 Excel 图表。大家好,我是程序员鱼皮,最近某宝网站的改进,属实是有点 “新” 了。 你敢相信这是一个购物网站么?你可以在 Excel 表格中挑选商品进行购物…

Golang模板template

背景概述 当我们在进行json字段选取以及渲染时,我们经常会见到{{}},其实这就是我们今天要讲解的模板即是template。例如prometheusAlert中的模板就是使用了改语法。 必备技能字段选取 ❝ {{ . }} 表示json的所有域,例如:{"name":"anruo","age&quo…

如何在springboot中,全局配置produces=text/plain;charset=UTF-8

为什么要使用produces="text/plain;charset=UTF-8"? 当不用这个配置时,接口返回的数据,是有斜杠的 配置后,就正常了 以前我的配置方式,是在每个接口上,都添加上produces="text/plain;charset=UTF-8"。但是这样显示不太好,每个接口都加的话,会比较…

鸿蒙发送消息通知

注意:发送消息通知要开启设置中的消息通知import notify from @ohos.notificationManager import image from @ohos.multimedia.image import { BusinessError } from @kit.BasicServicesKit@Entry @Component struct NotificationPage {// 全局任务ididx: number = 100// 图象…

域名解析错误是不是被限制了?

在我们畅游互联网的过程中,有时会遭遇域名解析错误的情况,这无疑会给我们的上网体验带来困扰。而很多人在遇到域名解析错误时,不禁会疑惑:这是不是意味着被限制了呢? 首先,域名解析错误并不一定意味着被限制。 域名解析是将域名转换为对应的IP地址的过程,就如同在电话簿…