Bài 7: Layout trong Flutter - Học lập trình Flutter cơ bản

Đăng bởi: Admin | Lượt xem: 20486 | Chuyên mục: Android


Trong Flutter các layout cũng là một loại widget, nhiệm vụ của chúng là bố trí các widget con, tạo nên giao diện người dùng cho ứng dụng. Flutter cung cấp nhiều loại layout khác nhau như Container, Center, Align... Chúng ta sẽ tìm hiểu chi tiết các loại layout trong bài học này.
Có hai loại widget layout chính trong Flutter
  • Single Child Widgets - Chỉ có một widget con
  • Multiple Child Widgets - Có nhiều widget con

Single Child Widgets

Các widget layout loại này chỉ có duy nhất một widget con và thường có chức năng bố trí nhất định. 
Ví dụ,  Center widget chỉ căn giữa widget con so với widget cha của nó và Container widget cung cấp khả năng linh hoạt trong việc đặt widget con bên trong nó thông qua các tuỳ chọn như padding, đường viền, nền,,,, 
Single child widgets thích hợp cho việc tạo ra các widget có tính ứng dụng cao và chỉ có một chức năng duy nhất như button, label.... 
Chúng ta sẽ xem thử đoạn code tạo ra một custom button sử dụng Container widget như sau:
class MyButton extends StatelessWidget {
   MyButton({Key key}) : super(key: key); 

   @override 
   Widget build(BuildContext context) {
      return Container(
         decoration: const BoxDecoration(
            border: Border(
               top: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
               left: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
               right: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
               bottom: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
            ),
         ),
         child: Container(
            padding: const
            EdgeInsets.symmetric(horizontal: 20.0, vertical: 2.0),
            decoration: const BoxDecoration(
               border: Border(
                  top: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)),
                  left: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)),
                  right: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)),
                  bottom: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)),
               ),
               color: Colors.grey,
            ),
            child: const Text(
               'OK',textAlign: TextAlign.center, style: TextStyle(color: Colors.black)
            ), 
         ), 
      ); 
   }
}
Đoạn code trên sử dụng 2 widget một Container widget và một Text widget. Kết quả như sau:
Một số single child layout widgets quan trọng trong Flutter 
  • Padding − Được sử dụng để padding child widget. Ở đây, padding có thể sử dụng EdgeInsets class.
  • Align − Căn lề child widget sử dụng thuộc tính alignment. Giá trị của  alignment  có thể được cung cấp bởi  FractionalOffset class.  FractionalOffset class xác định vị trí của phần tử từ vị trí điểm trên cùng bên trái
Một số ví dụ về align
  • FractionalOffset(1.0, 0.0) biểu thị phiá trên bên phải
  • FractionalOffset(0.0, 1.0) biểu thị phía dưới bên trái
Ví dụ qua đoạn code sau:
Center(
   child: Container(
      height: 100.0, 
      width: 100.0, 
      color: Colors.yellow, child: Align(
         alignment: FractionalOffset(0.2, 0.6),
         child: Container( height: 40.0, width:
            40.0, color: Colors.red,
         ), 
      ), 
   ), 
)
Một số single child layout khác:
  • FittedBox
  • AspectRatio
  • ConstrainedBox
  • Baseline
  • FractinallySizedBox
  • IntrinsicHeight
  • IntrinsicWidth
  • LiimitedBox
  • OffStage
  • OverflowBox
  • SizedBox
  • SizedOverflowBox
  • Transform
  • CustomSingleChildLayout
Mình sẽ sửa lại một chút code MyHomePage trong ứng dụng Hello Word ở bài trước để các bạn hiểu rõ hơn 
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hello World Demo Application',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Home page'),
    );
  }
}
class MyHomePage extends StatelessWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  Widget build(BuildContext context) {
    return Container(
        decoration: BoxDecoration(color: Colors.orange,),
        padding: EdgeInsets.all(25), child: Center(
      child:Text(
        'Hello World', style: TextStyle(
        color: Colors.red, letterSpacing: 0.5, fontSize: 30,
      ),
        textDirection: TextDirection.ltr,
      ),
    )
    );
  }
}
Chạy thử trên máy ảo

Multiple Child Widgets

Loại widget layout này sẽ cho phép có nhiều hơn một widget con. Ví dụ Row widget cho phép bố trí các widget con theo chiều ngang thành một hàng trong khi Column widget cho phép bố trí các widget con theo chiều dọc thành một cột.
Một số widget layout dạng này được sử dụng phổ biến
  • Row 
  • Column 
  • ListView 
  • GridView 
  • Expanded 
  • Table 
  • Flow 
  • Stack 

Ví dụ

Để hiểu hơn về layout trong Flutter mình sẽ hướng dẫn các bạn thực hiện một giao diện người dùng phức hợp gọi là product listing với thiết kế tuỳ chỉnh sử dụng cả  single và multiple child layout widget
Các bạn tạo project Flutter mới đặt tên là product_layout_app.
Thay thế  main.dart bằng đoạn code sau đây:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo', theme: ThemeData(
      primarySwatch: Colors.blue,),
      home: MyHomePage(title: 'Product layout demo home page'),
    );
  }
}
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(this.title),),
      body: Center(child: Text( 'Hello World', )),
    );
  }
}
Chúng ta tạo một widget là  MyHomePage kế thừa từ StatelessWidget 
Tiếp theo chúng ta sẽ tạo một widget ProductBox để hiển thị thông tin sản phẩm, bao gồm hình ảnh, tên sản phẩm, mô tả, và giá bán như thiết kế dưới đây:
Code của ProductBox như sau:
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: 120,  child: Card( 
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
                  Image.asset("assets/appimages/" +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()), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      );
   }
}
Giải thích:
ProductBox có 4 thuộc tính như sau:
  • name - Tên sản phẩm
  • description - Mô tả sản phẩm
  • price - Giá của sản phẩm
  • image - Hình anhr cửa sản phẩm
ProductBox sử dụng 7 widget như sau:
  • Container
  • Expanded
  • Row
  • Column
  • Card
  • Text
  • Image
Để hiểu hơn cấu trúc của ProductBox widget ta xem sơ đồ cấu trúc sau:
Bây giờ, chúng ta thêm ảnh của sản phẩm vào thư mục assets của ứng dụng, tạo thư mục con appimages để chứa ảnh  và cấu hình assets trong file pubspec.yaml như sau:
assets: 
   - assets/appimages/floppydisk.jpg 
   - assets/appimages/iphone.jpg 
   - assets/appimages/laptop.jpg 
   - assets/appimages/pendrive.jpg 
   - assets/appimages/pixel.jpg 
   - assets/appimages/tablet.jpg
Các bạn nhấn chuột phải vào ảnh rồi download về máy nhé.
Bây giờ để hiển thị nhiều sản phẩm ta sử dụng ListView widget để chứa các ProductBox, sửa lại code MyHomePage như sau:
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 Listing")),
        body: ListView(
          shrinkWrap: true, padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0),
          children: <Widget> [
            ProductBox(
                name: "iPhone",
                description: "iPhone is the stylist phone ever",
                price: 1000,
                image: "iphone.jpg"
            ),
            ProductBox(
                name: "Pixel",
                description: "Pixel is the most featureful phone ever",
                price: 800,
                image: "pixel.jpg"
            ),
            ProductBox(
                name: "Laptop",
                description: "Laptop is most productive development tool",
                price: 2000,
                image: "laptop.jpg"
            ),
            ProductBox(
                name: "Tablet",
                description: "Tablet is the most useful device ever for meeting",
                price: 1500,
                image: "tablet.jpg"
            ),
            ProductBox(
                name: "Pendrive",
                description: "Pendrive is useful storage medium",
                price: 100,
                image: "pendrive.jpg"
            ),
            ProductBox(
                name: "Floppy Drive",
                description: "Floppy drive is useful rescue storage medium",
                price: 20,
                image: "floppydisk.jpg"
            ),
          ],
        )
    );
  }
}
Full code  (main.dart)  của ứng dụng (product_layout_app) như sau:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo', theme: ThemeData(
      primarySwatch: Colors.blue,),
      home: MyHomePage(title: 'Product layout demo home page'),
    );
  }
}

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 Listing")),
        body: ListView(
          shrinkWrap: true, padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0),
          children: <Widget> [
            ProductBox(
                name: "iPhone",
                description: "iPhone is the stylist phone ever",
                price: 1000,
                image: "iphone.jpg"
            ),
            ProductBox(
                name: "Pixel",
                description: "Pixel is the most featureful phone ever",
                price: 800,
                image: "pixel.jpg"
            ),
            ProductBox(
                name: "Laptop",
                description: "Laptop is most productive development tool",
                price: 2000,
                image: "laptop.jpg"
            ),
            ProductBox(
                name: "Tablet",
                description: "Tablet is the most useful device ever for meeting",
                price: 1500,
                image: "tablet.jpg"
            ),
            ProductBox(
                name: "Pendrive",
                description: "Pendrive is useful storage medium",
                price: 100,
                image: "pendrive.jpg"
            ),
            ProductBox(
                name: "Floppy Drive",
                description: "Floppy drive is useful rescue storage medium",
                price: 20,
                image: "floppydisk.jpg"
            ),
          ],
        )
    );
  }
}
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: 120,  child: Card(
        child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
          Image.asset("assets/appimages/" +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()),
                ],
              )
              )
          )
        ]
        )
    )
    );
  }
}
Chạy thử ứng dụng trên máy ảo:
Bài tiếp theo: Gesture trong Flutter >>
vncoder logo

Theo dõi VnCoder trên Facebook, để cập nhật những bài viết, tin tức và khoá học mới nhất!