颤动的可滚动背景图像

时间:2019-06-01 09:43:07

标签: image flutter flutter-animation

我正在尝试实现可滚动的背景图像(视差)。 就像在主屏幕启动器中一样。

一个例子: 在Evie启动器中: this video

我曾尝试使用像这样的文档中提到的here中的AnimatedBuilder

我正在使用ValueNotifier<double>作为AnimatedBuilder小部件的动画的侦听器。

完整的代码是这里

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'PageView Scrolling',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>{
  ValueNotifier<double> _notifier;
  double _prevnotifier;

  double getOffset(){
    if (_notifier.value == 0 && _prevnotifier != null){
      return _prevnotifier;
    }
    return _notifier.value;
  }

  @override
  void dispose() {
    _notifier?.dispose();
    super.dispose();
  }

  @override
  void initState() {
    super.initState();
    _notifier = ValueNotifier<double>(0);
    _prevnotifier = _notifier.value;

    _notifier.addListener(
       (){
         print('object ${_notifier.value}');
           if (_notifier.value != 0)
             _prevnotifier = _notifier.value;
       }
    );
  }

  @override
  Widget build(BuildContext context) {
    print("Size is ${MediaQuery.of(context).size}");
    return Scaffold(
      body: Stack(
        children: <Widget>[
           AnimatedBuilder(
             animation: _notifier,
             builder: (context, _) {
               return Transform.translate(
                 offset: Offset(-getOffset() * 60, 0),
                 child: Image.asset(
                   "assets/wallhaven-r276qj.png",
                   height: MediaQuery.of(context).size.height,
                   fit: BoxFit.fitHeight
                 ),
               );
            },
          ),
          NotifyingPageView(
            notifier: _notifier,
          ),
        ],
      ),
    );
  }
}

class NotifyingPageView extends StatefulWidget {
  final ValueNotifier<double> notifier;

  const NotifyingPageView({Key key, this.notifier}) : super(key: key);

  @override
  _NotifyingPageViewState createState() => _NotifyingPageViewState();
}

class _NotifyingPageViewState extends State<NotifyingPageView> {
  int _previousPage;
  PageController _pageController;

  void _onScroll() {
    // Consider the page changed when the end of the scroll is reached
    // Using onPageChanged callback from PageView causes the page to change when
    // the half of the next card hits the center of the viewport, which is not
    // what I want

    if (_pageController.page.toInt() == _pageController.page) {
      _previousPage = _pageController.page.toInt();
    }
    widget.notifier?.value = _pageController.page - _previousPage;
  }

  @override
  void initState() {
    _pageController = PageController(
       initialPage: 0,
       viewportFraction: 0.9,
     )..addListener(_onScroll);

     _previousPage = _pageController.initialPage;
     super.initState();
  }

  List<Widget> _pages = List.generate(
    10,
    (index) {
      return Container(
        height: 10,
        alignment: Alignment.center,
          color: Colors.transparent,
          child: Text(
            "Card number $index",
            style: TextStyle(
              color: Colors.teal,
              fontWeight: FontWeight.bold,
              fontSize: 25,
            ),
          ),
        );
      },
  );

  @override
  Widget build(BuildContext context) {
    return PageView(
      children: _pages,
      controller: _pageController,
    );
  }
}

可以找到图像here

现在我有两个问题:

  • 使用fit: BoxFit.fitHeight时,图像没有像this那样完全溢出
  • 因为完成动画后该值将变为零,因此会像这样捕捉: this video

我尝试在_notifier.value变为零之前存储值,并在它返回零时使用它,但这导致了上面视频中向您展示的怪异转换。

您认为可以做些什么来使像可滚动墙纸一样飘动?

类似这样的东西

Design

1 个答案:

答案 0 :(得分:0)

这不像我想象的那么简单。

TLDR Github阅读评论。

我使用了如上所述的ValueNotifier<double>来控制滚动。

然后,我使用OverflowBoxTransform.translate属性代替了alignment。该值是根据渲染前的notifier.value计算出来的。

并以全屏模式显示图像:

我将AspectRatio与孩子DecoratedBox一起使用,孩子decorationBoxDecoration,其imageImageProvider

所有代码都可以在github上找到here。 (阅读评论)

在GitHub上的this issue上有一些详细的信息,而Antonello Galipò的替代实现则不太复杂