要获取屏幕上小部件的大小/位置,您可以使用GlobalKey
它来获取它BuildContext
,然后找到该RenderBox
特定小部件的,其中将包含其全局位置和渲染大小。
只需要注意一件事:如果未呈现小部件,则该上下文可能不存在。这可能会导致问题,ListView
因为小部件仅在它们可能可见时才会呈现。
RenderBox
另一个问题是您在调用期间无法获取小部件,build
因为小部件尚未呈现。
但是如果我需要在构建过程中获取大小怎么办!我能做些什么?
有一个很酷的小部件可以提供帮助:Overlay
及其OverlayEntry
. 它们用于在其他所有内容之上显示小部件(类似于堆栈)。
但最酷的是它们处于不同的build
流程中。它们是在常规小部件之后构建的。
这有一个非常酷的含义:OverlayEntry
可以具有取决于实际小部件树的小部件的大小。
好的。但是OverlayEntry不需要手动重建吗?
是的,他们有。但是还有一点需要注意:ScrollController
传递给 a 的Scrollable
,是一个可听的,类似于AnimationController
.
这意味着您可以将 aAnimatedBuilder
与 a结合使用ScrollController
,它会产生在滚动上自动重建小部件的可爱效果。非常适合这种情况,对吧?
将所有内容组合成一个示例:
在以下示例中,您将看到一个覆盖在内部小部件之后ListView
并共享相同高度。
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final controller = ScrollController();
OverlayEntry sticky;
GlobalKey stickyKey = GlobalKey();
@override
void initState() {
if (sticky != null) {
sticky.remove();
}
sticky = OverlayEntry(
builder: (context) => stickyBuilder(context),
);
SchedulerBinding.instance.addPostFrameCallback((_) {
Overlay.of(context).insert(sticky);
});
super.initState();
}
@override
void dispose() {
sticky.remove();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
controller: controller,
itemBuilder: (context, index) {
if (index == 6) {
return Container(
key: stickyKey,
height: 100.0,
color: Colors.green,
child: const Text("I'm fat"),
);
}
return ListTile(
title: Text(
'Hello $index',
style: const TextStyle(color: Colors.white),
),
);
},
),
);
}
Widget stickyBuilder(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (_,Widget child) {
final keyContext = stickyKey.currentContext;
if (keyContext != null) {
// widget is visible
final box = keyContext.findRenderObject() as RenderBox;
final pos = box.localToGlobal(Offset.zero);
return Positioned(
top: pos.dy + box.size.height,
left: 50.0,
right: 50.0,
height: box.size.height,
child: Material(
child: Container(
alignment: Alignment.center,
color: Colors.purple,
child: const Text("^ Nah I think you're okay"),
),
),
);
}
return Container();
},
);
}
}
注意:
当导航到不同的屏幕时,呼叫跟随否则粘滞将保持可见。
sticky.remove();