Flutter:使用 Sliver 小部件时如何在滚动时隐藏 BottomAppBar?
Posted
技术标签:
【中文标题】Flutter:使用 Sliver 小部件时如何在滚动时隐藏 BottomAppBar?【英文标题】:Flutter: How to hide BottomAppBar on scroll when using Sliver widgets? 【发布时间】:2019-07-28 16:54:26 【问题描述】:当用户向下滚动列表时,我试图隐藏底部的应用栏,就像它在行为部分的材料设计文档中显示的那样:
https://material.io/design/components/app-bars-bottom.html#behavior
我正在寻找的效果如下所示: https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F1gaSrddolFHd8BwOioeYz0ODiyEhCevtn%2Fbehavior-scroll.mp4
到目前为止,我在 Scaffold 中添加了底部应用栏,但它没有滚动行为。
在 *** 上回答了一个类似的问题,但它显示了一种解决方法,并且已经在不久前得到了回答。
在 Flutter 中是否有合法的方式来实现这种效果?
我的代码:
import 'package:fitness_fatality_flutter/data/entities/exercise.dart';
import 'package:fitness_fatality_flutter/data/entities/workout.dart';
import 'package:flutter/material.dart';
class WorkoutDetailsPage extends StatelessWidget
Workout _workout = Workout();
final List<Exercise> exercises = [
Exercise(name: "Push Ups"),
Exercise(name: "Bench press"),
Exercise(name: "Pull ups"),
Exercise(name: "Press ups"),
Exercise(name: "Crunches"),
Exercise(name: "Sit ups"),
Exercise(name: "BIceps curl"),
Exercise(name: "Something else"),
Exercise(name: "Push Ups"),
Exercise(name: "Bench press"),
Exercise(name: "Pull ups"),
Exercise(name: "Press ups"),
Exercise(name: "Crunches"),
Exercise(name: "Sit ups"),
Exercise(name: "BIceps curl"),
Exercise(name: "Something else"),
Exercise(name: "Push Ups"),
Exercise(name: "Bench press"),
Exercise(name: "Pull ups"),
Exercise(name: "Press ups"),
Exercise(name: "Crunches"),
Exercise(name: "Sit ups"),
Exercise(name: "BIceps curl"),
Exercise(name: "Something else"),
Exercise(name: "Push Ups"),
Exercise(name: "Bench press"),
Exercise(name: "Pull ups"),
Exercise(name: "Press ups"),
Exercise(name: "Crunches"),
Exercise(name: "Sit ups"),
Exercise(name: "BIceps curl"),
Exercise(name: "Something else"),
Exercise(name: "Push Ups"),
Exercise(name: "Bench press"),
Exercise(name: "Pull ups"),
Exercise(name: "Press ups"),
Exercise(name: "Crunches"),
Exercise(name: "Sit ups"),
Exercise(name: "BIceps curl"),
Exercise(name: "Something else"),
];
WorkoutDetailsPage(this._workout);
@override
Widget build(BuildContext context)
return Scaffold(
floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling,
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.blue,
elevation: 12,
onPressed: () ,
),
bottomNavigationBar: BottomAppBar(
elevation: 8,
shape: CircularNotchedRectangle(),
color: Colors.blue,
child: Container(
height: 50,
child: Row(
children: <Widget>[Text("data")],
),
),
),
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
bottom:BottomAppBar(), //<-- This would throw an error
expandedHeight: 150,
pinned: true,
floating: true,
snap: true,
flexibleSpace: FlexibleSpaceBar(
title: Text(_workout.name),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(buildSliverListItem,
childCount: exercises.length),
),
],
),
);
Widget buildSliverListItem(BuildContext context, int index)
return Center(
child: ListTile(
title: Text(exercises[index].name),
),
);
【问题讨论】:
【参考方案1】:BottomAppBar
是 Scaffold
小部件的一部分,而不是 Sliver
。有一种方法可以实现您所描述的,但它有点复杂,需要StatefulWiget
和ScrollController
,AnimatedContainer
。请参阅下面的完整示例代码:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
debugShowCheckedModeBanner: false,
home: WorkoutDetailsPage(Workout()),
);
class Exercise
String name;
Exercise(@required name)
this.name = name;
class Workout
String name = "my name";
class WorkoutDetailsPage extends StatefulWidget
Workout _workout = Workout();
WorkoutDetailsPage(this._workout);
@override
_WorkoutDetailsPageState createState() => _WorkoutDetailsPageState();
class _WorkoutDetailsPageState extends State<WorkoutDetailsPage>
final List<Exercise> exercises = [
Exercise(name: "Push Ups"),
Exercise(name: "Bench press"),
Exercise(name: "Pull ups"),
Exercise(name: "Press ups"),
Exercise(name: "Crunches"),
Exercise(name: "Sit ups"),
Exercise(name: "BIceps curl"),
Exercise(name: "Something else"),
Exercise(name: "Push Ups"),
Exercise(name: "Bench press"),
Exercise(name: "Pull ups"),
Exercise(name: "Press ups"),
Exercise(name: "Crunches"),
Exercise(name: "Sit ups"),
Exercise(name: "BIceps curl"),
Exercise(name: "Something else"),
Exercise(name: "Push Ups"),
Exercise(name: "Bench press"),
Exercise(name: "Pull ups"),
Exercise(name: "Press ups"),
Exercise(name: "Crunches"),
Exercise(name: "Sit ups"),
Exercise(name: "BIceps curl"),
Exercise(name: "Something else"),
Exercise(name: "Push Ups"),
Exercise(name: "Bench press"),
Exercise(name: "Pull ups"),
Exercise(name: "Press ups"),
Exercise(name: "Crunches"),
Exercise(name: "Sit ups"),
Exercise(name: "BIceps curl"),
Exercise(name: "Something else"),
Exercise(name: "Push Ups"),
Exercise(name: "Bench press"),
Exercise(name: "Pull ups"),
Exercise(name: "Press ups"),
Exercise(name: "Crunches"),
Exercise(name: "Sit ups"),
Exercise(name: "BIceps curl"),
Exercise(name: "Something else"),
];
ScrollController _hideButtonController;
bool _isVisible = true;
@override
void initState()
super.initState();
_isVisible = true;
_hideButtonController = new ScrollController();
_hideButtonController.addListener(()
print("listener");
if (_hideButtonController.position.userScrollDirection ==
ScrollDirection.reverse)
setState(()
_isVisible = false;
print("**** $_isVisible up");
);
if (_hideButtonController.position.userScrollDirection ==
ScrollDirection.forward)
setState(()
_isVisible = true;
print("**** $_isVisible down");
);
);
@override
Widget build(BuildContext context)
return Scaffold(
floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling,
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: _isVisible
? FloatingActionButton(
backgroundColor: Colors.blue,
elevation: 12,
onPressed: () ,
)
: null,
bottomNavigationBar: AnimatedContainer(
duration: Duration(milliseconds: 200),
height: _isVisible ? 60 : 0.0,
child: BottomAppBar(
elevation: 8,
shape: CircularNotchedRectangle(),
color: Colors.blue,
child: Container(
height: 60,
child: Row(
children: <Widget>[Text("data")],
),
),
),
),
body: CustomScrollView(
controller: _hideButtonController,
slivers: <Widget>[
SliverAppBar(
expandedHeight: 150,
pinned: true,
floating: true,
snap: true,
flexibleSpace: FlexibleSpaceBar(
title: Text(widget._workout.name),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(buildSliverListItem,
childCount: exercises.length),
),
],
),
);
Widget buildSliverListItem(BuildContext context, int index)
return Center(
child: ListTile(
title: Text(exercises[index].name),
),
);
【讨论】:
以上是关于Flutter:使用 Sliver 小部件时如何在滚动时隐藏 BottomAppBar?的主要内容,如果未能解决你的问题,请参考以下文章
Flutter - 如何在图像下方使用带有图像和文本/图标的容器小部件
如何在 Flutter 小部件测试中执行缩放(捏缩放)多指手势?