Kodokon kodokon.com

การแบ่งปันสเตต: การยกสเตตขึ้นและคอลแบ็ก

เก็บสเตตไว้ในบรรพบุรุษร่วม แล้วเปลี่ยนวิดเจ็ตลูกของคุณให้เป็นวิดเจ็ตแบบควบคุมที่รายงานการเปลี่ยนแปลงกลับขึ้นไปผ่านคอลแบ็ก

8 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

สถานการณ์คลาสสิก ตัวเลือกจำนวน (quantity selector) กับยอดรวมที่แสดงอยู่ที่อื่นจำเป็นต้องใช้ข้อมูลชุดเดียวกัน กฎทองคำคือ สเตตอยู่ใน บรรพบุรุษร่วมที่ใกล้ที่สุด ของวิดเจ็ตที่พึ่งพามัน วิดเจ็ตแม่เป็นเจ้าของข้อมูล ส่งมันลงไปเป็นพารามิเตอร์ และรับการเปลี่ยนแปลงผ่าน คอลแบ็ก ที่ไหลย้อนกลับขึ้นมา นี่คือสิ่งที่เรียกว่า "การยกสเตตขึ้น" (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} €'),
        ],
      ),
    );
  }
}
วิดเจ็ตแม่เป็นเจ้าของสเตต และแจกจ่ายค่าและคอลแบ็กออกไป

วิดเจ็ตลูกกลายเป็น วิดเจ็ตแบบควบคุม มันเป็น StatelessWidget ไม่เก็บอะไรไว้เลย แสดงค่าที่มันได้รับ และส่งสัญญาณความตั้งใจแต่ละครั้งผ่าน onChanged โดย ValueChanged<int> ก็เป็นเพียงชื่อแทน (alias) ของ void Function(int) สังเกตปุ่มลบที่ถูกปิดใช้งานเมื่อจำนวนเท่ากับ 1 เพียงส่ง null ให้ onPressed ก็เพียงพอ และตรรกะนี้สืบเนื่องมาจากค่าที่ได้รับโดยตรง

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),
        ),
      ],
    );
  }
}
วิดเจ็ตลูกแสดงค่าที่มันได้รับ และรายงานความตั้งใจกลับขึ้นไป

คุณใช้ข้อตกลงนี้อยู่ตลอดเวลาโดยไม่รู้ตัว Checkbox, Slider, Switch ล้วนเป็นวิดเจ็ตแบบควบคุมทั้งสิ้น (value + onChanged) เมื่อทรีลึกลงและการร้อยคอลแบ็กผ่านห้าระดับเริ่มน่าเบื่อ ก็จะถึงเวลาที่ต้องรู้จักโซลูชันการจัดการสเตต ซึ่งเป็นหัวข้อของโมดูลถัดไป

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. คุณควรวางสเตตที่ถูกแบ่งปันโดยวิดเจ็ตพี่น้องสองตัวไว้ที่ใด?
    • ไว้ในวิดเจ็ตทั้งสองตัว
    • ไว้ในบรรพบุรุษร่วมที่ใกล้ที่สุด
    • ไว้ในตัวแปรโกลบอล
    • ไว้ใน MaterialApp
  2. วิดเจ็ตลูกร้องขอให้เปลี่ยนสเตตอย่างไร?
    • มันเรียก setState บนวิดเจ็ตแม่โดยตรง
    • มันแก้ไขคุณสมบัติที่ได้รับมาเป็นพารามิเตอร์
    • มันเรียกใช้คอลแบ็กที่วิดเจ็ตแม่จัดหาให้
  3. วิดเจ็ตแบบควบคุมคืออะไร?
    • วิดเจ็ตที่รับค่าของมันและรายงานการเปลี่ยนแปลงผ่านคอลแบ็ก โดยไม่มีสเตตเป็นของตัวเอง
    • วิดเจ็ตที่ต้องใช้ setState
    • วิดเจ็ตที่มีการทดสอบหน่วย (unit test) ครอบคลุม
    • วิดเจ็ตที่ประกาศเป็น const