1. Bố cục Flutter

Khái niệm chính của cơ chế bố trí là widget. Chúng ta biết rằng sự flutter giả định mọi thứ như một widget Vì vậy, hình ảnh, biểu tượng, văn bản và thậm chí cả bố cục(layout) của ứng dụng của bạn đều là widget. Ở đây, một số thứ bạn không thấy trên giao diện người dùng ứng dụng của mình, chẳng hạn như các hàng, cột và lưới sắp xếp, ràng buộc và căn chỉnh các widget hiển thị cũng là các widget.

Flutter cho phép chúng ta tạo bố cục bằng cách soạn nhiều widget để xây dựng các widget phức tạp hơn. Ví dụ , chúng ta có thể thấy hình ảnh dưới đây hiển thị ba biểu tượng với nhãn bên dưới mỗi biểu tượng.

Trong hình ảnh thứ hai, chúng ta có thể thấy bố cục trực quan của hình ảnh trên. Hình ảnh này hiển thị một hàng gồm ba cột và các cột này chứa một biểu tượng và nhãn.

Trong hình trên, vùng chứa là một lớp widget cho phép chúng ta tùy chỉnh widget con. Nó chủ yếu được sử dụng để thêm đường viền, đệm, lề, màu nền và nhiều thứ khác. Tại đây, widget văn bản nằm dưới vùng chứa để thêm lề. Toàn bộ hàng cũng được đặt trong một vùng chứa để thêm lề và phần đệm xung quanh hàng. Ngoài ra, phần còn lại của giao diện người dùng được kiểm soát bởi các thuộc tính như màu sắc, kiểu văn bản, v.v.

2. Bố trí một widget

Hãy để chúng ta tìm hiểu cách chúng ta có thể tạo và hiển thị một widget đơn giản. Các bước sau đây cho biết cách bố trí widget:

Bước 1: Đầu tiên, bạn cần chọn một Bố cục widget.

Bước 2: Tiếp theo, tạo một widget hiển thị.

Bước 3: Sau đó, thêm widget hiển thị vào widget layout.

Bước 4: Cuối cùng, thêm widget bố cục vào trang mà bạn muốn hiển thị.

3. Các loại widget bố cục

Chúng tôi có thể phân loại widget bố cục thành hai loại:

  1. widget đơn
  2. widget đa

3.1 Các widget đơn

widget bố cục con duy nhất là một loại widget, có thể chỉ có một widget bên trong widget bố cục mẹ. Các widget này cũng có thể chứa chức năng bố cục đặc biệt. Flutter cung cấp cho chúng ta nhiều widget con để làm cho giao diện người dùng của ứng dụng trở nên hấp dẫn. Nếu chúng ta sử dụng các widget này một cách thích hợp, nó có thể tiết kiệm thời gian của chúng ta và làm cho code ứng dụng dễ đọc hơn. Danh sách các loại widget đơn lẻ khác nhau là:

Container: Đây là widget bố cục phổ biến nhất cung cấp các tùy chọn có thể tùy chỉnh để đặt màu, định vị và định cỡ các widget.

Center(  
  child: Container(  
    margin: const EdgeInsets.all(15.0),  
    color: Colors.blue,  
    width: 42.0,  
    height: 42.0,  
  ),  
)  

Padding: Nó là một widget được sử dụng để sắp xếp widget con của nó theo khoảng đệm đã cho. Nó chứa EdgeInsetsEdgeInsets.fromLTRB cho phía mong muốn mà bạn muốn cung cấp đệm.

const Greetings(  
  child: Padding(  
    padding: EdgeInsets.all(14.0),  
    child: Text('Hello Cafedev!'),  
  ),  
)  

Center: widget này cho phép bạn căn giữa widget trong chính nó.

Align: Đây là một widget, căn chỉnh widget của nó trong chính nó và định kích thước nó dựa trên kích thước của đối tượng con. Nó cung cấp nhiều quyền kiểm soát hơn để đặt widget ở vị trí chính xác mà bạn cần.

Center(  
  child: Container(  
    height: 110.0,  
    width: 110.0,  
    color: Colors.blue,  
    child: Align(  
      alignment: Alignment.topLeft,  
      child: FlutterLogo(  
        size: 50,  
      ),  
    ),  
  ),  
)  

SizedBox: widget này cho phép bạn cung cấp kích thước được chỉ định cho widget thông qua tất cả các màn hình.

SizedBox(  
  width: 300.0,  
  height: 450.0,  
  child: const Card(child: Text('Hello Cafedev!')),  
)  

AspectRatio: widget này cho phép bạn giữ kích thước của widget theo một tỷ lệ khung hình được chỉ định. 

AspectRatio(  
  aspectRatio: 5/3,  
  child: Container(  
    color: Colors.bluel,  
  ),  
),  

Baseline widget này thay đổi widget theo đường cơ sở của widget con bên trong. 

child: Baseline(  
         baseline: 30.0,  
         baselineType: TextBaseline.alphabetic,  
         child: Container(  
              height: 60,  
              width: 50,  
              color: Colors.blue,  
         ),  
)  

ConstrainedBox: Đây là một widget cho phép bạn buộc các ràng buộc bổ sung lên widget con của nó. Nó có nghĩa là bạn có thể buộc widget con có một ràng buộc cụ thể mà không làm thay đổi các thuộc tính của widget con.

ConstrainedBox(  
  constraints: new BoxConstraints(  
    minHeight: 150.0,  
    minWidth: 150.0,  
    maxHeight: 300.0,  
    maxWidth: 300.0,  
  ),  
  child: new DecoratedBox(  
    decoration: new BoxDecoration(color: Colors.red),  
  ),  
),  

CustomSingleChildLayout: Nó là một widget, chuyển từ bố cục của con đơn thành một delegate. Người được ủy quyền(delegate) quyết định vị trí của widget và cũng được sử dụng để xác định kích thước của widget.

FittedBox: Nó chia tỷ lệ và định vị widget theo sự phù hợp được chỉ định .

import 'package:flutter/material.dart';  
  
void main() => runApp(MyApp());  
  
class MyApp extends StatelessWidget {  
  // It is the root widget of your application.  
  @override  
  Widget build(BuildContext context) {  
    return MaterialApp(  
      title: 'Multiple Layout Widget',  
      debugShowCheckedModeBanner: false,  
      theme: ThemeData(  
        // This is the theme of your application.  
        primarySwatch: Colors.green,  
      ),  
      home: MyHomePage(),  
    );  
  }  
}  
class MyHomePage extends StatelessWidget {  
  
  @override  
  Widget build(BuildContext context) {  
    return Scaffold(  
        appBar: AppBar(title: Text("FittedBox Widget")),  
        body: Center(  
        child: FittedBox(child: Row(  
          children: <Widget>[  
            Container(  
              child: Image.asset('assets/computer.png'),  
              ),  
              Container(  
                child: Text("This is a widget"),  
              )  
            ],  
          ),  
          fit: BoxFit.contain,  
        )  
      ),  
    );  
  }  
}  

Đầu ra

FractionallySizedBox: Nó là một widget cho phép kích thước của widget con của nó theo phần nhỏ của không gian có sẵn.

Chiều cao và chiều rộng nội tại: Chúng là một widget cho phép chúng ta định kích thước widget của nó theo chiều cao và chiều rộng nội tại của widget con.

LimitedBox: widget này cho phép chúng ta giới hạn kích thước của nó chỉ khi nó không bị giới hạn.

Offstage: Nó được sử dụng để đo kích thước của một widget mà không cần đưa nó lên màn hình.

OverflowBox: Nó là một widget, cho phép áp đặt các ràng buộc khác nhau đối với widget con của nó so với nó nhận được từ cha mẹ. Nói cách khác, nó cho phép con làm tràn widget cha.

Thí dụ

import 'package:flutter/material.dart';  
  
void main() => runApp(MyApp());  
  
class MyApp extends StatelessWidget {  
  // It is the root widget of your application.  
  @override  
  Widget build(BuildContext context) {  
    return MaterialApp(  
      title: 'Single Layout Widget',  
      debugShowCheckedModeBanner: false,  
      theme: ThemeData(  
        // This is the theme of your application.  
        primarySwatch: Colors.blue,  
      ),  
      home: MyHomePage(),  
    );  
  }  
}  
class MyHomePage extends StatelessWidget {  
    
  @override  
  Widget build(BuildContext context) {  
    return Scaffold(  
      appBar: AppBar(  
        title: Text("OverflowBox Widget"),  
      ),  
      body: Center(  
      child: Container(  
        height: 50.0,  
        width: 50.0,  
        color: Colors.red,  
        child: OverflowBox(  
          minHeight: 70.0,  
          minWidth: 70.0,  
          child: Container(  
            height: 50.0,  
            width: 50.0,  
            color: Colors.blue,  
            ),  
          ),  
        ),  
      ),  
    );  
  }  
}

Đầu ra

4. Đa widget

Đa widget là một loại widget chứa nhiều hơn một widget con bên trong và cách bố trí của các widget này là duy nhất . Ví dụ: widget Hàng bố trí widget theo hướng ngang và widget Cột bố trí widget theo hướng dọc. Nếu chúng ta kết hợp widget Hàng và Cột, thì nó có thể xây dựng bất kỳ cấp độ nào của widget phức tạp.

Ở đây, chúng ta sẽ tìm hiểu các loại khác nhau của nhiều widget:

Row: Nó cho phép sắp xếp các widget con của nó theo hướng nằm ngang.

Thí dụ

import 'package:flutter/material.dart';  
  
void main() => runApp(MyApp());  
  
class MyApp extends StatelessWidget {  
  // It is the root widget of your application.  
  @override  
  Widget build(BuildContext context) {  
    return MaterialApp(  
      title: 'Multiple Layout Widget',  
      debugShowCheckedModeBanner: false,  
      theme: ThemeData(  
        // This is the theme of your application.  
        primarySwatch: Colors.blue,  
      ),  
      home: MyHomePage(),  
    );  
  }  
}  
class MyHomePage extends StatelessWidget {  
  @override  
  Widget build(BuildContext context) {  
    return Center(  
      child: Container(  
        alignment: Alignment.center,  
        color: Colors.white,  
        child: Row(  
          children: <Widget>[  
            Expanded(  
              child: Text('Peter', textAlign: TextAlign.center),  
            ),  
            Expanded(  
              child: Text('John', textAlign: TextAlign.center ),  
  
            ),  
            Expanded(  
              child: FittedBox(  
                fit: BoxFit.contain, // otherwise the logo will be tiny  
                child: const FlutterLogo(),  
              ),  
            ),  
          ],  
        ),  
      ),  
    );  
  }  
} 

Đầu ra

Column: Nó cho phép sắp xếp các widget con của nó theo hướng dọc.

ListView: Đây là widget cuộn phổ biến nhất cho phép chúng ta sắp xếp các widget con của nó lần lượt theo hướng cuộn.

GridView: Nó cho phép chúng ta sắp xếp các widget con của nó dưới dạng một mảng widget 2D, có thể cuộn được. Nó bao gồm một mô hình lặp lại của các ô được sắp xếp theo bố cục ngang và dọc.

Expanded: Nó cho phép tạo các con của widget Hàng và Cột chiếm diện tích tối đa có thể.

Table: Nó là một widget cho phép chúng ta sắp xếp các con của nó trong một widget dựa trên bảng.

Flow: Nó cho phép chúng ta triển khai widget dựa trên luồng.

Stack: Đây là một widget thiết yếu, chủ yếu được sử dụng để chồng lên một số widget. Nó cho phép bạn đặt nhiều lớp lên màn hình. Ví dụ sau đây giúp bạn hiểu điều đó. 

import 'package:flutter/material.dart';  
  
void main() => runApp(MyApp());  
  
class MyApp extends StatelessWidget {  
  // It is the root widget of your application.  
  @override  
  Widget build(BuildContext context) {  
    return MaterialApp(  
      title: 'Multiple Layout Widget',  
      debugShowCheckedModeBanner: false,  
      theme: ThemeData(  
        // This is the theme of your application.  
        primarySwatch: Colors.blue,  
      ),  
      home: MyHomePage(),  
    );  
  }  
}  
class MyHomePage extends StatelessWidget {  
  @override  
  Widget build(BuildContext context) {  
    return Center(  
      child: Container(  
        alignment: Alignment.center,  
        color: Colors.white,  
        child: Stack(  
          children: <Widget>[  
            // Max Size  
            Container(  
              color: Colors.blue,  
            ),  
            Container(  
              color: Colors.pink,  
              height: 400.0,  
              width: 300.0,  
            ),  
            Container(  
              color: Colors.yellow,  
              height: 220.0,  
              width: 200.0,  
            )  
          ],  
        ),  
      ),  
    );  
  }  
} 

Đầu ra

5. Xây dựng bố cục phức tạp

Trong phần này, chúng ta sẽ tìm hiểu cách bạn có thể tạo giao diện người dùng phức tạp bằng cách sử dụng cả widget bố cục đơn và nhiều widget. Khung bố cục cho phép bạn tạo bố cục giao diện người dùng phức tạp bằng cách lồng các hàng và cột vào bên trong các hàng và cột.

Hãy để cafedev tạo một ví dụ về giao diện người dùng phức tạp bằng cách tạo danh sách sản phẩm . Với mục đích này, trước tiên bạn cần thay thế code của tệp main.dart bằng đoạn mã sau.

import 'package:flutter/material.dart';  
  
void main() => runApp(MyApp());  
  
class MyApp extends StatelessWidget {  
  // It is the root widget of your application.  
  @override  
  Widget build(BuildContext context) {  
    return MaterialApp(  
      title: 'Flutter Demo Application', theme: ThemeData(  
      primarySwatch: Colors.green,),  
      home: MyHomePage(title: 'Complex layout example'),  
    );  
  }  
}  
class MyHomePage extends StatelessWidget {  
  MyHomePage({Key key, this.title}) : super(key: key);  
  final String title;  
  
  @override  
  Widget build(BuildContext context) {  
    return Scaffold(  
        appBar: AppBar(title: Text("Product List")),  
        body: ListView(  
          padding: const EdgeInsets.fromLTRB(3.0, 12.0, 3.0, 12.0),  
          children: <Widget>[  
            ProductBox(  
                name: "iPhone",  
                description: "iPhone is the top branded phone ever",  
                price: 55000,  
                image: "iphone.png"  
            ),  
            ProductBox(  
                name: "Android",  
                description: "Android is a very stylish phone",  
                price: 10000,  
                image: "android.png"  
            ),  
            ProductBox(  
                name: "Tablet",  
                description: "Tablet is a popular device for official meetings",  
                price: 25000,  
                image: "tablet.png"  
            ),  
            ProductBox(  
                name: "Laptop",  
                description: "Laptop is most famous electronic device",  
                price: 35000,  
                image: "laptop.png"  
            ),  
            ProductBox(  
                name: "Desktop",  
                description: "Desktop is most popular for regular use",  
                price: 10000,  
                image: "computer.png"  
            ),  
          ],  
        )  
    );  
  }  
}  
class ProductBox extends StatelessWidget {  
  ProductBox({Key key, this.name, this.description, this.price, this.image}) :  
        super(key: key);  
  final String name;  
  final String description;  
  final int price;  
  final String image;  
  
  Widget build(BuildContext context) {  
    return Container(  
        padding: EdgeInsets.all(2),  
        height: 110,  
        child: Card(  
            child: Row(  
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,  
                children: <Widget>[  
                  Image.asset("assets/" + image),  
                  Expanded(  
                      child: Container(  
                          padding: EdgeInsets.all(5),  
                          child: Column(  
                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,  
                            children: <Widget>[  
                              Text(  
                                  this.name, style: TextStyle(  
                                  fontWeight: FontWeight.bold  
                              )  
                              ),  
                              Text(this.description), Text(  
                                  "Price: " + this.price.toString()  
                              ),  
                            ],  
                          )  
                      )  
                  )  
                ]  
            )  
        )  
    );  
  }  
}  

Trong đoạn code trên, chúng ta tạo ProductBox widget chứa các chi tiết của sản phẩm, chẳng hạn như hình ảnh, tên, giá và mô tả. Trong widget ProductBox, chúng tôi sử dụng các widget sau: Vùng chứa, Hàng, Cột, Mở rộng, Thẻ, Văn bản, Hình ảnh, v.v. widget này có bố cục sau:

Đầu ra

Bây giờ, khi chúng ta chạy file dart trong trình giả lập Android, nó sẽ cho kết quả như sau.

Cài ứng dụng cafedev để dễ dàng cập nhật tin và học lập trình mọi lúc mọi nơi tại đây.

Tài liệu từ cafedev:

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!