it-swarm.com.de

Wie erreiche ich die Höhe eines Widgets?

Ich verstehe nicht, wie mit LayoutBuilder die Höhe eines Widgets ermittelt wird.

Ich muss die Liste der Widgets anzeigen und ihre Höhe ermitteln, damit ich einige spezielle Bildlaufeffekte berechnen kann. Ich entwickle ein Paket und andere Entwickler stellen Widgets zur Verfügung (ich kontrolliere sie nicht). Ich habe gelesen, dass LayoutBuilder verwendet werden kann, um Höhe zu bekommen.

In einem sehr einfachen Fall habe ich versucht, Widget in LayoutBuilder.builder einzubinden und in den Stapel zu legen, aber ich erhalte immer minHeight0.0 Und maxHeightINFINITY. Missbrauche ich den LayoutBuilder?

[~ # ~] edit [~ # ~] : Es scheint, dass LayoutBuilder ein No-Go ist. Ich fand das CustomSingleChildLayout , das fast eine Lösung ist.

Ich habe diesen Delegaten erweitert und konnte die Höhe des Widgets in der getPositionForChild(Size size, Size childSize) -Methode ermitteln. ABER die erste Methode, die aufgerufen wird, ist Size getSize(BoxConstraints constraints) und als Einschränkung erhalte ich 0 zu INFINITY, weil ich diese CustomSingleChildLayouts in einer ListView lege.

Mein Problem ist, dass SingleChildLayoutDelegate getSize so funktioniert, als ob es die Höhe einer Ansicht zurückgeben müsste. Ich kenne die Größe eines Kindes in diesem Moment nicht. Ich kann nur constraints.smallest (das ist 0, die Höhe ist 0) oder constraints.biggest zurückgeben, das unendlich ist und die App zum Absturz bringt.

In den Dokumenten heißt es sogar:

... aber die Größe des Elternteils kann nicht von der Größe des Kindes abhängen.

Und das ist eine seltsame Einschränkung.

31
Gudin

Um die Größe/Position eines Widgets auf dem Bildschirm abzurufen, können Sie GlobalKey verwenden, um dessen BuildContext abzurufen und dann das RenderBox dieses spezifischen Widgets zu finden, das seine globale Position enthält und gerendert wird Größe.

Beachten Sie Folgendes: Dieser Kontext ist möglicherweise nicht vorhanden, wenn das Widget nicht gerendert wird. Dies kann zu Problemen mit ListView führen, da Widgets nur gerendert werden, wenn sie möglicherweise sichtbar sind.

Ein weiteres Problem ist, dass Sie das RenderBox eines Widgets während des build -Aufrufs nicht abrufen können, da das Widget noch nicht gerendert wurde.


Muss ich aber bei der Erstellung auf die Größe achten! 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 Stapel).

Aber das Coolste ist, dass sie sich in einem anderen build Fluss befinden. Sie werden erstellt nach regulären Widgets.

Das hat eine super coole Bedeutung: OverlayEntry kann eine Größe haben, die von den Widgets des aktuellen Widget-Baums abhängt.


Okay. Aber muss OverlayEntry nicht manuell neu erstellt werden?

Ja, das tun sie. Aber es gibt noch eine andere Sache, die Sie beachten sollten: ScrollController, an Scrollable weitergegeben, ist ähnlich zu AnimationController hörbar.

Das heißt, Sie könnten ein AnimatedBuilder mit einem ScrollController kombinieren. Dies hätte den schönen Effekt, dass Ihr Widget automatisch in einem Bildlauf neu erstellt wird. Perfekt für diese Situation, oder?


Alles zu einem Beispiel zusammenfassen:

Im folgenden Beispiel sehen Sie eine Überlagerung, die auf ein Widget in ListView folgt 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(
      opaque: false,
      // lambda created to help working with hot-reload
      builder: (context) => stickyBuilder(context),
    );

    // not possible inside initState
    SchedulerBinding.instance.addPostFrameCallback((_) {
      Overlay.of(context).insert(sticky);
    });
    super.initState();
  }

  @override
  void dispose() {
    // remove possible overlays on dispose as they would be visible even after [Navigator.Push]
    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();
      },
    );
  }
}
52
Rémi Rousselet

Wenn ich das richtig verstehe, möchten Sie die Größe einiger beliebiger Widgets messen und können diese Widgets mit einem anderen Widget umschließen. In diesem Fall sollte die Methode in diese Antwort für Sie funktionieren.

Grundsätzlich besteht die Lösung darin, einen Rückruf im Widget-Lebenszyklus zu binden, der nach dem Rendern des ersten Frames aufgerufen wird. Von dort aus können Sie auf context.size Zugreifen. Der Haken ist, dass Sie das Widget, das Sie messen möchten, in ein statusbehaftetes Widget einschließen müssen. Und wenn Sie die Größe in build() unbedingt benötigen, können Sie nur im zweiten Rendering darauf zugreifen (es ist nur nach dem ersten Rendering verfügbar).

0
Gan Quan