我想实现此视频中的布局(在5:50时)https://www.youtube.com/watch?v=KYUTQQ1usZE&index=1&list=PL23Revp-82LKxKN9SXqQ5Nxaa1ZpYEQuaadd#t=05m50s

您将如何解决?我尝试使用ListView&GridLayout,但这似乎仅限于存档。我是否需要使用CustomMultiChildLayout(https://docs.flutter.io/flutter/widgets/CustomMultiChildLayout-class.html)或CustomScrollView(https://docs.flutter.io/flutter/widgets/CustomScrollView-class.html)之类的东西?
任何建议,将不胜感激,谢谢:)

更新:
据我所知,我将需要使用CustomScrollView(如果我错了,请纠正我)。但是Flutter框架给我留下的选择让我有些不知所措。而且我从文档中不确定要扩展我的目标需要扩展哪些类或需要实现哪些接口(interface)。我不知道我需要深入框架。具有自定义滚动效果的条和列表涉及以下类:

  • RenderSliver这实际上是实现滚动效果的渲染对象的基础。我想重新实现此方法可能会过大。但是,也许将其子类化并从那里开始(也许也太过杀了)?
  • RenderSliverMultiBoxAdaptor如果我们在层次结构中上移,则会找到抽象类RenderSliv​​erMultiBoxAdaptor。带有多个盒子子的条子。 RenderSliverBoxChildManager可以为RenderSliv​​erMultiBoxAdaptor即时提供子级。这些都是抽象类。那么也许从这里开始并扩展这些类(class)?
  • RenderSliverList这扩展了RenderSliv​​erMultiBoxAdaptor并提供了沿主轴布置的框子项。子级由实现RenderSliv​​erBoxChildManager的类传递。
    SliverMultiBoxAdaptorElement实现RenderSliv​​erBoxChildManager。因此,RenderSliv​​erList和SliverMultiBoxAdaptorElement是RenderSliv​​erMultiBoxAdaptor和RenderSliv​​erBoxChildManager的具体实现。我以为我可以扩展这些类(class)。但是,如果这样做,我仍然必须重新实现performLayout方法。因此,也许可以重用SliverMultiBoxAdaptorElement并扩展RenderSliv​​erMultiBoxAdaptor?
  • SliverList此类最终创建渲染对象(一个带有SliverMultiBoxAdaptorElement作为子管理器的RenderSliv​​erList),并向SliverMultiBoxAdaptorElement提供SliverChildDelegate,后者又懒惰地为SliverMultiBoxAdaptorWidget构建子代。 SliverList在沿主轴的线性阵列中放置多个框子级。它使用扩展SliverChildDelegate的类来即时提供子项。可以将其放置在CustomScrollViews条形数组中。这是在CustomScrollView中创建列表的最具体的条子。因此,我是否也可以仅以此为目标来存档根据视频进行布局的目标?到目前为止,我尝试为CustomScrollView提供一个ScrollController来拦截滚动偏移量,然后根据滚动偏移量和带有SliverChildBuilderDelegate的元素索引来构建子元素。但是这样做时,scrollview不再滚动。仅当所有单元格的总高度超过视口时,它才会滚动。

  • 那么,我真的必须扩展RenderSliv​​erMultiBoxAdaptor并自己实现perfromLayout方法吗?对我来说,这似乎是唯一的选择...

    最佳答案

    乍一看很难理解条状逻辑。
    但是重要的是SliverGeometry类

  • paintOrigin-将其视为delta y。当您想制作小部件时
    固定在屏幕上时,您需要从顶部 push 它。
  • constraints.scrollOffset显示逻辑位置的滚动偏移量
    小部件。
  • scrollExtent显示小部件的逻辑高度。帮助小部件
    知道您滚动了所有条子。

  • layout -  flutter ,自定义滚动效果-LMLPHP
    import 'dart:math' as math;
    import 'package:flutter/foundation.dart';
    import 'package:flutter/rendering.dart';
    import 'package:flutter/widgets.dart';
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: Scaffold(
            body: MyHomePage(),
          ),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      final GlobalKey _key = GlobalKey();
    
      RenderObject ansestor;
      @override
      void initState() {
        WidgetsBinding.instance.addPostFrameCallback(_getPosition);
    
        super.initState();
      }
    
      _getPosition(_) {
        setState(() {
          ansestor = _key.currentContext.findRenderObject();
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return LayoutBuilder(builder: (context, constraints) {
          return CustomScrollView(
            physics: ClampingScrollPhysics(),
            key: _key,
            slivers: <Widget>[
              CustomSliver(
                isInitiallyExpanded: true,
                ansestor: ansestor,
                child: _Item(
                  title: 'first title',
                  fileName: 'item_1',
                ),
              ),
              CustomSliver(
                ansestor: ansestor,
                child: _Item(
                  title: 'second title',
                  fileName: 'item_2',
                ),
              ),
              CustomSliver(
                ansestor: ansestor,
                child: _Item(
                  title: 'third title',
                  fileName: 'item_3',
                ),
              ),
              CustomSliver(
                ansestor: ansestor,
                child: _Item(
                  title: 'fourth title',
                  fileName: 'item_4',
                ),
              ),
              CustomSliver(
                ansestor: ansestor,
                child: _Item(
                  title: 'fifth title',
                  fileName: 'item_5',
                ),
              ),
              CustomSliver(
                ansestor: ansestor,
                child: _Item(
                  title: 'first title',
                  fileName: 'item_6',
                ),
              ),
              SliverToBoxAdapter(
                child: Container(
                  child: Center(
                    child: Text('end'),
                  ),
                  height: 1200,
                  color: Colors.green.withOpacity(0.3),
                ),
              ),
            ],
          );
        });
      }
    }
    
    class CustomSliver extends SingleChildRenderObjectWidget {
      CustomSliver({
        this.child,
        Key key,
        this.ansestor,
        this.isInitiallyExpanded = false,
      }) : super(key: key);
    
      final RenderObject ansestor;
      final bool isInitiallyExpanded;
    
      @override
      RenderObject createRenderObject(BuildContext context) {
        return CustomRenderSliver(
          isInitiallyExpanded: isInitiallyExpanded,
        );
      }
    
      @override
      void updateRenderObject(
        BuildContext context,
        CustomRenderSliver renderObject,
      ) {
        renderObject.ansestor = ansestor;
        renderObject.markNeedsLayout();
      }
    
      final Widget child;
    }
    
    class CustomRenderSliver extends RenderSliverSingleBoxAdapter {
      CustomRenderSliver({
        RenderBox child,
        this.isInitiallyExpanded,
      }) : super(child: child);
    
      final double max = 250;
      final double min = 100;
    
      RenderObject ansestor;
      final bool isInitiallyExpanded;
      void performLayout() {
        var constraints = this.constraints;
    
        double distanceToTop;
    
        double maxExtent;
    
        if (ansestor != null) {
          distanceToTop = child.localToGlobal(Offset.zero, ancestor: ansestor).dy;
        }
    
        if (ansestor == null) {
          if (isInitiallyExpanded) {
            maxExtent = max;
          } else {
            maxExtent = min;
          }
        } else {
          if (constraints.scrollOffset > 0) {
            maxExtent = (max - constraints.scrollOffset).clamp(0.0, max);
          } else if (distanceToTop < max) {
            maxExtent = min + (3 * (250 - distanceToTop) / 5);
          } else {
            maxExtent = min;
          }
        }
    
        child.layout(
          constraints.asBoxConstraints(maxExtent: maxExtent),
          parentUsesSize: true,
        );
    
        var paintExtent = math.min(maxExtent, constraints.remainingPaintExtent);
    
        geometry = SliverGeometry(
          paintOrigin: maxExtent == 0 ? 0.0 : constraints.scrollOffset,
          scrollExtent: max,
          paintExtent: paintExtent,
          maxPaintExtent: paintExtent,
          hasVisualOverflow: true,
        );
    
        constraints = constraints.copyWith(remainingPaintExtent: double.infinity);
        setChildParentData(child, constraints, geometry);
      }
    }
    
    class _Item extends StatelessWidget {
      const _Item({
        Key key,
        @required this.title,
        @required this.fileName,
      }) : super(key: key);
    
      final String title;
      final String fileName;
    
      @override
      Widget build(BuildContext context) {
        return LayoutBuilder(
          builder: (context, constraints) {
            return Container(
              height: 250,
              decoration: BoxDecoration(
                image: DecorationImage(
                  image: AssetImage('assets/images/$fileName.png'),
                  fit: BoxFit.fitWidth,
                ),
              ),
              child: Padding(
                padding: const EdgeInsets.only(top: 40),
                child: Text(
                  title,
                  style: Theme.of(context).textTheme.headline4.copyWith(
                        color: Colors.white,
                        fontWeight: FontWeight.bold,
                        fontSize: 60,
                      ),
                ),
              ),
            );
          },
        );
      }
    }
    

    关于layout - flutter ,自定义滚动效果,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47979598/

    10-10 08:20