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,
),
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。
以上是 Flutter常用的布局和事件示例详解 的全部内容, 来源链接: utcz.com/p/241726.html