Para obtener el tamaño / posición de un widget en la pantalla, puede usar GlobalKey
para obtenerlo BuildContext
y luego encontrar el RenderBox
de ese widget específico, que contendrá su posición global y tamaño renderizado.
Solo una cosa a tener en cuenta: ese contexto puede no existir si el widget no se representa. Lo que puede causar un problema ListView
ya que los widgets se procesan solo si son potencialmente visibles.
Otro problema es que no puede obtener un widget RenderBox
durante la build
llamada, ya que aún no se ha renderizado.
¡Pero necesito el tamaño durante la construcción! ¿Que puedo hacer?
Hay un widget genial que puede ayudar: Overlay
y es OverlayEntry
. Se utilizan para mostrar widgets encima de todo lo demás (similar a la pila).
Pero lo mejor es que están en un build
flujo diferente ; se construyen después de los widgets normales.
Eso tiene una implicación genial: OverlayEntry
puede tener un tamaño que depende de los widgets del árbol de widgets real.
Bueno. Pero, ¿no es necesario reconstruir OverlayEntry manualmente?
Ellos si. Pero hay otra cosa a tener en cuenta:, ScrollController
pasado a Scrollable
, es un similar escuchable a AnimationController
.
Lo que significa que podría combinar un AnimatedBuilder
con un ScrollController
, tendría el efecto encantador de reconstruir su widget automáticamente en un pergamino. Perfecto para esta situación, ¿verdad?
Combinando todo en un ejemplo:
En el siguiente ejemplo, verá una superposición que sigue a un widget en el interior ListView
y comparte la misma altura.
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();
},
);
}
}
Nota :
Cuando navegue a una pantalla diferente, llame a seguir, de lo contrario, permanecerá visible.
sticky.remove();