Um die Größe / Position eines Widgets auf dem Bildschirm anzuzeigen, können Sie es verwenden GlobalKey
, um es BuildContext
zu ermitteln und dann das zu findenRenderBox
dieses spezielle Widget, das seine globale Position und gerenderte Größe enthält.
Nur eines ist zu beachten: Dieser Kontext existiert möglicherweise nicht, wenn das Widget nicht gerendert wird. Dies kann zu Problemen führen, ListView
da Widgets nur gerendert werden, wenn sie möglicherweise sichtbar sind.
Ein weiteres Problem ist, dass Sie RenderBox
während eines build
Anrufs keine Widgets erhalten können , da das Widget noch nicht gerendert wurde.
Aber ich muss die Größe während des Builds! Was kann ich tun?
Es gibt ein cooles Widget, das helfen kann: Overlay
und sein OverlayEntry
. Sie werden verwendet, um Widgets über allem anderen anzuzeigen (ähnlich wie beim Stapel).
Aber das Coolste ist, dass sie sich in einem anderen build
Fluss befinden. Sie werden nach regulären Widgets erstellt.
Das hat eine super coole Implikation: OverlayEntry
Kann eine Größe haben, die von den Widgets des tatsächlichen Widget-Baums abhängt.
In Ordnung. Aber muss OverlayEntry nicht manuell neu erstellt werden?
Ja, das tun sie. Aber es gibt noch eine andere Sache, die Sie beachten sollten: ScrollController
Übergeben an a Scrollable
, ist eine hörbare ähnlich wie AnimationController
.
Das heißt, Sie könnten ein AnimatedBuilder
mit einem kombinieren ScrollController
, es hätte den schönen Effekt, Ihr Widget automatisch auf einer Schriftrolle neu zu erstellen. Perfekt für diese Situation, oder?
Alles zu einem Beispiel zusammenfassen:
Im folgenden Beispiel sehen Sie eine Überlagerung, die einem Widget im Inneren folgt ListView
und dieselbe Höhe aufweist.
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) {
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();
},
);
}
}
Hinweis :
Wenn Sie zu einem anderen Bildschirm navigieren, bleibt der folgende Anruf sichtbar, da er sonst sichtbar bleibt.
sticky.remove();