Flutter中的可滚动视图在背景小部件上方显示。

huangapple go评论73阅读模式
英文:

Scrollable view over background widget in Flutter

问题

I want to make something similar to the Google weather app where there is a background image displayed underneath scrollable content, but the content starts off the screen and only comes into view when the user scrolls.

My first idea was to use a Stack with a ListView as a child. This works in that the content is scrollable and the image is in the background, but the content does not start off the bottom of the screen.

If I add a SizedBox(height: 500) to the top of the ListView, I can scroll the content into view, but obviously when the SizedBox reaches the top, the scrolling stops, and if I resize the app, the SizedBox needs to be resized.

I'm using a Lottie animation instead of an image for the background, but the concept is the same.
Animation from Lottie Files: https://lottiefiles.com/139998-day-and-night-landscape
Lottie Flutter package: https://pub.dev/packages/lottie

Can anyone recommend a good way to achieve the desired effect?

英文:

I want to make something similar to the Google weather app where there is a background image displayed underneath scrollable content, but the content starts off the screen and only comes into view when the user scrolls.
Flutter中的可滚动视图在背景小部件上方显示。

My first idea was to use a Stack with a ListView as a child. This works in that the content is scrollable and the image is in the background, but the content does not start off the bottom of the screen.
Flutter中的可滚动视图在背景小部件上方显示。

import 'package:lottie/lottie.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Padding(
      padding: const EdgeInsets.all(8.0),
      child: Stack(
        children: [
          ClipRRect(
            borderRadius: BorderRadius.circular(12),
            child: Lottie.asset('assets/landscape.zip'),
          ),
          Column(
            children: [
              const Padding(
                padding: EdgeInsets.all(8.0),
                child: Text(
                  "Good Morning!",
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
              Expanded(
                child: ListView(
                  padding: const EdgeInsets.all(8.0),
                  children: [
                    const SizedBox(height: 500),
                    Container(
                      color: Colors.grey[300],
                      height: 200,
                      margin: const EdgeInsets.only(bottom: 10),
                    ),
                    Container(
                      color: Colors.grey[300],
                      height: 200,
                    ),
                  ],
                ),
              ),
            ],
          )
        ],
      ),
    ));
  }
}

If I add a SizedBox(height: 500) to the top of the ListView, I can scroll the content into view, but obviously when the SizedBox reaches the top, the scrolling stops, and if I resize the app the SizedBox needs to be resized.
Flutter中的可滚动视图在背景小部件上方显示。

I'm using a Lottie animation instead of an image for the background, but the concept is the same.
Animation from Lottie Files: https://lottiefiles.com/139998-day-and-night-landscape
Lottie Flutter package: https://pub.dev/packages/lottie

Can anyone recommend a good way to achieve the desired effect?

答案1

得分: 1

我尝试通过使用Stack来呈现背景和前景页面的行为。

前景页面位于垂直PageView内,以重现用户停留在整个页面时的步进效果。
使用PageViewScrollController可以检测到第一页(用于显示背景的空白页)和第二页之间的过渡。

如果将此绑定到执行颜色过渡的额外图层,可以使用Opacity小部件获得很酷的效果。

可能需要一些调整才能完美实现您想要的设计,但我认为它相当不错。

PS:视频将在两天后过期 https://streamable.com/obizkq

class TestAppHomePage extends StatefulWidget {
  @override
  TestAppHomePageState createState() => TestAppHomePageState();
}

class TestAppHomePageState extends State<TestAppHomePage> {
  final PageController controller = PageController();
  double currentPageValue = 0.0;

  @override
  void initState() {
    controller.addListener(() {
      if (controller.page != null && controller.page! <= 1.0) {
        setState(() {
          currentPageValue = controller.page!;
        });
      }
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Stack(
      children: [
        Image.asset(
          'assets/background_image.png',
          fit: BoxFit.cover,
          height: MediaQuery.of(context).size.height,
          width: MediaQuery.of(context).size.width,
        ),
        Opacity(
          opacity: currentPageValue,
          child: Container(
            color: Colors.green,
          ),
        ),
        PageView(
          scrollDirection: Axis.vertical,
          controller: controller,
          children: const <Widget>[
            CustomPage(
              title: '',
              backgroundColor: Colors.transparent,
            ),
            Opacity(
              opacity: currentPageValue,
              child: CustomPage(
                title: 'Page 2',
                backgroundColor: Colors.transparent,
              ),
            ),
            CustomPage(
              title: 'Page 3',
              backgroundColor: Colors.blue,
            ),
          ],
        ),
      ],
    ));
  }
}

class CustomPage extends StatelessWidget {
  final String title;
  final Color backgroundColor;
  const CustomPage(
      {super.key, required this.title, required this.backgroundColor});

  @override
  Widget build(BuildContext context) {
    return Container(
      color: backgroundColor,
      child: Center(
        child: Text(title),
      ),
    );
  }
}
英文:

I tried to reproduce the behavior you are describing by using a Stack to present the background and the front pages.

The front pages are inside a vertical PageView in order to reproduce the stepper effect as the user stops on the entire page.
Using the ScrollController of the PageView you can detect the transition between the first page (which is empty to display the background) and the second page.

If you bind this to an additional layer that performs the color transition with an Opacity widget you have something pretty cool.

It may require a few adjustments to perfectly achieve the design you want but it's pretty neat in my opinion.

PS: Video expires in two days https://streamable.com/obizkq

class TestAppHomePage extends StatefulWidget {
  @override
  TestAppHomePageState createState() =&gt; TestAppHomePageState();
}

class TestAppHomePageState extends State&lt;TestAppHomePage&gt; {
  final PageController controller = PageController();
  double currentPageValue = 0.0;

  @override
  void initState() {
    controller.addListener(() {
      if (controller.page != null &amp;&amp; controller.page! &lt;= 1.0) {
        setState(() {
          currentPageValue = controller.page!;
        });
      }
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Stack(
      children: [
        Image.asset(
          &#39;assets/background_image.png&#39;,
          fit: BoxFit.cover,
          height: MediaQuery.of(context).size.height,
          width: MediaQuery.of(context).size.width,
        ),
        Opacity(
          opacity: currentPageValue,
          child: Container(
            color: Colors.green,
          ),
        ),
        PageView(
          scrollDirection: Axis.vertical,
          controller: controller,
          children: const &lt;Widget&gt;[
            CustomPage(
              title: &#39;&#39;,
              backgroundColor: Colors.transparent,
            ),
            Opacity(
              opacity: currentPageValue,
              child: CustomPage(
                title: &#39;Page 2&#39;,
                backgroundColor: Colors.transparent,
              ),
            ),
            CustomPage(
              title: &#39;Page 3&#39;,
              backgroundColor: Colors.blue,
            ),
          ],
        ),
      ],
    ));
  }
}

class CustomPage extends StatelessWidget {
  final String title;
  final Color backgroundColor;
  const CustomPage(
      {super.key, required this.title, required this.backgroundColor});

  @override
  Widget build(BuildContext context) {
    return Container(
      color: backgroundColor,
      child: Center(
        child: Text(title),
      ),
    );
  }
}

huangapple
  • 本文由 发表于 2023年6月8日 03:06:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76426379.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定