0

我正在尝试在 Flutter 应用程序中创建视差背景,构建它的最有效方法是使用Stack填充屏幕的图像作为背景,然后将我的列表放在顶部。图像ImageRepeat在 Y 轴上用一组平铺。然后计划是偏移磁贴的原点与ScrollController我用于我的列表的同步。然后我可以调整平铺图像的原点以创建视差效果。这应该很简单。这是上下文的一些代码:

Stack(
          children: [
            SizedBox.expand(
              child: Image(
                image: AssetImage('assets/images/tiled_background_leaf.jpg'),
                repeat: ImageRepeat.repeatY,
              ),
            ),
            CustomScrollView(
              controller: _controller,
              slivers: [ ...

我的问题是 Image 没有offset属性或origin位置。我需要一些关于最简单方法的建议。我已经看到有自定义画家、画布方法等,但是当小部件中应该有更优雅的解决方案时,它们似乎都过于复杂Image,或者可能在另一个小部件中可以给我相同的视差效果。

4

1 回答 1

1

感谢@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 在正数之间切换和负数,并改变视差的方向。

于 2020-08-24T11:06:41.507 回答