Layout 颤振,自定义滚动效果
我想实现一个类似于本视频的布局(5:50) 你将如何处理这个问题?我尝试使用ListView&GridLayout,但这似乎仅限于存档。我是否需要使用CustomMultiChildLayout()或CustomScrollView()之类的工具? 如有任何建议,将不胜感激,thx:) 更新: 据我所知,我需要使用CustomScrollView(如果我错了,请纠正我)。但我有点被颤振框架留给我的选项弄得不知所措。从文档中我不确定需要扩展哪些类,或者需要实现哪些接口来归档我的目标。我不知道我需要深入研究这个框架有多深。当涉及具有自定义滚动效果的条子和列表时,会涉及以下类:Layout 颤振,自定义滚动效果,layout,dart,scrollview,flutter,Layout,Dart,Scrollview,Flutter,我想实现一个类似于本视频的布局(5:50) 你将如何处理这个问题?我尝试使用ListView&GridLayout,但这似乎仅限于存档。我是否需要使用CustomMultiChildLayout()或CustomScrollView()之类的工具? 如有任何建议,将不胜感激,thx:) 更新: 据我所知,我需要使用CustomScrollView(如果我错了,请纠正我)。但我有点被颤振框架留给我的选项弄得不知所措。从文档中我不确定需要扩展哪些类,或者需要实现哪些接口来归档我的目标。我不知道我需要
- 这实际上是实现滚动效果的渲染对象的基础。我想重新实施这项计划未免太过分了。但也许可以将其子类化并从那里开始(也可能是过度杀戮)
- 如果我们在层次结构中走得更高,我们会找到抽象类RendersLiverMultiboxAdapter。一条有多个盒子子的长条。这为RenderLiverMultiboxAdapter提供了动态的子对象。这两个都是抽象类。那么,也许从这里开始,扩展这些类
- 这扩展了RenderShielveMultiboxAdapter,并提供沿主轴布置的长方体子对象。子类由实现RenderLiverBoxChildManager的类交付。 实现RenderSliverBoxChildManager。因此,RenderLiverList和SliverMultiboxAdapterElement是RenderLiverMultiboxAdapter和RenderLiverBoxChildManager的具体实现。我想我可以扩展这些课程。但是如果我这样做,我无论如何都必须重新实现performLayout方法。那么,是否可以重用SliverMultiboxAdapter元素并扩展RendersLiverMultiboxAdapter
- 该类最终创建渲染对象(一个RenderSliverList,其中SliverMultiboxAdapterElement作为子管理器),并向SliverMultiboxAdapterElement提供一个SliverChildDelegate,而SliverMultiboxAdapterElement又为其惰性地构建子对象。SliverList沿主轴将多个长方体子对象放置在线性阵列中。它使用一个扩展类来动态提供子对象。它可以放置在CustomScrollViews狭缝阵列中。这是在CustomScrollView中创建列表的最具体的片段。那么,我是否可以简单地用这个来存档我的目标,即根据视频进行布局?到目前为止,我试图为CustomScrollView提供一个ScrollController来截取滚动偏移量,然后根据滚动偏移量和元素索引构建子元素。但这样做时,scrollview不再滚动。仅当所有单元的总高度超过视口时,它才会滚动
那么,我真的需要扩展RenderShielveMultiboxAdapter并自己实现perfromLayout方法吗?对我来说,这似乎是现在唯一的选择…从第一眼就很难理解条子的逻辑 但重要的是滑度学课程
- paintOrigin——将其视为一种delta y。当你想制作小部件时 固定在屏幕上,您需要从顶部推动它
- constraints.scrollOffset显示逻辑位置的滚动偏移 小部件
- scrollExtent显示小部件的逻辑高度。It帮助小部件 知道你滚动了所有的条子
导入'dart:math'作为数学;
进口“包装:颤振/基础.dart”;
导入“package:flatter/rendering.dart”;
导入“package:flatter/widgets.dart”;
进口“包装:颤振/材料.省道”;
void main()=>runApp(MyApp());
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
标题:“颤振演示”,
主题:主题数据(
主样本:颜色。蓝色,
),
家:脚手架(
正文:MyHomePage(),
),
);
}
}
类MyHomePage扩展StatefulWidget{
@凌驾
_MyHomePageState createState()=>\u MyHomePageState();
}
类_MyHomePageState扩展状态{
最终全局键=全局键();
RenderObject-ansestor;
@凌驾
void initState(){
WidgetsBinding.instance.addPostFrameCallback(_getPosition);
super.initState();
}
_getPosition(u41;{
设置状态(){
ansestor=_key.currentContext.FindEnderObject();
});
}
@凌驾
小部件构建(构建上下文){
返回布局生成器(生成器:(上下文、约束){
返回自定义滚动视图(
物理:ClampingScrollPhysics(),
键:_键,
条子:[
银条(
我最初是这样说的:是的,
ansestor:ansestor,
子项:\u项(
标题:“第一个标题”,
文件名:'item_1',
),
),
银条(
ansestor:ansestor,
子项:\u项(
标题:“第二个标题”,
文件名:'item_2',
),
),
银条(
ansestor:ansestor,
子项:\u项(
标题:“第三个标题”,
文件名:'item_3',
),
),
银条(
ansestor:ansestor,
子项:\u项(
标题:“第四个标题”,
文件名:'item_4',
),
),
银条(
ansestor:ansestor,
子项:\u项(
标题:“第五名”,
文件名:“第5项”,
),
),
银条(
ansestor:ansestor,
子项:\u项(
标题:“第一个标题”,
文件名:“第6项”,
),
),
滑动双轴适配器(
子:容器(
儿童:中心(
子项:文本('end'),
),
嗨
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,
),
),
),
);
},
);
}
}