Flutter常用的布局和事件示例详解

Flutter 项目中常用的布局详情,及封装和使用,快速开发项目.

以及手势事件和滚动事件的使用

Scaffold 导航栏的实现,有些路由页可能会有抽屉菜单(Drawer)以及底部Tab导航菜单等

const Scaffold({
 Key key,
 this.appBar,//标题栏
 this.body,//内容
 this.floatingActionButton,//悬浮按钮
 this.persistentFooterButtons,//底部持久化现实按钮
 this.drawer,//侧滑菜单左
 this.endDrawer,//侧滑菜单右
 this.bottomNavigationBar,//底部导航
 this.backgroundColor,//背景颜色
 this.resizeToAvoidBottomPadding: true,//自动适应底部padding
 this.primary: true,//使用primary主色
 })

Flutter 中自带的material样式的标题栏,首先看一下AppBar具有哪些属性,代码如下:

AppBar({
 Key key,
 this.leading,//主导Widget
 this.automaticallyImplyLeading: true,
 this.title,//标题
 this.actions,//其他附加功能
 this.flexibleSpace,//伸缩空间,显示在title上面
 this.bottom,//显示在title下面
 this.elevation: 4.0,//阴影高度
 this.backgroundColor,//背景颜色
 this.brightness,//明暗模式
 this.iconTheme,//icon主题
 this.textTheme,//text主题
 this.primary: true,//是否是用primary
 this.centerTitle,//标题是否居中
 this.titleSpacing: NavigationToolbar.kMiddleSpacing,//title与leading的间隔
 this.toolbarOpacity: 1.0,//title级文字透明度
 this.bottomOpacity: 1.0,//底部文字透明度
 })

悬浮button 属性详解

const FloatingActionButton({
 Key key,
 this.child,//button的显示样式
 this.tooltip,//提示,长按按钮提示文字
 this.backgroundColor,//背景颜色
 this.heroTag: const _DefaultHeroTag(),//页面切换动画Tag
 this.elevation: 6.0,//阴影
 this.highlightElevation: 12.0,//高亮阴影
 @required this.onPressed,//点击事件
 this.mini: false//是否使用小图标
 })

底部导航栏BottomNavigationBar的实现,与经常搭配的PageView实现项目中常用的tab切换

Scaffold(
 body: PageView(
 controller: _controller,
 children: <Widget>[//page的页面
 HomePage(),
 SearchPage(),
 TravelPage(),
 MinePage(),
 ],
 onPageChanged: (int index) {//滑动page的监听
 setState(() {//改变tab状态
 _controllerIndex = index;
 });
 },
 ),
 bottomNavigationBar: BottomNavigationBar(
 currentIndex: _controllerIndex, //当前的index
 onTap: (index) {//点击tab
 _controller.jumpToPage(index); //跳转到具体的页面
 //注意改变_controllerIndex的状态
 setState(() {
 _controllerIndex = index;
 });
 },
 type: BottomNavigationBarType.fixed,//固定
 items: [//底部tab图片、字体及颜色
 homeItem(),
 searchItem(),
 travelItem(),
 mineItem(),
 ]),
 );

BottomNavigationBarItem的实现

BottomNavigationBarItem mineItem() {
 return BottomNavigationBarItem(
 icon: Icon(
 //定义默认状态下的图片以及颜色
 Icons.supervised_user_circle,
 color: _defaultColor,
 ),
 activeIcon: Icon(
 //定义选中状态下的图片以及颜色
 Icons.supervised_user_circle,
 color: _activityColor,
 ),
 title: Text(
 //定义文字
 '我的',
 style: TextStyle(
 color: _controllerIndex != 3 ? _defaultColor : _activityColor,
 ),
 ));
}

Container

Container({
 	Key key,
 	this.alignment,//内部widget对齐方式
 this.padding,//内边距
 	Color color,//背景颜色,与decoration只能存在一个
 Decoration decoration,//背景装饰,与decoration只能存在一个
 this.foregroundDecoration//前景装饰,
 double width,//容器的宽
 	double height,//容器的高
 	BoxConstraints constraints//,
 	this.margin,//外边距
 	this.transform,//倾斜
 	this.child,//子widget
})

alignment: 内部Widget对齐方式,左上对齐topLeft、垂直顶部对齐,水平居中对齐topCenter、右上topRight、垂直居中水平左对齐centerLeft、居中对齐center、垂直居中水平又对齐centerRight、底部左对齐bottomLeft、底部居中对齐bottomCenter、底部右对齐bottomRight

padding: 内间距,子Widget距Container的距离。

color: 背景颜色

decoration: 背景装饰

foregroundDecoration: 前景装饰

width:容器的宽

height:容器的高

constraints:容器宽高的约束,容器最终的宽高最终都要受到约束中定义的宽高影响

margin:容器外部的间隔

transform: Matrix4变换

child:内部子Widget

可以通过decoration装饰器实现圆角和边框,渐变等

decoration: BoxDecoration(
 border: Border(
 bottom:
 BorderSide(width: 1, color: Color(0xfff2f2f2))), //设置底部分割线
 ),
 borderRadius: BorderRadius.circular(12), //设置圆角
 gradient: LinearGradient(
 colors: [
 Color(0xffff4e63),
 Color(0xffff6cc9),
 ],
 begin: Alignment.centerLeft,
 end: Alignment.centerRight,
 ), //
 )

设置网络图片

Image.network(
 salesBoxModel.icon,
 fit: BoxFit.fill,
 height: 15,
 ),

设置行布局

Column({
 Key key,
 MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,//主轴X 排列方式
 MainAxisSize mainAxisSize = MainAxisSize.max,
 CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,//纵轴排列方式
 TextDirection textDirection,
 VerticalDirection verticalDirection = VerticalDirection.down,
 TextBaseline textBaseline,
 List<Widget> children = const <Widget>[],
 })

设置列布局

Row({
 Key key,
 MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
 MainAxisSize mainAxisSize = MainAxisSize.max,
 CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
 TextDirection textDirection,
 VerticalDirection verticalDirection = VerticalDirection.down,
 TextBaseline textBaseline,
 List<Widget> children = const <Widget>[],
 })

设置内边距Padding

Padding 也是一个Widget,它内部可以包裹一个Widget

Padding(
 padding: EdgeInsets.only(top: 10),
 child: Text(
 model.title,
 style: TextStyle(
 fontSize: 14,
 color: Colors.white,
 ),
 ),
 )

设置宽度/高度撑满父布局FractionallySizedBox

FractionallySizedBox({
 Key key,
 this.alignment = Alignment.center,
 this.widthFactor,//设置为1 则宽度撑满父布局
 this.heightFactor,//设置为1 则高度撑满父布局
 Widget child,//包裹的子Widget
 })

Expanded撑满整个界面

Expanded({
 Key key,
 int flex = 1,
 @required Widget child,
 })

Stack 可以理解为栈布局,先放入的显示在最下面,后放入的显示在上面,跟Android中的ReaviteLayout相似

Stack({
 Key key,
 this.alignment: AlignmentDirectional.topStart,//对齐方式
 this.textDirection,
 this.fit: StackFit.loose,//是否按照父类宽高处理自己大小
 this.overflow: Overflow.clip,//溢出处理方式
 List<Widget> children: const <Widget>[],
 })

我们可以用Stack来实现:请求网络中的时候,显示加载中的布局;请求网络成功后,隐藏加载中的布局,显示成功的布局.
自定义一个LoadingWidget,传递isLoading是否正在加载中,child加载成功后显示的布局.这样的好处就是我们可以在任何需要用到加载中的布局时,直接使用,统一管理.使用setState来改变isLoading,来实现状态的改变.

class LoadingWidget extends StatelessWidget {
 final bool isLoading;
 final bool cover;
 final Widget child;

 //required必须传递的参数
 const LoadingWidget(
 {Key key,
 @required this.isLoading,
 this.cover = false,
 @required this.child})
 : super(key: key);

 @override
 Widget build(BuildContext context) {
 return !cover
 ? !isLoading ? child : _loadingView
 : Stack(
 children: <Widget>[child, isLoading ? _loadingView : null],
 );
 }

 Widget get _loadingView {
 return Center(
 child: CircularProgressIndicator(), //圆形的进度条
 );
 }
}

看一个简单调用的例子.

class _HomePageState extends State<HomePage> {
bool isLoading = true;//默认是加载中的状态
 @override
 void initState() {
 super.initState();
 _handleRefresh();
 }

 Future<Null> _handleRefresh() async {
 try {
 HomeModel model = await HomeDao.fetch();
 setState(() {
 gridNavList = model.localNavList;
 girdModeList = model.gridNav;
 subNavList = model.subNavList;
 salesBoxModel = model.salesBox;
 bannerList = model.bannerList;
 isLoading = false;
 });
 } catch (e) {
 print(e.toString());
 setState(() {
 isLoading = false;
 });
 }
 return null;
 }

 @override
 Widget build(BuildContext context) {
 return Scaffold(
 backgroundColor: Color(0xfff2f2f2),
 body: LoadingWidget(//使用自定义的布局
 isLoading: isLoading,
 //加载成功后显示的View
 child: Stack(
 .......
 )
 )
 );
 }
}

当然,Stack还有很多其他的使用场景,可自行翻阅文档Stack

IndexedStack

只不过IndexedStack只显示指定位置的Widget,其他的位置的Widget不会显示。

PageView 类似Android中的ViewPage组件,他还可以实现底部导航栏的效果
Flutter官网PageView

首先看一下PageView有哪些属性,代码如下:

PageView({
 Key key,
 this.scrollDirection = Axis.horizontal,
 this.reverse = false,
 PageController controller,
 this.physics,
 this.pageSnapping = true,
 this.onPageChanged,
 List<Widget> children = const <Widget>[],
 this.dragStartBehavior = DragStartBehavior.down,
 }) : controller = controller ?? _defaultPageController,
 childrenDelegate = SliverChildListDelegate(children),
 super(key: key);

来看一下各个属性的意思

this.scrollDirection = Axis.horizontal,Axis.vertical//设置滚动方向 横向和竖向

pageSnapping true 带有阻力的滑动,如果设置为false滑动到哪就停止到哪

controller 页面控制器,通过调用jumpToPage 实现页面的跳转

BottomNavigationBar

BottomNavigationBar({
 Key key,
 @required this.items,
 this.onTap,//点击事件
 this.currentIndex = 0,//当前的位置
 BottomNavigationBarType type,//底部固定和隐藏类型
 this.fixedColor,
 this.iconSize = 24.0,//图片的大小
 })

final List<BottomNavigationBarItem> items;

BottomNavigationBarItem 定义底部的icon 选中的icon 文字

const BottomNavigationBarItem({
 @required this.icon,
 this.title,
 Widget activeIcon,
 this.backgroundColor,
 }) : activeIcon = activeIcon ?? icon,
 assert(icon != null);

底部固定

enum BottomNavigationBarType {
 /// The [BottomNavigationBar]'s [BottomNavigationBarItem]s have fixed width, always
 /// display their text labels, and do not shift when tapped.
 fixed,

 /// The location and size of the [BottomNavigationBar] [BottomNavigationBarItem]s
 /// animate and labels fade in when they are tapped. Only the selected item
 /// displays its text label.
 shifting,
}

手势事件GestureDetector

GestureDetector 手势监听,它可以包裹任何Widget并处理包裹Widget的点击、滑动、双击等事件,GestureDetector extends StatelessWidget 可以直接return Widget

来看一个Widget触发点击事件的例子

GestureDetector(
 onTap: () {
 CommonModel model = bannerList[index];
 Navigator.push(
 context,
 MaterialPageRoute(
 builder: (context) => WebView(
 url: model.url,
 title: model.title,
 statusBarColor: model.statusBarColor,
 hideAppBar: model.hideAppBar,
 )));
 },
 child: Image.network(bannerList[index].icon,
 fit: BoxFit.fill), //加载网络图片,
 );

另外关于其他的双击、滑动等事件可自行翻阅文档.GestureDetector

滚动事件NotificationListener

NotificationListener 可用于监听所有Widget的滚动事件,不管使用何种Widget都可以很方便的进行处理

NotificationListener(
 //滚动监听 list view
 onNotification: (scrollNotification) {
 //监听滚动的距离ScrollUpdateNotification 滚动时在进行回调
 if (scrollNotification is ScrollUpdateNotification &&
 scrollNotification.depth == 0) {
 //只检测listview的滚动第0个元素widget时候才开始滚动
 _scroll(scrollNotification.metrics.pixels);
 }
 },
 child: _buildListView,
 ),

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • Flutter Android端启动白屏问题的解决

    问题描述 Flutter 应用在 Android 端上启动时会有一段很明显的白屏现象,白屏的时长由设备的性能决定,设备性能越差,白屏时间越长. 问题分析 其实启动白屏的问题在Android原生应用上也是一个常见问题,大致是因为从用户点击 Launcher Icon 到应用首页显示之间,Android 系统在完成应用的初始化工作,其流程如下: 在 Flutter Android 端上,白屏的问题会更加严重,因为除了 Android 应用启动耗时外,还增加了 Flutter 初始化耗时. 直到 Fl

  • Flutter进阶之实现动画效果(一)

    上一篇文章我们了解了Flutter的动画基础,这一篇文章我们就来实现一个图表的动画效果. 首先,我们需要创建一个新项目myapp,然后把main.dart的内容替换成下面的代码 import 'package:flutter/material.dart'; import 'dart:math'; void main() { runApp(new MyApp()); } class MyApp extends StatelessWidget { @override Widget build(Bui

  • Flutter质感设计之底部导航

    BottomNavigationBar即底部导航栏控件.显示在应用底部的质感设计控件,用于在少量视图中切换.底部导航栏包含多个以标签.图标或两者搭配的形式显示在项目底部的项目,提供了应用程序的顶级视图之间的快速导航.对于较大的屏幕,侧面导航可能更好. 创建navigation_icon_view.dart文件,定义一个NavigationIconView类,用于管理BottomNavigationBarItem(底部导航栏项目)控件的样式.行为与动画. import 'package:flutt

  • Flutter布局模型之层叠定位

    Stack即层叠布局控件,能够将子控件层叠排列. Stack控件的每一个子控件都是定位或不定位,定位的子控件是被Positioned控件包裹的.Stack控件本身包含所有不定位的子控件,其根据alignment定位(默认为左上角).然后根据定位的子控件的top.right.bottom和left属性将它们放置在Stack控件上. import 'package:flutter/material.dart'; class LayoutDemo extends StatelessWidget { @

  • flutter实现仿boss直聘功能

    Flutter是Google使用Dart语言开发的移动应用开发框架,使用一套Dart代码就能构建高性能.高保真的iOS和Android应用程序,并且在排版.图标.滚动.点击等方面实现零差异. 2年前,RN刚出来时做了个仿拉钩的demo,react-native-lagou. 这次flutter来了,想感受一下,索性用目前flutter的版本写的一个仿boss直聘应用. 时间有限,没完全仿照,去掉了一些功能,但是界面风格一致,有参考价值. github地址:flutter仿boss直聘. 感悟 与

  • Flutter路由的跳转、动画和传参详解(最简单)

    路由 做Android/iOS原生开发的时候,要打开一个新的页面,你得知道你的目标页面对象,然后初始化一个Intent或者ViewController,再通过startActivity或者pushViewController来推出一个新的页面,不能跟web一样,直接丢一个链接地址就跳转到新的页面.当然,可以自己去加一个中间层来实现这些功能. Flutter里面是原生支持路由的.Flutter的framework提供了路由跳转的实现.我们可以直接使用这些功能. Flutter路由介绍 Flutte

  • Flutter中ListView 的使用示例

    这个小例子使用的是豆瓣 API 中 正在上映的电影 的开放接口,要实现的主要效果如下: JSON 数据结构 Item 结构 Item 的结构是一个 Card 包含着一个 Row 然后这个 Row 里面左边是一个 Image ,右边是一个 Column 功能实现 material 库 Json 解析 网络请求 加载菊花 要实现上面四个功能,我们首先需要在 .dart 文件中引入如下代码 import 'dart:convert'; import 'package:http/http.dart' a

  • Flutter实现底部导航栏效果

    大家最近都在讨论新鲜技术-flutter,小编也在学习中,遇到大家都遇到的问题,底部导航.下面给大家贴出底部导航的编写,主要参考了lime这个项目. 上代码 一.在main.dart文件中 定义APP的基本信息 class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return new

  • Flutter质感设计之持久底部面板

    持久性底部面板可以用于补充应用主要内容的信息,即使用户与应用程序的其他控件进行互动,也仍然可以看到持久的底部面板.可以使用Scaffold.showBottomSheet函数创建和显示持久性底部面板. import 'package:flutter/material.dart'; class MyApp extends StatefulWidget { @override _MyApp createState() => new _MyApp(); } class _MyApp extends S

  • Flutter质感设计之弹出菜单

    PopupMenuButton控件即弹出菜单控件,点击控件会出现菜单. import 'package:flutter/material.dart'; class MenusDemo extends StatefulWidget { @override _MenusDemoState createState() => new _MenusDemoState(); } class _MenusDemoState extends State<MenusDemo> { String _body

随机推荐