Kodokon kodokon.com

State teilen: State hochziehen und Callbacks

Halte den State im gemeinsamen Vorfahren und mach aus deinen Kind-Widgets kontrollierte Widgets, die Änderungen über Callbacks zurückmelden.

8 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Eine klassische Situation: Ein Mengenwähler und eine an anderer Stelle angezeigte Summe müssen dieselben Daten teilen. Die goldene Regel: Der State lebt im nächstgelegenen gemeinsamen Vorfahren der Widgets, die von ihm abhängen. Das Eltern-Widget besitzt die Daten, reicht sie als Parameter nach unten und erhält Änderungen über einen Callback, der nach oben zurückfließt. Das ist "lifting state up".

DART
import 'package:flutter/material.dart';

class CartScreen extends StatefulWidget {
  const CartScreen({super.key});

  @override
  State<CartScreen> createState() => _CartScreenState();
}

class _CartScreenState extends State<CartScreen> {
  int _quantity = 1;

  void _updateQuantity(int value) {
    setState(() => _quantity = value);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Cart')),
      body: Column(
        children: [
          QuantitySelector(
            quantity: _quantity,
            onChanged: _updateQuantity,
          ),
          Text('Total: ${_quantity * 20} €'),
        ],
      ),
    );
  }
}
Das Eltern-Widget besitzt den State und gibt Wert und Callback heraus.

Das Kind wird zu einem kontrollierten Widget: Es ist ein StatelessWidget, speichert nichts, zeigt den Wert an, den es erhält, und signalisiert jede Absicht über onChanged. ValueChanged<int> ist einfach ein Alias für void Function(int). Beachte den Minus-Button, der bei Menge 1 deaktiviert ist: null an onPressed zu übergeben genügt, und diese Logik ergibt sich direkt aus dem erhaltenen Wert.

DART
import 'package:flutter/material.dart';

class QuantitySelector extends StatelessWidget {
  const QuantitySelector({
    super.key,
    required this.quantity,
    required this.onChanged,
  });

  final int quantity;
  final ValueChanged<int> onChanged;

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        IconButton(
          icon: const Icon(Icons.remove),
          onPressed: quantity > 1
              ? () => onChanged(quantity - 1)
              : null,
        ),
        Text('$quantity'),
        IconButton(
          icon: const Icon(Icons.add),
          onPressed: () => onChanged(quantity + 1),
        ),
      ],
    );
  }
}
Das Kind zeigt den Wert an, den es erhält, und meldet Absichten nach oben.

Du nutzt diesen Vertrag ständig, ohne es zu merken: Checkbox, Slider und Switch sind alle kontrollierte Widgets (value + onChanged). Wenn der Baum tiefer wird und es mühsam wird, Callbacks durch fünf Ebenen zu fädeln, ist es Zeit, Lösungen für State-Management zu entdecken - das ist das Thema des nächsten Moduls.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Wo sollte man State platzieren, den sich zwei Geschwister-Widgets teilen?
    • In jedem der beiden Widgets
    • Im nächstgelegenen gemeinsamen Vorfahren
    • In einer globalen Variable
    • In der MaterialApp
  2. Wie fordert ein Kind-Widget eine Änderung des State an?
    • Es ruft setState direkt auf seinem Eltern-Widget auf
    • Es ändert die Property, die es als Parameter erhalten hat
    • Es ruft den Callback auf, den das Eltern-Widget bereitstellt
  3. Was ist ein kontrolliertes Widget?
    • Ein Widget, das seinen Wert erhält und Änderungen über einen Callback meldet, ohne eigenen State
    • Ein Widget, das setState verwenden muss
    • Ein Widget, das von Unit-Tests abgedeckt ist
    • Ein Widget, das als const deklariert ist