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

Flutter 项目中常用的布局详情,及封装和使用,快速开发项目 以及手势事件和滚动事件的使用 Scaffold 导航栏的实现,有些路由页可能会有抽屉菜单(D

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,
 ),

总结

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

您可能有感兴趣的文章
图文详解Flutter单例的如何实现

Flutter中如何使用setState时的6个简单技巧总结

Flutter路由fluro引入配置和如何使用的具体方法

Flutter如何实现下拉刷新和上拉加载更多

封装flutter状态管理工具示例详解