感谢@pskink 对此的回答(见上面的评论)。
这是仪表板的一些代码,该仪表板具有滚动的文章列表和视差滚动平铺图像作为背景...
class DashboardRoot extends StatefulWidget {
DashboardRoot({Key key}) : super(key: key);
@override
_DashboardRootState createState() => _DashboardRootState();
}
class _DashboardRootState extends State<DashboardRoot> {
int _currentIndex = 0;
ScrollController _controller;
double _offsetY = 0.0;
_scrollListener() {
setState(() {
_offsetY = _controller.offset;
});
}
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
var state = Provider.of<ArticlesState>(context, listen: false);
state.initArticleStream();
});
_controller = ScrollController();
_controller.addListener(_scrollListener);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: AppBottomNavigationBar(),
body: Stack(
children: [
SizedBox.expand(
child: Image(
image: AssetImage('assets/images/tiled_background_leaf.jpg'),
repeat: ImageRepeat.repeatY,
alignment: FractionalOffset(0, (_offsetY / 1000) * -1),
),
),
CustomScrollView(
controller: _controller,
slivers: [
SliverAppBar(
elevation: 0.0,
floating: true,
expandedHeight: 120,
flexibleSpace: FlexibleSpaceBar(
title: Text(NavigationManager
.instance.menuItems[_currentIndex].title),
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.settings),
onPressed: () => {
locator<NavigationService>()
.navigateTo(SettingsNavigator.routeName)
},
),
IconButton(
icon: Icon(Icons.menu),
onPressed: () => {RootScaffold.openDrawer(context)},
),
],
),
Consumer<ArticlesState>(
builder: (context, state, child) {
final List<Article> list = state.articles;
if (list == null) {
return SliverToBoxAdapter(
child: Center(
child: CircularProgressIndicator(
backgroundColor: Colors.amber, strokeWidth: 1),
),
);
} else if (list.length > 0) {
return SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 1.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
Article article = list[index];
return ArticleCell(
article: article,
cellTapHandler: () {
Navigator.pushNamed(
context, ArticleDetail.routeName,
arguments: new ArticleDetailArguments(
article.docId, article.heading));
});
},
childCount: list.length,
),
);
} else {
return Center(
child: Text("No Articles"),
);
}
},
),
],
),
],
));
}
}
请注意,Stack
背景图像在展开SizedBox
后会填满屏幕空间。上面的层是CustomScrollView
具有 theSliverGrid
和其他东西的层。
重要的是Image
:
child: Image(
image: AssetImage('assets/images/tiled_background_leaf.jpg'),
repeat: ImageRepeat.repeatY,
alignment: FractionalOffset(0, (_offsetY / 1000) * -1),
),
以及在用户滚动时由侦听_offsetY
器设置的属性:ScrollController
double _offsetY = 0.0;
_scrollListener() {
setState(() {
_offsetY = _controller.offset;
});
}
对齐属性用于将Image
对齐设置为顶部、中心、左侧等,但它也可以是任意偏移量。该FractionalOffset
值是一个范围0..1
,但将其设置为高于或低于零的较大数字也绝对没问题。因为图像也是使用ImageRepeat.repeatY
原点平铺的,平铺图像的原点使用对齐方式重新绘制,并且通过乱动数字,您可以创建一个很好的视差滚动效果。
请注意,FractionalOffset(0, (_offsetY / 1000) * -1)
偏移值除以 1000(这是您的速度,值越高,背景的视差越慢(将其视为两层之间的距离)。将数字乘以 -1 在正数之间切换和负数,并改变视差的方向。