Flutter 头部滑动渐变与flutter_swiper使用

577 阅读2分钟

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,比如SizeChangedLayoutNotificationScrollNotificationKeepAliveNotificationOverscrollIndicatorNotificationDraggableScrollableNotification等。

监听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')
   )
)

实例:

头部渐变.gif

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: '我的',
          )
        ],
      ),
    );
  }
}

\