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