Home Coding Bắt đầu với Flutter – Dogs App: Custom Card Widget – widget tuỳ chỉnh có thể tái sử dụng

Bắt đầu với Flutter – Dogs App: Custom Card Widget – widget tuỳ chỉnh có thể tái sử dụng

by Khanh Tran

Hôm nay chúng ta cùng nhau tạo một widget hiển thị thông tin và đánh giá một chú chó. Widget này có thể tuỳ chỉnh và tái sử dụng được. Đây là bước quan trọng trong lộ trình học flutter cho người mới.

Create Dog Card Widget

Bạn sẽ tạo một card giống như sau:

thẻ chó
Hình 1: Dog Card Widget

Tạo một tệp mới có tên ‘dog_card.dart`. Đầu tiên, tất cả những gì sẽ làm là hiển thị tên của một chú chó. Chúng ta sẽ sử dụng StatefulWidget.

// dog_card.dart

import 'package:flutter/material.dart';

import 'dog_model.dart';

class DogCard extends StatefulWidget {
  final Dog dog;

  DogCard(this.dog);

  @override
  _DogCardState createState() => _DogCardState(dog);
}

class _DogCardState extends State<DogCard> {
   Dog dog;

   _DogCardState(this.dog);

  @override
  Widget build(BuildContext context) {
    return Text(widget.dog.name);
  }
}

Tiếp the0, Để DogCardxuất hiện, hãy sửa đổi phương thứcbuild trong _MyHomePageState main.dart:

// main.dart
@override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
          backgroundColor: Colors.black87,
        ),
        body: Container(
          child: DogCard(initialDoggos[1]), // New code
        ),
    );
  }

Và đừng quên import dog_card.dart:

// main.dart
import 'package:flutter/material.dart';
import 'dog_card.dart';
import 'dog_model.dart';
Hình 2: DogCard

Đây mới là khởi đầu, cùng đi tiếp nhé!

Dog Card UI

Có hai phần trong widget này. Gồm một image và một thẻ hiện thông tin và đánh giá.

Thêm một getter vào _DogCardState class:

// dog_card.dart
// A class property that represents the URL flutter will render
// from the Dog class.
String renderUrl;
Widget get dogImage {
  return Container(
      // You can explicitly set heights and widths on Containers.
      // Otherwise they take up as much space as their children.
    width: 100.0,
    height: 100.0,
      // Decoration is a property that lets you style the container.
      // It expects a BoxDecoration.
    decoration: BoxDecoration(
      // BoxDecorations have many possible properties.
      // Using BoxShape with a background image is the
      // easiest way to make a circle cropped avatar style image.
      shape: BoxShape.circle,
      image: DecorationImage(
        // Just like CSS's `imagesize` property.
        fit: BoxFit.cover,
        // A NetworkImage widget is a widget that
        // takes a URL to an image.
        // ImageProviders (such as NetworkImage) are ideal
        // when your image needs to be loaded or can change.
        // Use the null check to avoid an error.
        image: NetworkImage(renderUrl ?? ''),
      ),
    ),
  );
}

Để xem hình ảnh này, trước tiên bạn cần yêu cầu class Dog lấy hình ảnh đó từ internets.

Trong DogCard, hãy thêm đoạn code sau vào _DogCardState class:

// dog_card.dart
// State classes run this method when the state is created.
// You shouldn't do async work in initState, so we'll defer it
// to another method.
void initState() {
  super.initState();
  renderDogPic();
}
// IRL, we'd want the Dog class itself to get the image
// but this is a simpler way to explain Flutter basics
void renderDogPic() async {
  // this makes the service call
  await dog.getImageUrl();
  // setState tells Flutter to rerender anything that's been changed.
  // setState cannot be async, so we use a variable that can be overwritten
  if (mounted) { // Avoid calling `setState` if the widget is no longer in the widget tree.
    setState(() {
      renderUrl = dog.imageUrl;
    });
  }
}

Bây giờ ta đã lấy được avatar cho một chú cún. Giờ là lúc sử dụng Stack để xây dựng giao diện cho DogCard của chúng ta.

Cũng giống như Column hay Row, Stack cũng là 1 tập hợp các widget, tuy nhiên điểm khác biệt là các widget con trong stack có thể chồng lên nhau. Thường đi kèm với widget này là widget Positioned để căn chỉnh vị trí của từng widget trong Stack.

Stack summary:

  • Sử dụng cho các widgets muốn đặt lên trên một widget con khác
  • Widget đầu trong danh sách widget con sẽ là base widget, và các widget còn lại khi hiển thị sẽ nằm trên base widget.
  • Stack thì không thể scroll được
  • Bạn có thể lựa chọn cắt bỏ widget con nào mà vượt quá khung hiển thị.
// dog_card.dart
@override
Widget build(BuildContext context) {
  // Start with a container so we can add layout and style props:
  return Container(
    // Arbitrary number that I decided looked good:
    height: 115.0,
    // A stack takes children, with a list of widgets.
    child: Stack(
      children: <Widget>[
        // position our dog image, so we can explicitly place it.
        // We'll place it after we've made the card.
        Positioned(
        child: dogImage,
        ),
      ],
    ),
  );
}
Hình 3: DogCard avatar

Create một card và layout chúng trong _DogCardState, đây là thẻ hiển thị thông tin về chú cún của chúng ta:

/ dog_card.dart
Widget get dogCard {
  // A new container
  // The height and width are arbitrary numbers for styling.
  return Container(
    width: 290.0,
    height: 115.0,
    child: Card(
      color: Colors.black87,
      // Wrap children in a Padding widget in order to give padding.
      child: Padding(
        // The class that controls padding is called 'EdgeInsets'
        // The EdgeInsets.only constructor is used to set
        // padding explicitly to each side of the child.
        padding: const EdgeInsets.only(
          top: 8.0,
          bottom: 8.0,
          left: 64.0,
        ),
        // Column is another layout widget -- like stack -- that
        // takes a list of widgets as children, and lays the
        // widgets out from top to bottom.
        child: Column(
          // These alignment properties function exactly like
          // CSS flexbox properties.
          // The main axis of a column is the vertical axis,
          // `MainAxisAlignment.spaceAround` is equivalent of
          // CSS's 'justify-content: space-around' in a vertically
          // laid out flexbox.
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            Text(widget.dog.name,
                // Themes are set in the MaterialApp widget at the root of your app.
                // They have default values -- which we're using because we didn't set our own.
                // They're great for having consistent, app-wide styling that's easily changed.
                style: Theme.of(context).textTheme.headline),
            Text(widget.dog.location,
                style: Theme.of(context).textTheme.subhead),
            Row(
              children: <Widget>[
                Icon(
                  Icons.star,
                ),
                Text(': ${widget.dog.rating} / 10')
              ],
            )
          ],
        ),
      ),
    ),
  );
}

Sửa lại phương thức build:

// dog_card.dart
@override
Widget build(BuildContext context) {
  return Padding(
    padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
    child: Container(
      height: 115.0,
      child: Stack(
        children: <Widget>[
          Positioned(
            left: 50.0,
            child: dogCard,
          ),
          Positioned(top: 7.5, child: dogImage),
        ],
      ),
    ),
  );
}
Hình 4: DogCard complete

You may also like

Leave a Comment