先来看下效果图

在开发中,很多场景都会使用的返回顶部的功能,那么在flutter中,返回顶部的按钮要怎么做呢?
首先,我们要来写一个按钮:
好吧,我相信大家都会写这个按钮。
其次,监听页面的滚动,原生中我们使用delegate,可以知道滚动的距离,在flutter中怎么监听呢?看代码:
	//这个widget.controller就是外部传入的被监听的这个滚动的'控制器'或者页面吧
    widget.controller?.addListener(isScroll);
	void isScroll() {
	    final bool toShow = (widget.controller?.offset ?? 0) >
	        MediaQuery.of(context).size.height / 2;
	    if (toShow ^ shown) {
	      setState(() {
	      	//控制返回顶部是否隐藏
	        shown = toShow;
	      });
    	}
  	}
复制代码返回顶部的功能很简单,在原生中就是改变偏移量,在flutter中怎么写呢?
//是不是很眼熟啊,很相似的用法,特别是这个easeIn,简单的位移动画你会了么?
widget.controller?.animateTo(0,
                  duration: Duration(milliseconds: 200),
                  curve: Curves.easeIn);
复制代码注意:这里的这个返回顶部按钮要飘起来哦,必须使用Stack包着的布局,且要使用Positioned来控制位置:
///按钮的widget完整写法
import 'package:flutter/material.dart';
class BackToTop extends StatefulWidget {
  final ScrollController controller;
  ///传入距离底部的距离
  final double bottom;
  BackToTop(this.controller, {this.bottom});
  @override
  _BackToTopState createState() => _BackToTopState();
}
class _BackToTopState extends State<BackToTop> {
  bool shown = false;
  @override
  void initState() {
    super.initState();
    widget.controller?.addListener(isScroll);
  }
  @override
  void dispose() {
    super.dispose();
    widget.controller?.removeListener(isScroll);
  }
  void isScroll() {
    final bool toShow = (widget.controller?.offset ?? 0) >
        MediaQuery.of(context).size.height / 2;
    if (toShow ^ shown) {
      setState(() {
        shown = toShow;
      });
    }
  }
  @override
  Widget build(BuildContext context) {
    return Positioned(
        bottom: MediaQuery.of(context).padding.bottom + (widget.bottom ?? 40),
        right: 20,
        child: Offstage(
          offstage: !shown,
          child: GestureDetector(
            onTap: () {
              widget.controller?.animateTo(0,
                  duration: Duration(milliseconds: 200),
                  curve: Curves.easeIn);
            },
            child: Container(
                height: 44,
                width: 44,
                alignment: Alignment(0, 0),
                decoration: new BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.all(Radius.circular(16)),
                    boxShadow: [
                      BoxShadow(
                          color: Color(0xFF000000).withOpacity(0.1),
                          blurRadius: 4,
                          spreadRadius: 0),
                    ]),
                child: Column(
                  children: <Widget>[
                    Container(
                      margin: EdgeInsets.only(top: 4),
                      child: Icon(
                        Icons.vertical_align_top,
                        size: 20,
                        color: Colors.black38,
                      ),
                    ),
                    Container(
                      margin: EdgeInsets.only(top: 0),
                      child: Text(
                        'Top',
                        style: TextStyle(fontSize: 10, color: Color(0xFFA1A6AA)),
                      ),
                    )
                  ],
                )
            ),
          ),
        )
    );
  }
}
复制代码说完按钮了,来说说这个显示按钮的页面中有哪些需要注意的:
唯一需要注意的应该就是要把controller传进去,这个controller也必须是当前页面的controller:
///controller属性要赋值哦
controller: _controller,
复制代码看下这个滚动的主页面的代码吧:
import 'package:flutter/material.dart';
import 'package:flutter_app/back_to_top.dart';
class BackTopListView extends StatefulWidget {
  @override
  _BackTopListViewState createState() => _BackTopListViewState();
}
class _BackTopListViewState extends State<BackTopListView> {
//
  final ScrollController _controller = ScrollController(keepScrollOffset: false);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: Text('返回顶部页'),
      ),
      body: SafeArea(
      	///这种场景,应该是要用stack的吧?要不然不会盖在页面上,布局问题就不多说了
        child: Stack(
          children: <Widget>[
            ListView.separated(
               ///这里的controller一定要赋值,这样才能保证是同一个控制器中
              controller: _controller,
              itemBuilder: (BuildContext context, int index) {
                return Container(
                    child: Container(
                        padding: EdgeInsets.all(10),
                        child: Text('返回顶部页Item$index')
                    )
                );
              },
              separatorBuilder: (BuildContext context, int index) {
                return Divider();
              },
              itemCount: 100,
            ),
            ///返回顶部按钮,传入控制器
            BackToTop(_controller),
          ],
        ),
      ),
    );
  }
}
复制代码以上,是不是很简单呢?你学会了么?
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
    























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)
