我想实现此视频中的布局(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)之类的东西? 任何建议将不胜感激,thx:)
更新: 据我所知,我需要使用CustomScrollView(如果我错了,请纠正我)。但是我对Flutter框架留给我的选项感到有些不知所措。我不确定从文档中我需要扩展哪些类,或者我需要实现哪些接口来存档我的目标。我不知道我需要深入了解框架。当涉及到具有自定义滚动效果的条子和列表时,涉及以下类:
所以我真的需要扩展RenderSliverMultiBoxAdaptor并自己实现perfromLayout方法吗?对我来说,它似乎是现在唯一的选择......
答案 0 :(得分:1)
从一开始就很难理解银条的逻辑。
但是重要的是SliverGeometry类
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,
),
),
),
);
},
);
}
}