Aby uzyskać rozmiar / pozycję widżetu na ekranie, możesz użyć GlobalKeygo, BuildContextaby następnie znaleźć RenderBoxten konkretny widżet, który będzie zawierał jego globalną pozycję i renderowany rozmiar.
Tylko jedna rzecz, na którą należy uważać: ten kontekst może nie istnieć, jeśli widżet nie jest renderowany. Co może powodować problem z ListViewwidżetami as jest renderowany tylko wtedy, gdy są one potencjalnie widoczne.
Innym problemem jest to, że nie możesz pobrać widżetu RenderBoxpodczas buildpołączenia, ponieważ widżet nie został jeszcze wyrenderowany.
Ale potrzebuję rozmiaru podczas budowy! Co mogę zrobić?
Jest jeden fajny widget, który może pomóc: Overlayi jego OverlayEntry. Służą do wyświetlania widżetów na wszystkim innym (podobnie do stosu).
Ale najfajniejsze jest to, że są na innym buildnurcie; są tworzone na podstawie zwykłych widgetów.
Ma to jedną super fajną konsekwencję: OverlayEntrymoże mieć rozmiar zależny od widżetów w aktualnym drzewie widżetów.
W porządku. Ale czy OverlayEntry nie wymaga ręcznej przebudowy?
Tak, robią. Ale jest jeszcze jedna rzecz, o której należy pamiętać: ScrollControllerprzekazana do a Scrollable, jest podobna do słuchania AnimationController.
Oznacza to, że możesz połączyć widżet AnimatedBuilderz a ScrollController, miałoby cudowny efekt, gdybyś automatycznie przebudował swój widżet na zwoju. Idealny w tej sytuacji, prawda?
Łącząc wszystko w przykład:
W poniższym przykładzie zobaczysz nakładkę, która podąża za widżetem wewnątrz ListViewi ma tę samą wysokość.
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();
},
);
}
}
Uwaga :
Podczas przechodzenia do innego ekranu wywołanie śledzone w przeciwnym razie pozostanie widoczne.
sticky.remove();