flutter 03 添加自定义导航并使上下滚动时导航渐变

2019-10-13

上一节,我们制作了轮播图,在这里来对这个轮播图进行一点点优化。

在浏览应用时,很多应用都会有在页面滚动时,出现轮播图慢慢消失,导航不断清晰的效果,那么这里就来制作这样的效果。

首先我们要将首页的Column,改写成ListView,让首页滚动起来。

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      body: Center(
        child: ListView( //这里原来是Column,现在改写成listview
          children: <Widget>[
            Container(
              height: 160,
              child: Swiper(  //这里是我们的轮播图
                itemCount: _imageUrls.length, //轮播图数量
                autoplay: true, //自动播放
                itemBuilder: (BuildContext context,int index){  //在这个里面编写展示的每个内容
                  return Image.network( //通过远程链接获取图片
                    _imageUrls[index], //获取图片url
                    fit: BoxFit.fill,  //展示方式
                  );
                },
                pagination: SwiperPagination(), //添加轮播下方的小点
              ),
            )
          ],
        ),
      )
    );
  }

现在还是无法滚动,因为高度不够。我们还需要在listview中添加一个填充高度的容器。

                child: ListView(
                  children: <Widget>[
                    Container(
                      height: 160,
                      child: Swiper(  //这里是我们的轮播图
                        itemCount: _imageUrls.length, //轮播图数量
                        autoplay: true, //自动播放
                        itemBuilder: (BuildContext context,int index){  //在这个里面编写展示的每个内容
                          return Image.network( //通过远程链接获取图片
                            _imageUrls[index], //获取图片url
                            fit: BoxFit.fill,  //展示方式:充满父容器
                          );
                        },
                        pagination: SwiperPagination(), //添加轮播下方的小点
                      ),
                    ),
                    Container(
                      height: 800,
                      child: ListTile(
                        title: Text('哈哈'),
                      ),
                    )
                  ],
                ),

如果你是刘海屏,你会发现轮播图并没有紧贴顶部,我们希望我们的轮播图可以紧贴顶部。这里就需要用到一个MediaQuery.removePadding。那么我们就用MediaQuery.removePadding来替代Center组件

          MediaQuery.removePadding(  //移除默认的padding,可以移除iphone刘海屏的距离,不会直接移除需要removeTop: true配合
              removeTop: true,  //移除顶部距离
              context: context,
              child: NotificationListener( //监听组件,可以监听列表组件的滚动
                // ignore: missing_return
                onNotification: (scrollNotification){  //监听列表滚动
                  if(scrollNotification is ScrollUpdateNotification && scrollNotification.depth == 0){ //触发第0个组件,也就是listview
                    //列表滚动时调用
                    _onScroll(scrollNotification.metrics.pixels); //传入的参数为滚动距离
                  }
                },
                child: ListView(
                  children: <Widget>[
                    Container(
                      height: 160,
                      child: Swiper(  //这里是我们的轮播图
                        itemCount: _imageUrls.length, //轮播图数量
                        autoplay: true, //自动播放
                        itemBuilder: (BuildContext context,int index){  //在这个里面编写展示的每个内容
                          return Image.network( //通过远程链接获取图片
                            _imageUrls[index], //获取图片url
                            fit: BoxFit.fill,  //展示方式:充满父容器
                          );
                        },
                        pagination: SwiperPagination(), //添加轮播下方的小点
                      ),
                    ),
                    Container(
                      height: 800,
                      child: ListTile(
                        title: Text('哈哈'),
                      ),
                    )
                  ],
                ),
              )
          ),

MediaQuery.removePadding并不能直接去除默认顶部距离,而是要通过removeTop: true来实现。同时我们也引用了一个监听组件NotificationListener,用来监听屏幕的滚动。

  • ScrollUpdateNotification: 滚动发生更新
  • scrollNotification.depth: 滚动深度,也就是触发到第几个组件。
  • scrollNotification.metrics.pixels: 获取滚动的距离

现在我们来创建滚动触发的方法:

 const APPBAR_SCROLL_OFFSET = 100 ; //当滚动到100时变量
 double appBarAlpha = 0;  //透明值
  //listview滚动调用的函数
  _onScroll(offset){
     double alpha = offset/APPBAR_SCROLL_OFFSET;
     if(alpha < 0){
       alpha = 0;
     }else if(alpha> 1){
       alpha = 1;
     }
     setState(() {
       appBarAlpha = alpha;
     });
  }

这里的offset就是我们滚动页面时传来实时高度。APPBAR_SCROLL_OFFSET我们设置了一个变量,也就是在滚动到100位置之前导航会从透明到半透明,如果超过100的位置就会透明消失。变为不透明。

现在我们正式来添加导航,这里我为了大家看起来直观方便,直接把全部代码放出:

import 'package:flutter/material.dart';
import 'package:flutter_swiper/flutter_swiper.dart';
const APPBAR_SCROLL_OFFSET = 100 ; //当滚动到100时变量
class HomePage extends StatefulWidget{
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>{
  //轮播图的图片地址
  List _imageUrls=[
    'http://tcode.net/images/blogupload/timg201908190221.jpeg',
    'http://tcode.net/images/blogupload/20190501images.jpeg',
    'http://tcode.net/images/blogupload/symfony-ubuntu-apachepng-450x191.png'
  ];
 double appBarAlpha = 0;  //透明值
  //listview滚动调用的函数
  _onScroll(offset){
     double alpha = offset/APPBAR_SCROLL_OFFSET;
     if(alpha < 0){
       alpha = 0;
     }else if(alpha> 1){
       alpha = 1;
     }
     setState(() {
       appBarAlpha = alpha;
     });
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      body: Stack(
        children: <Widget>[
          MediaQuery.removePadding(  //移除默认的padding,可以移除iphone刘海屏的距离,不会直接移除需要removeTop: true配合
              removeTop: true,  //移除顶部距离
              context: context,
              child: NotificationListener( //监听组件,可以监听列表组件的滚动
                // ignore: missing_return
                onNotification: (scrollNotification){  //监听列表滚动
                  if(scrollNotification is ScrollUpdateNotification && scrollNotification.depth == 0){ //触发第零个组件,也就是listview
                    //列表滚动时调用
                    _onScroll(scrollNotification.metrics.pixels); //传入的参数为滚动距离
                  }
                },
                child: ListView(
                  children: <Widget>[
                    Container(
                      height: 160,
                      child: Swiper(  //这里是我们的轮播图
                        itemCount: _imageUrls.length, //轮播图数量
                        autoplay: true, //自动播放
                        itemBuilder: (BuildContext context,int index){  //在这个里面编写展示的每个内容
                          return Image.network( //通过远程链接获取图片
                            _imageUrls[index], //获取图片url
                            fit: BoxFit.fill,  //展示方式:充满父容器
                          );
                        },
                        pagination: SwiperPagination(), //添加轮播下方的小点
                      ),
                    ),
                    Container(
                      height: 800,
                      child: ListTile(
                        title: Text('哈哈'),
                      ),
                    )
                  ],
                ),
              )
          ),
          Opacity(  //设置一个透明的appbar ,自定义导航
            opacity: appBarAlpha,
            child: Container(
              height: 80,
              decoration: BoxDecoration(color: Colors.white),
              child: Center(
                child: Padding(
                  padding: EdgeInsets.only(top: 20),
                  child: Text('首页'),
                ),
              ),
            ),
          )
        ],
      )
    );
  }

}

效果如下(静态图):

@2020  TCODE    津ICP备13002520号-4