Site icon Kiso

Thêm tương tác vào ứng dụng Flutter của bạn

Sau khi đã xây dựng được UI ở các phần trước, chúng ta sẽ thêm tương tác vào ứng dụng của chúng ta để nhận tương tác của người dùng và phản hồi lại? Trong hướng dẫn này, bạn sẽ thêm tính tương tác vào một ứng dụng, cụ thể, bạn sẽ sửa đổi một icon để làm cho nó có thể click được bằng cách tạo một widget trạng thái tùy chỉnh quản lý hai widget không có trạng thái.

Giả sử ta đã xây dựng được một màn hình như ảnh sau.

Khi ứng dụng lần đầu tiên được mở, ngôi sao được fill màu đỏ, cho thấy hồ này trước đây đã được ưa chuộng. Con số bên cạnh ngôi sao cho thấy 41 người đã thích hồ này. Sau khi hoàn thành hướng dẫn này, chạm vào ngôi sao sẽ loại bỏ trạng thái ưa thích của nó, thay thế ngôi sao bằng một icon không được fill màu và giảm số lượng. Chạm một lần nữa sẽ yêu thích hồ, fill màu cho ngôi sao và tăng số lượng.


Widget tùy chỉnh bạn sẽ tạo

Để thực hiện điều này, bạn sẽ tạo một widget tùy chỉnh duy nhất bao gồm cả ngôi sao và số đếm, chính là các widget con. Vì việc chạm vào ngôi sao sẽ thay đổi trạng thái cho cả hai widget, vì vậy cùng một widget sẽ quản lý cả hai.

Quản lý trạng thái của Widget

Vấn đề ở đây là gì?

Ai quản lý trạng thái của widget? Tự bản thân các widget? Các widget cha? Cả hai? Một đối tượng khác? Câu trả lời phụ thuộc vao nhiều thứ. Có một số cách hợp lệ để làm cho widget của bạn tương tác. Bạn, với tư cách là người thiết kế widget, đưa ra quyết định dựa trên cách bạn mong đợi widget của mình được sử dụng. Dưới đây là những cách phổ biến nhất để quản lý trạng thái:

Làm thế nào để bạn quyết định sử dụng phương pháp nào? Các nguyên tắc sau sẽ giúp bạn quyết định:

Nếu còn đang nghi ngờ, hãy bắt đầu bằng cách quản lý trạng thái trong widget cha.

Ta hãy xem các ví dụ về các cách quản lý trạng thái khác nhau bằng cách tạo ba ví dụ đơn giản: TapboxA, TapboxB và TapboxC. Tất cả các ví dụ đều hoạt động tương tự nhau, mỗi nhóm tạo ra một container, khi tap, bật giữa một hộp màu xanh lá cây hoặc màu xám. Boolean _active xác định màu: xanh lục với trạng thái active hoặc xám đối với không active.

Các ví dụ này sử dụng GestureDetector để ghi lại hoạt động trên Container.

Các widget quản lý trạng thái riêng của nó

Đôi khi hợp lý nhất đối với các widget là quản lý trạng thái của nó trong nội bộ. Ví dụ, ListView tự động cuộn khi nội dung của nó vượt quá hộp kết xuất. Hầu hết các developer sử dụng ListView không muốn quản lý hành vi cuộn của ListView, vì vậy chính ListView quản lý cuộn của nó.

Lớp _TapboxAState :

// TapboxA manages its own state.

//------------------------- TapboxA ----------------------------------

class TapboxA extends StatefulWidget {
  TapboxA({Key key}) : super(key: key);

  @override
  _TapboxAState createState() => _TapboxAState();
}

class _TapboxAState extends State<TapboxA> {
  bool _active = false;

  void _handleTap() {
    setState(() {
      _active = !_active;
    });
  }

  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        child: Center(
          child: Text(
            _active ? 'Active' : 'Inactive',
            style: TextStyle(fontSize: 32.0, color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: BoxDecoration(
          color: _active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}

//------------------------- MyApp ----------------------------------

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Demo'),
        ),
        body: Center(
          child: TapboxA(),
        ),
      ),
    );
  }
}

Các widget cha quản lý trạng thái của widget con

Thông thường, hợp lý nhất là widget cha quản lý trạng thái và cho biết widget con của nó khi nào cần cập nhật. Ví dụ: IconButton cho phép bạn coi biểu tượng là nút có thể tap được. IconButton là một widget phi trạng thái bởi vì Flutter đã quyết định rằng widget cha cần biết liệu nút đó đã được tap vào hay chưa, để nó có thể thực hiện hành động thích hợp.

Trong ví dụ sau, TapboxB xuất trạng thái của nó sang widget cha thông qua một callback. Vì TapboxB không quản lý bất kỳ trạng thái nào, nên nó subclass từ StatlessWidget.

// ParentWidget manages the state for TapboxB.

//------------------------ ParentWidget --------------------------------

class ParentWidget extends StatefulWidget {
  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: TapboxB(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//------------------------- TapboxB ----------------------------------

class TapboxB extends StatelessWidget {
  TapboxB({Key key, this.active: false, @required this.onChanged})
      : super(key: key);

  final bool active;
  final ValueChanged<bool> onChanged;

  void _handleTap() {
    onChanged(!active);
  }

  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        child: Center(
          child: Text(
            active ? 'Active' : 'Inactive',
            style: TextStyle(fontSize: 32.0, color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: BoxDecoration(
          color: active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}

Mẹo: Khi tạo API, hãy cân nhắc sử dụng chú thích @required cho bất kỳ tham số nào mà mã của bạn dựa vào. Để sử dụng @required , hãy import thư viện ‘package:flutter/foundation.dart’ ;

Cách tiếp cận kết hợp

Đối với một số widget, cách tiếp cận kết hợp là hợp lý nhất. Trong trường hợp này, widget trạng thái quản lý một số trạng thái và widget cha quản lý các khía cạnh khác của trạng thái.

Trong ví dụ TapboxC , khi tap xuống, một đường viền màu xanh đậm xuất hiện xung quanh hộp. Khi nhả ra, đường viền biến mất và màu của hộp thay đổi. TapboxC xuất trạng thái active của nó sang cha nhưng quản lý trạng thái highlight của nó trong nội bộ. Ví dụ này có hai đối tượng ParentWidgetState và TapboxCState .

Đối tượng _ParentWidgetState :

Đối tượng _TapboxCState :

//---------------------------- ParentWidget ----------------------------

class ParentWidget extends StatefulWidget {
  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: TapboxC(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//----------------------------- TapboxC ------------------------------

class TapboxC extends StatefulWidget {
  TapboxC({Key key, this.active: false, @required this.onChanged})
      : super(key: key);

  final bool active;
  final ValueChanged<bool> onChanged;

  _TapboxCState createState() => _TapboxCState();
}

class _TapboxCState extends State<TapboxC> {
  bool _highlight = false;

  void _handleTapDown(TapDownDetails details) {
    setState(() {
      _highlight = true;
    });
  }

  void _handleTapUp(TapUpDetails details) {
    setState(() {
      _highlight = false;
    });
  }

  void _handleTapCancel() {
    setState(() {
      _highlight = false;
    });
  }

  void _handleTap() {
    widget.onChanged(!widget.active);
  }

  Widget build(BuildContext context) {
    // This example adds a green border on tap down.
    // On tap up, the square changes to the opposite state.
    return GestureDetector(
      onTapDown: _handleTapDown, // Handle the tap events in the order that
      onTapUp: _handleTapUp, // they occur: down, up, tap, cancel
      onTap: _handleTap,
      onTapCancel: _handleTapCancel,
      child: Container(
        child: Center(
          child: Text(widget.active ? 'Active' : 'Inactive',
              style: TextStyle(fontSize: 32.0, color: Colors.white)),
        ),
        width: 200.0,
        height: 200.0,
        decoration: BoxDecoration(
          color:
              widget.active ? Colors.lightGreen[700] : Colors.grey[600],
          border: _highlight
              ? Border.all(
                  color: Colors.teal[700],
                  width: 10.0,
                )
              : null,
        ),
      ),
    );
  }
}

Nguồn (https://flutter.io/docs/development/ui/interactive#managing-state)
Còn tiếp….

Exit mobile version