MediaQuery.removePadding
flutter的会自动根据机型隔出通知栏的空间
要想去除隔出的空间,需要通过MediaQuery.removePadding包裹removeTop移除头部空间
bool removeLeft bool removeTop bool removeRight bool removeBottom
MediaQuery.removePadding(
removeTop: true,
context: context,
child: ...
)
NotificationListener
NotificationListener
NotificationListener是以冒泡的方式监听Notification的组件,冒泡方式就是向上传递,从子组件向父组件传递。
系统定义了很多Notification,比如SizeChangedLayoutNotification、ScrollNotification、KeepAliveNotification、OverscrollIndicatorNotification、DraggableScrollableNotification等。
监听listview的滚动事件监听ListView的滚动事件
下面监听最常见的ListView:
NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
print('$notification');
return true;
},
child: ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Text('index:$index'),
);
},
itemCount: 100,
),
)
打印日志:
ScrollStartNotification(depth: 0 (local), FixedScrollMetrics(0.0..[896.0]..4782.0), DragStartDetails(Offset(153.3, 520.3)))
UserScrollNotification(depth: 0 (local), FixedScrollMetrics(0.0..[896.0]..4782.0), direction: ScrollDirection.reverse)
ScrollUpdateNotification(depth: 0 (local), FixedScrollMetrics(1.2..[896.0]..4780.8), scrollDelta: 1.1666666666666667, DragUpdateDetails(Offset(0.0, -1.7)))
...ScrollUpdateNotification...
flutter: ScrollEndNotification(depth: 0 (local), FixedScrollMetrics(296.5..[896.0]..4485.5), DragEndDetails(Velocity(0.0, 0.0)))
flutter: UserScrollNotification(depth: 0 (local), FixedScrollMetrics(296.5..[896.0]..4485.5), direction: ScrollDirection.idle)
ScrollNotification中metrics 类型是ScrollMetrics,ScrollMetrics属性说明如下:
- pixels:当前的位置
- minScrollExtent:最小滚动距离
- maxScrollExtent:最大滚动距离
- viewportDimension:滚动控件的长度
- axisDirection:滚动的方向,向上、下、左、右
- axis:滚动控件的轴向,垂直或者水平
- outOfRange:是否超出滚动范围
- atEdge:是否在边缘(开始或者结束的位置),
- extentBefore:距离开始的距离,==0,表示在开始处。
- extentAfter:距离结尾的距离,==0,表示在末尾处。
- extentInside:控件范围内的列表长度
自定义监听事件
自定义事件:
class CustomNotification extends Notification {
CustomNotification(this.value);
final String value;
}
发送和接收事件:
NotificationListener<CustomNotification>(
onNotification: (CustomNotification notification) {
print('介绍事件——2:${notification.value}');
return true;
},
child: Center(
child: Builder(
builder: (context) {
return RaisedButton(
child: Text('发送'),
onPressed: () {
CustomNotification('自定义事件').dispatch(context);
},
);
},
),
),
)
运行打印 :
flutter: 介绍事件——2:自定义事件
onNotification的方法需要返回bool值,返回true,表示当前事件不在向上传递,false表示继续向上传递,
代码修改如下:
NotificationListener<CustomNotification>(
onNotification: (CustomNotification notification) {
print('介绍事件——1:${notification.value}');
return true;
},
child: NotificationListener<CustomNotification>(
onNotification: (CustomNotification notification) {
print('介绍事件——2:${notification.value}');
return false;
},
child: Center(
child: Builder(
builder: (context) {
return RaisedButton(
child: Text('发送'),
onPressed: () {
CustomNotification('自定义事件').dispatch(context);
},
);
},
),
),
))
在事件-2中返回false,打印日志:
flutter: 介绍事件——2:自定义事件
flutter: 介绍事件——1:自定义事件
返回true,打印日志:
flutter: 介绍事件——2:自定义事件
说明,返回true,当前事件不在向上传递。
补充
NotificationListener的onNotification(notification){}回调函数携带事件信息,通过事件信息if (notification is ScrollUpdateNotification)判断后执行操作。
- ScrollUpdateNotification 滚动
- SizeChangedLayoutNotification
NotificationListener( onNotification: (notification) { print('child:$notification'); return false; }, child: SizeChangedLayoutNotifier( child: Container(width: size, height: size, color: Colors.red), ), ),- KeepAliveNotification
- LayoutChangedNotification
它的返回值类型为布尔值,当返回值为
true时,阻止冒泡,其父级Widget将再也收不到该通知;当返回值为false时继续向上冒泡通知。
通过depth属性控制监听的组件。
如通过depth==0判断后则监听child的最外层
NotificationListener(
onNotification: (notification) {
if (notification is ScrollUpdateNotification && notification.depth == 0) {
// 滚动的距离 notification.metrics.pixels
_onScroll(notification.metrics.pixels);
return true;
}
return false;
},
child: ListView(
child: Text('aaaaa')
)
)
实例:
import 'package:flutter/material.dart';
import 'package:flutter_xiecheng/pages/home_page.dart';
import 'package:flutter_xiecheng/pages/my_page.dart';
import 'package:flutter_xiecheng/pages/search_page.dart';
import 'package:flutter_xiecheng/pages/travel_page.dart';
class TabNavigator extends StatefulWidget {
const TabNavigator({Key? key}) : super(key: key);
@override
State<TabNavigator> createState() => _TabNavigatorState();
}
class _TabNavigatorState extends State<TabNavigator> {
final _defaultColor = Colors.grey;
final _activeColor = Colors.blue;
int _currentIndex = 0;
final _controller = PageController(
initialPage: 0,
viewportFraction: 1.0, // 占视口的宽度0-1.0之间,不设置默认1.0
);
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _controller,
onPageChanged: (index) {
setState(() {
_currentIndex = index;
});
},
children: const [
HomePage(),
SearchPage(),
TravelPage(),
MyPage(),
],
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
// animateToPage:带动画跳转
// jumpToPage:直接改变当前页面无动画
// nextPage:下一页
// previousPage:上一页
_controller.animateToPage(index,
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
setState(() {
_currentIndex = index;
});
},
// 底部固定
type: BottomNavigationBarType.fixed,
selectedItemColor: _activeColor,
unselectedItemColor: _defaultColor,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: '搜索',
),
BottomNavigationBarItem(
icon: Icon(Icons.camera_alt),
label: '旅拍',
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
label: '我的',
)
],
),
);
}
}
\