likes
comments
collection
share

Flutter开发 - 写一个返回顶部功能的小控件

作者站长头像
站长
· 阅读数 39

先来看下效果图 Flutter开发 - 写一个返回顶部功能的小控件

在开发中,很多场景都会使用的返回顶部的功能,那么在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),
          ],
        ),
      ),
    );
  }
}

以上,是不是很简单呢?你学会了么?

转载自:https://juejin.cn/post/6936063361046528031
评论
请登录